notionjournal / script.js
madan2248c's picture
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);
}
});