From 5bc9c9bdb18dfa928ae34b9b571804ec721f98b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20R=C5=AF=C5=BEi=C4=8Dka?= <97810333+thejakubruzicka@users.noreply.github.com> Date: Tue, 20 May 2025 20:07:34 +0200 Subject: [PATCH] Update script.js --- script.js | 175 +++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 120 insertions(+), 55 deletions(-) diff --git a/script.js b/script.js index 836f82e..921cd7b 100644 --- a/script.js +++ b/script.js @@ -7,15 +7,52 @@ document.addEventListener('DOMContentLoaded', () => { const noteTitleInput = document.getElementById('noteTitleInput'); const noteContentInput = document.getElementById('noteContentInput'); + const themeToggleBtn = document.getElementById('themeToggleBtn'); + const body = document.body; + let notes = []; let currentEditingId = null; + // --- Theming --- + function applyTheme(theme) { // theme is 'light' or 'dark' + body.classList.remove('light-theme', 'dark-theme'); + body.classList.add(theme + '-theme'); // e.g., 'dark-theme' + localStorage.setItem('mnotes-theme', theme); + + const icon = themeToggleBtn.querySelector('md-icon'); + if (theme === 'dark') { + icon.textContent = 'light_mode'; + themeToggleBtn.ariaLabel = "Activate light theme"; + } else { + icon.textContent = 'dark_mode'; + themeToggleBtn.ariaLabel = "Activate dark theme"; + } + } + + function toggleTheme() { + const currentTheme = localStorage.getItem('mnotes-theme') || (body.classList.contains('dark-theme') ? 'dark' : 'light'); + applyTheme(currentTheme === 'light' ? 'dark' : 'light'); + } + + function loadInitialTheme() { + const savedTheme = localStorage.getItem('mnotes-theme'); + if (savedTheme) { + applyTheme(savedTheme); + } else { + // Prefer system theme if no preference saved + if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) { + applyTheme('dark'); + } else { + applyTheme('light'); // Default to light + } + } + } + themeToggleBtn.addEventListener('click', toggleTheme); + // --- Data Persistence --- function loadNotes() { const storedNotes = localStorage.getItem('material-notes'); - if (storedNotes) { - notes = JSON.parse(storedNotes); - } + notes = storedNotes ? JSON.parse(storedNotes) : []; renderNotes(); } @@ -27,36 +64,39 @@ document.addEventListener('DOMContentLoaded', () => { function renderNotes() { notesList.innerHTML = ''; // Clear existing notes if (notes.length === 0) { - notesList.innerHTML = `

No notes yet. Click the + button to add one!

`; + const p = document.createElement('p'); + p.textContent = "No notes yet. Click the + button to add one!"; + notesList.appendChild(p); return; } + notes.sort((a, b) => new Date(b.lastModified) - new Date(a.lastModified)); // Sort by most recently modified + notes.forEach(note => { const noteCard = document.createElement('div'); noteCard.className = 'note-card'; - // Add md-elevation for a card-like appearance - const elevation = document.createElement('md-elevation'); + + const elevation = document.createElement('md-elevation'); // Add MWC elevation noteCard.appendChild(elevation); - const title = document.createElement('h3'); title.textContent = note.title; const content = document.createElement('p'); - content.textContent = note.content.substring(0, 150) + (note.content.length > 150 ? '...' : ''); // Preview + content.textContent = note.content.substring(0, 150) + (note.content.length > 150 ? '...' : ''); const actions = document.createElement('div'); actions.className = 'note-card-actions'; const editButton = document.createElement('md-icon-button'); editButton.innerHTML = `edit`; - editButton.ariaLabel = 'Edit Note'; - editButton.addEventListener('click', () => openNoteDialog(note)); + editButton.ariaLabel = `Edit note: ${note.title}`; + editButton.addEventListener('click', (e) => { e.stopPropagation(); openNoteDialog(note); }); const deleteButton = document.createElement('md-icon-button'); deleteButton.innerHTML = `delete`; - deleteButton.ariaLabel = 'Delete Note'; - deleteButton.addEventListener('click', () => deleteNote(note.id)); + deleteButton.ariaLabel = `Delete note: ${note.title}`; + deleteButton.addEventListener('click', (e) => { e.stopPropagation(); deleteNote(note.id); }); actions.appendChild(editButton); actions.appendChild(deleteButton); @@ -70,54 +110,89 @@ document.addEventListener('DOMContentLoaded', () => { // --- Note Operations --- function openNoteDialog(note = null) { - noteForm.reset(); // Clear form + noteForm.reset(); // Clear form fields + // Reset MWC text field states (e.g. error messages) + noteTitleInput.errorText = ""; + noteTitleInput.error = false; + noteContentInput.errorText = ""; + noteContentInput.error = false; + if (note) { currentEditingId = note.id; - noteIdInput.value = note.id; noteTitleInput.value = note.title; - noteContentInput.value = note.content; + noteContentInput.value = note.content; // Full content for editing noteDialog.querySelector('[slot="headline"]').textContent = 'Edit Note'; } else { currentEditingId = null; - noteIdInput.value = ''; noteDialog.querySelector('[slot="headline"]').textContent = 'Add New Note'; } noteDialog.show(); } - function handleFormSubmit() { - const title = noteTitleInput.value.trim(); - const content = noteContentInput.value.trim(); + function handleDialogClose(event) { + // event.detail.action contains the `value` of the button that closed it + if (event.detail.action === 'save') { + const title = noteTitleInput.value.trim(); + const content = noteContentInput.value.trim(); - if (!title || !content) { - // Basic validation, could be more robust - alert('Title and content cannot be empty.'); - return; - } - - if (currentEditingId) { - // Editing existing note - const noteIndex = notes.findIndex(n => n.id === currentEditingId); - if (noteIndex > -1) { - notes[noteIndex].title = title; - notes[noteIndex].content = content; + let isValid = true; + if (!title) { + noteTitleInput.error = true; + noteTitleInput.errorText = "Title cannot be empty."; + isValid = false; + } else { + noteTitleInput.error = false; + noteTitleInput.errorText = ""; + } + if (!content) { + noteContentInput.error = true; + noteContentInput.errorText = "Content cannot be empty."; + isValid = false; + } else { + noteContentInput.error = false; + noteContentInput.errorText = ""; } - } else { - // Creating new note - const newNote = { - id: Date.now().toString(), // Simple unique ID - title, - content - }; - notes.push(newNote); - } - saveNotes(); - renderNotes(); - // noteDialog.close(); // Handled by form method="dialog" and button value + if (!isValid) { + // To prevent dialog from closing on invalid native form submission, + // we'd need to handle the 'submit' event on the form, preventDefault, + // then manually close if valid. + // Since `method="dialog"` auto-closes, we re-show if invalid. + // This is a common pattern if not using full form.requestSubmit(). + noteDialog.show(); // Re-open dialog if validation fails after it auto-closed. + return; + } + + const now = new Date().toISOString(); + if (currentEditingId) { + const noteIndex = notes.findIndex(n => n.id === currentEditingId); + if (noteIndex > -1) { + notes[noteIndex].title = title; + notes[noteIndex].content = content; + notes[noteIndex].lastModified = now; + } + } else { + notes.push({ + id: Date.now().toString(), + title, + content, + createdAt: now, + lastModified: now + }); + } + saveNotes(); + renderNotes(); + } + // Reset for next time dialog opens + currentEditingId = null; + noteForm.reset(); // Ensure form is clean + noteTitleInput.error = false; noteTitleInput.errorText = ""; + noteContentInput.error = false; noteContentInput.errorText = ""; } function deleteNote(id) { + // Consider using an md-dialog for confirmation for better UX + // For simplicity, using confirm() for now if (confirm('Are you sure you want to delete this note?')) { notes = notes.filter(note => note.id !== id); saveNotes(); @@ -127,19 +202,9 @@ document.addEventListener('DOMContentLoaded', () => { // --- Event Listeners --- addNoteFab.addEventListener('click', () => openNoteDialog()); - - // The dialog component handles its own close on button clicks if `form.method="dialog"` - // and buttons have `value="cancel"` or `form="formId"`. - // We listen for the 'closed' event to react to the "save" action. - noteDialog.addEventListener('closed', (event) => { - if (event.detail.action === 'save') { - handleFormSubmit(); - } - // Reset for next time - currentEditingId = null; - noteForm.reset(); - }); + noteDialog.addEventListener('closed', handleDialogClose); // --- Initial Load --- + loadInitialTheme(); loadNotes(); });