Spaces:
Running
Running
which i press enter it should also directly give an option to start typing in the next block currently again i am needing to click on the new block to typee
24bdc07
verified
| document.addEventListener('DOMContentLoaded', function() { | |
| // Initialize variables | |
| let entries = JSON.parse(localStorage.getItem('journalEntries')) || []; | |
| const journalContainer = document.getElementById('journal-container'); | |
| const addEntryBtn = document.getElementById('add-entry-btn'); | |
| // Render existing entries | |
| renderEntries(); | |
| // Add new entry | |
| addEntryBtn.addEventListener('click', () => { | |
| const newEntry = { | |
| id: Date.now(), | |
| date: new Date().toISOString().split('T')[0], | |
| title: '', | |
| blocks: [ | |
| { id: Date.now() + 1, type: 'paragraph', content: '' } | |
| ] | |
| }; | |
| entries.unshift(newEntry); | |
| saveEntries(); | |
| renderEntries(); | |
| }); | |
| // Render all entries | |
| function renderEntries() { | |
| journalContainer.innerHTML = ''; | |
| if (entries.length === 0) { | |
| journalContainer.innerHTML = ` | |
| <div class="text-center py-12"> | |
| <i data-feather="book-open" class="w-16 h-16 mx-auto text-gray-300 mb-4"></i> | |
| <h3 class="text-xl font-medium text-gray-500 mb-2">No journal entries yet</h3> | |
| <p class="text-gray-400">Create your first entry to get started</p> | |
| </div> | |
| `; | |
| feather.replace(); | |
| return; | |
| } | |
| entries.forEach(entry => { | |
| const entryElement = createEntryElement(entry); | |
| journalContainer.appendChild(entryElement); | |
| }); | |
| } | |
| // Create entry element | |
| function createEntryElement(entry) { | |
| const entryDiv = document.createElement('div'); | |
| entryDiv.className = 'entry-card bg-white rounded-xl shadow-sm border border-gray-200 overflow-hidden'; | |
| entryDiv.innerHTML = ` | |
| <div class="p-6"> | |
| <div class="flex justify-between items-start mb-4"> | |
| <div> | |
| <h2 class="text-xl font-semibold text-gray-800 mb-1">${entry.title || 'Untitled Entry'}</h2> | |
| <div class="date-badge text-white text-sm px-3 py-1 rounded-full inline-block"> | |
| ${formatDate(entry.date)} | |
| </div> | |
| </div> | |
| <button class="delete-entry text-gray-400 hover:text-red-500" data-id="${entry.id}"> | |
| <i data-feather="trash-2" class="w-5 h-5"></i> | |
| </button> | |
| </div> | |
| <div class="editor-container space-y-3" data-id="${entry.id}"> | |
| ${entry.blocks.map(block => createBlockElement(block)).join('')} | |
| </div> | |
| <div class="mt-4 flex space-x-2"> | |
| <button class="add-block text-gray-500 hover:text-gray-700 flex items-center text-sm" data-id="${entry.id}"> | |
| <i data-feather="plus" class="w-4 h-4 mr-1"></i> Add block | |
| </button> | |
| </div> | |
| </div> | |
| `; | |
| // Add event listeners | |
| const deleteBtn = entryDiv.querySelector('.delete-entry'); | |
| deleteBtn.addEventListener('click', () => { | |
| entries = entries.filter(e => e.id !== entry.id); | |
| saveEntries(); | |
| renderEntries(); | |
| }); | |
| const addBlockBtn = entryDiv.querySelector('.add-block'); | |
| addBlockBtn.addEventListener('click', () => { | |
| const entryId = parseInt(addBlockBtn.getAttribute('data-id')); | |
| const entry = entries.find(e => e.id === entryId); | |
| const newBlock = { id: Date.now(), type: 'paragraph', content: '' }; | |
| entry.blocks.push(newBlock); | |
| saveEntries(); | |
| renderEntries(); | |
| }); | |
| // Add block event listeners | |
| const editorContainer = entryDiv.querySelector('.editor-container'); | |
| editorContainer.querySelectorAll('[contenteditable]').forEach(element => { | |
| element.addEventListener('input', handleContentChange); | |
| element.addEventListener('keydown', handleKeyDown); | |
| element.addEventListener('blur', handleBlur); | |
| }); | |
| // Title input listener | |
| const titleInput = entryDiv.querySelector('.entry-title'); | |
| if (titleInput) { | |
| titleInput.addEventListener('input', handleTitleChange); | |
| } | |
| feather.replace(); | |
| return entryDiv; | |
| } | |
| // Create block element | |
| function createBlockElement(block) { | |
| switch (block.type) { | |
| case 'heading': | |
| return ` | |
| <div class="editor-block relative group pl-8 pr-4 py-2 rounded-lg hover:bg-gray-50" data-block-id="${block.id}"> | |
| <div class="absolute left-2 top-3 drag-handle text-gray-400 cursor-move group-hover:opacity-100"> | |
| <i data-feather="grip-vertical" class="w-4 h-4"></i> | |
| </div> | |
| <h3 | |
| class="text-2xl font-bold outline-none" | |
| contenteditable="true" | |
| data-type="heading" | |
| data-block-id="${block.id}" | |
| >${block.content}</h3> | |
| <button class="delete-button absolute right-2 top-3 text-gray-400 hover:text-red-500"> | |
| <i data-feather="x" class="w-4 h-4"></i> | |
| </button> | |
| </div> | |
| `; | |
| case 'paragraph': | |
| return ` | |
| <div class="editor-block relative group pl-8 pr-4 py-2 rounded-lg hover:bg-gray-50" data-block-id="${block.id}"> | |
| <div class="absolute left-2 top-3 drag-handle text-gray-400 cursor-move group-hover:opacity-100"> | |
| <i data-feather="grip-vertical" class="w-4 h-4"></i> | |
| </div> | |
| <p | |
| class="outline-none text-gray-700 leading-relaxed" | |
| contenteditable="true" | |
| data-type="paragraph" | |
| data-block-id="${block.id}" | |
| data-placeholder="Type something..." | |
| >${block.content}</p> | |
| <button class="delete-button absolute right-2 top-3 text-gray-400 hover:text-red-500"> | |
| <i data-feather="x" class="w-4 h-4"></i> | |
| </button> | |
| </div> | |
| `; | |
| case 'divider': | |
| return ` | |
| <div class="editor-block relative group py-4" data-block-id="${block.id}"> | |
| <div class="absolute left-2 top-5 drag-handle text-gray-400 cursor-move group-hover:opacity-100"> | |
| <i data-feather="grip-vertical" class="w-4 h-4"></i> | |
| </div> | |
| <hr class="border-gray-200"> | |
| <button class="delete-button absolute right-2 top-3 text-gray-400 hover:text-red-500"> | |
| <i data-feather="x" class="w-4 h-4"></i> | |
| </button> | |
| </div> | |
| `; | |
| default: | |
| return ''; | |
| } | |
| } | |
| // Handle content change | |
| function handleContentChange(e) { | |
| const blockId = parseInt(e.target.getAttribute('data-block-id')); | |
| const entryId = parseInt(e.target.closest('.editor-container').getAttribute('data-id')); | |
| const content = e.target.textContent; | |
| const entry = entries.find(e => e.id === entryId); | |
| if (entry) { | |
| const block = entry.blocks.find(b => b.id === blockId); | |
| if (block) { | |
| block.content = content; | |
| saveEntries(); | |
| } | |
| } | |
| } | |
| // Handle title change | |
| function handleTitleChange(e) { | |
| const entryId = parseInt(e.target.closest('.entry-card').querySelector('.editor-container').getAttribute('data-id')); | |
| const title = e.target.textContent; | |
| const entry = entries.find(e => e.id === entryId); | |
| if (entry) { | |
| entry.title = title; | |
| saveEntries(); | |
| } | |
| } | |
| // Handle keydown events | |
| function handleKeyDown(e) { | |
| if (e.key === 'Enter') { | |
| e.preventDefault(); | |
| insertBlockBelow(e.target); | |
| } else if (e.key === 'Backspace' && e.target.textContent === '') { | |
| e.preventDefault(); | |
| deleteBlock(e.target); | |
| } | |
| } | |
| // Handle blur event | |
| function handleBlur(e) { | |
| if (e.target.textContent === '') { | |
| const placeholder = e.target.getAttribute('data-placeholder'); | |
| if (placeholder) { | |
| e.target.textContent = ''; | |
| } | |
| } | |
| } | |
| // Insert block below current block | |
| function insertBlockBelow(target) { | |
| const blockId = parseInt(target.getAttribute('data-block-id')); | |
| const entryId = parseInt(target.closest('.editor-container').getAttribute('data-id')); | |
| const entry = entries.find(e => e.id === entryId); | |
| if (entry) { | |
| const blockIndex = entry.blocks.findIndex(b => b.id === blockId); | |
| const newBlock = { id: Date.now(), type: 'paragraph', content: '' }; | |
| entry.blocks.splice(blockIndex + 1, 0, newBlock); | |
| saveEntries(); | |
| renderEntries(); | |
| // Focus the new block after rendering | |
| setTimeout(() => { | |
| const newBlockElement = document.querySelector(`[data-block-id="${newBlock.id}"]`); | |
| if (newBlockElement) { | |
| const editableElement = newBlockElement.querySelector('[contenteditable]'); | |
| if (editableElement) { | |
| editableElement.focus(); | |
| // Place cursor at the beginning | |
| const range = document.createRange(); | |
| const sel = window.getSelection(); | |
| range.selectNodeContents(editableElement); | |
| range.collapse(true); | |
| sel.removeAllRanges(); | |
| sel.addRange(range); | |
| } | |
| } | |
| }, 0); | |
| } | |
| } | |
| // Delete block | |
| function deleteBlock(target) { | |
| const blockId = parseInt(target.getAttribute('data-block-id')); | |
| const entryId = parseInt(target.closest('.editor-container').getAttribute('data-id')); | |
| const entry = entries.find(e => e.id === entryId); | |
| if (entry && entry.blocks.length > 1) { | |
| const blockIndex = entry.blocks.findIndex(b => b.id === blockId); | |
| entry.blocks = entry.blocks.filter(b => b.id !== blockId); | |
| saveEntries(); | |
| renderEntries(); | |
| // Focus the previous block after rendering | |
| setTimeout(() => { | |
| if (blockIndex > 0) { | |
| const prevBlock = entry.blocks[blockIndex - 1]; | |
| const prevBlockElement = document.querySelector(`[data-block-id="${prevBlock.id}"]`); | |
| if (prevBlockElement) { | |
| const range = document.createRange(); | |
| const sel = window.getSelection(); | |
| range.selectNodeContents(prevBlockElement); | |
| range.collapse(false); | |
| sel.removeAllRanges(); | |
| sel.addRange(range); | |
| } | |
| } | |
| }, 0); | |
| } | |
| } | |
| // Save entries to localStorage | |
| function saveEntries() { | |
| localStorage.setItem('journalEntries', JSON.stringify(entries)); | |
| } | |
| // Format date | |
| function formatDate(dateString) { | |
| const options = { year: 'numeric', month: 'short', day: 'numeric' }; | |
| return new Date(dateString).toLocaleDateString(undefined, options); | |
| } | |
| }); |