From f4ca071c9ea057a2253ae3741566673a0dcd01b8 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:13:24 +0200 Subject: [PATCH 01/12] Update index.html --- index.html | 50 ++++++++++++++------------------------------------ 1 file changed, 14 insertions(+), 36 deletions(-) diff --git a/index.html b/index.html index 4078575..b674e3f 100644 --- a/index.html +++ b/index.html @@ -8,36 +8,31 @@ - + - + @@ -46,20 +41,19 @@

MNotes

- dark_mode + dark_mode
- +
- add + add -
Add/Edit Note
@@ -73,22 +67,6 @@ - - + From 9a4a97984b60b92e5f304ae7eabb1c062822b9cf 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:13:48 +0200 Subject: [PATCH 02/12] Update style.css --- style.css | 134 ++++++++++++++++-------------------------------------- 1 file changed, 39 insertions(+), 95 deletions(-) diff --git a/style.css b/style.css index 6bbf4c5..0f7d163 100644 --- a/style.css +++ b/style.css @@ -1,28 +1,28 @@ -/* Global variables for theming */ +/* Global variables for theming (from previous version, ensure they are complete) */ :root { /* Light Theme (Default) */ - --app-bg-color: #FEF7FF; /* Material 3 Surface */ - --app-text-color: #1D1B20; /* Material 3 On Surface */ - --app-surface-color: #FDFCFB; /* Material 3 Surface Container Low */ - --app-primary-color: #6750A4; /* Material 3 Primary */ + --app-bg-color: #FEF7FF; + --app-text-color: #1D1B20; + --app-surface-color: #FDFCFB; + --app-primary-color: #6750A4; --app-on-primary-color: #FFFFFF; - --app-secondary-color: #625B71; /* Material 3 Secondary */ - --app-outline-color: #79747E; /* Material 3 Outline */ - --app-note-card-bg: #EADDFF; /* Material 3 Primary Container */ - --app-note-card-text: #21005D; /* Material 3 On Primary Container */ - --app-note-card-header: var(--app-primary-color); /* Or #1D192B for On Surface */ + --app-secondary-color: #625B71; + --app-outline-color: #79747E; + --app-note-card-bg: #EADDFF; /* Primary Container */ + --app-note-card-text: #21005D; /* On Primary Container */ + --app-note-card-header: var(--app-primary-color); /* MWC Theme Token Mapping */ --md-sys-color-background: var(--app-bg-color); --md-sys-color-on-background: var(--app-text-color); --md-sys-color-surface: var(--app-surface-color); - --md-sys-color-surface-dim: #DED8E1; /* Example, add more as needed */ + --md-sys-color-surface-dim: #DED8E1; --md-sys-color-surface-bright: #FEF7FF; --md-sys-color-surface-container-lowest: #FFFFFF; --md-sys-color-surface-container-low: #F7F2FA; --md-sys-color-surface-container: #F3EDF7; --md-sys-color-surface-container-high: #ECE6F0; - --md-sys-color-surface-container-highest: #E6E0E9; /* Was used for note cards */ + --md-sys-color-surface-container-highest: #E6E0E9; --md-sys-color-surface-variant: #E7E0EC; --md-sys-color-on-surface: var(--app-text-color); --md-sys-color-on-surface-variant: #49454F; @@ -36,32 +36,31 @@ --md-sys-color-on-error: #FFFFFF; --md-sys-color-outline: var(--app-outline-color); --md-sys-color-shadow: #000000; - --md-sys-color-surface-tint-color: var(--app-primary-color); /* Alias for primary */ - + --md-sys-color-surface-tint-color: var(--app-primary-color); /* Expressive shape tokens */ --md-sys-shape-corner-extra-small: 4px; --md-sys-shape-corner-small: 8px; --md-sys-shape-corner-medium: 12px; - --md-sys-shape-corner-large: 16px; /* Used for cards */ + --md-sys-shape-corner-large: 16px; /* Used for cards, FABs */ --md-sys-shape-corner-extra-large: 28px; /* Used for dialogs */ - --md-sys-shape-corner-full: 9999px; /* For fully rounded elements like FAB */ + --md-sys-shape-corner-full: 9999px; /* For fully rounded "pill" shapes */ } body.dark-theme { /* Dark Theme Overrides */ --app-bg-color: #141218; --app-text-color: #E6E0E9; - --app-surface-color: #1C1B1F; /* Material 3 Surface Container Low (Dark) */ + --app-surface-color: #1C1B1F; --app-primary-color: #D0BCFF; --app-on-primary-color: #381E72; --app-secondary-color: #CCC2DC; --app-outline-color: #938F99; - --app-note-card-bg: #4A4458; /* Material 3 Primary Container (Dark) - example, adjust to preference */ - --app-note-card-text: #E8DEF8; /* Material 3 On Primary Container (Dark) */ + --app-note-card-bg: #36343B; /* Surface Container Highest (Dark) */ + --app-note-card-text: #CAC4D0; /* On Surface Variant (Dark) */ --app-note-card-header: var(--app-primary-color); - /* MWC Dark Theme Token Mapping (subset, repeat for all relevant tokens) */ + /* MWC Dark Theme Token Mapping (subset) */ --md-sys-color-surface-dim: #141218; --md-sys-color-surface-bright: #3B383E; --md-sys-color-surface-container-lowest: #0F0D13; @@ -75,106 +74,51 @@ body.dark-theme { --md-sys-color-surface-tint-color: var(--app-primary-color); } -/* Apply MWC tokens for body styles */ body { - font-family: 'Roboto', sans-serif; /* Already set in head style */ + font-family: 'Roboto', sans-serif; margin: 0; background-color: var(--md-sys-color-background); color: var(--md-sys-color-on-background); transition: background-color 0.3s, color 0.3s; } -.container { - max-width: 800px; - margin: 0 auto; - padding: 16px; -} +.container { max-width: 800px; margin: 0 auto; padding: 16px; } +header { display: flex; align-items: center; justify-content: space-between; padding-bottom: 16px; margin-bottom: 20px; } +header h1 { margin: 0; color: var(--md-sys-color-primary); font-size: 2em; } +#themeToggleBtn md-icon { color: var(--md-sys-color-secondary); } -header { - display: flex; - align-items: center; - justify-content: space-between; - padding-bottom: 16px; - margin-bottom: 20px; -} -header h1 { - margin: 0; - color: var(--md-sys-color-primary); - font-size: 2em; /* More prominent title */ -} -#themeToggleBtn md-icon { - color: var(--md-sys-color-secondary); /* Themed icon color */ -} - -/* Note Card Styling */ .note-card { margin-bottom: 16px; padding: 16px; - border-radius: var(--md-sys-shape-corner-large); /* Expressive rounding via token */ + border-radius: var(--md-sys-shape-corner-large); /* 16px */ background-color: var(--app-note-card-bg); color: var(--app-note-card-text); position: relative; - /* box-shadow: 0 1px 3px rgba(0,0,0,0.05), 0 2px 8px rgba(0,0,0,0.1); MWC Elevation handles this */ overflow: hidden; } -.note-card md-elevation { /* This applies the standard MWC shadow */ - --md-elevation-level: 1; -} +.note-card md-elevation { --md-elevation-level: 1; } +.note-card h3 { margin-top: 0; margin-bottom: 8px; color: var(--app-note-card-header); font-size: 1.25em; font-weight: 500; } +.note-card p { margin-bottom: 8px; white-space: pre-wrap; font-size: 0.95em; line-height: 1.5; } +.note-card-actions { position: absolute; top: 8px; right: 8px; display: flex; } +.note-card-actions md-icon-button { --md-icon-button-icon-color: var(--app-note-card-text); } /* MWC default can be used */ +.note-card-actions md-icon-button:hover { /* MWC handles hover states well */ } -.note-card h3 { - margin-top: 0; - margin-bottom: 8px; - color: var(--app-note-card-header); - font-size: 1.25em; - font-weight: 500; -} - -.note-card p { - margin-bottom: 8px; - white-space: pre-wrap; - font-size: 0.95em; - line-height: 1.5; -} - -.note-card-actions { - position: absolute; - top: 8px; - right: 8px; - display: flex; -} -.note-card-actions md-icon-button { - --md-icon-button-icon-color: var(--app-note-card-text); -} -.note-card-actions md-icon-button:hover { - --md-icon-button-icon-color: var(--app-primary-color); -} - -/* FAB Styling (MWC theming should mostly handle this) */ md-fab { position: fixed; bottom: 24px; right: 24px; - /* --md-fab-container-shape: var(--md-sys-shape-corner-large); /* Example of shape override if not default */ + /* MWC FABs use --md-fab-container-shape, which defaults to --md-sys-shape-corner-large (16px for a standard FAB) */ + /* If you want it perfectly circular and it's a regular (non-extended) FAB: */ + /* --md-fab-container-shape: var(--md-sys-shape-corner-full); */ + /* However, the default is usually quite rounded already. Primary variant is good. */ } -/* Dialog Styling */ md-dialog { - /* MWC sets its own width, can be overridden with --md-dialog-container-min-width etc. */ - /* border-radius: var(--md-sys-shape-corner-extra-large); /* MWC default or use --md-dialog-container-shape */ - --md-dialog-container-color: var(--md-sys-color-surface-container-high); /* Themed dialog bg */ + /* --md-dialog-container-shape default is --md-sys-shape-corner-extra-large (28px) */ + --md-dialog-container-color: var(--md-sys-color-surface-container-high); --md-dialog-headline-color: var(--md-sys-color-on-surface-variant); --md-dialog-supporting-text-color: var(--md-sys-color-on-surface-variant); } -md-dialog [slot="headline"] { - font-size: 1.5em; - font-weight: 500; - /* color: var(--md-sys-color-primary); /* Already set by --md-dialog-headline-color or inherited */ -} +md-dialog [slot="headline"] { font-size: 1.5em; font-weight: 500; } -/* Placeholder text for empty notes list */ -#notesList > p { - text-align: center; - color: var(--md-sys-color-outline); - padding: 20px 0; - font-size: 1.1em; -} +#notesList > p { text-align: center; color: var(--md-sys-color-outline); padding: 20px 0; font-size: 1.1em; } From df7424d0f122ce2517bc5fdaf64659d38d02c26f 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:14:43 +0200 Subject: [PATCH 03/12] Update script.js --- script.js | 440 +++++++++++++++++++++++++++++------------------------- 1 file changed, 239 insertions(+), 201 deletions(-) diff --git a/script.js b/script.js index 921cd7b..532cd5b 100644 --- a/script.js +++ b/script.js @@ -1,210 +1,248 @@ -document.addEventListener('DOMContentLoaded', () => { - const notesList = document.getElementById('notesList'); - const addNoteFab = document.getElementById('addNoteFab'); - const noteDialog = document.getElementById('noteDialog'); - const noteForm = document.getElementById('noteForm'); - const noteIdInput = document.getElementById('noteIdInput'); - const noteTitleInput = document.getElementById('noteTitleInput'); - const noteContentInput = document.getElementById('noteContentInput'); +// Define variables in a scope accessible by functions +let notesList, addNoteFab, noteDialog, noteForm, noteIdInput, noteTitleInput, noteContentInput; +let themeToggleBtn, body; +let notes = []; +let currentEditingId = null; - const themeToggleBtn = document.getElementById('themeToggleBtn'); - const body = document.body; +async function initializeApp() { + // List of MWC components your app uses directly + const mwcComponents = [ + 'md-icon', 'md-fab', 'md-dialog', 'md-filled-button', 'md-text-button', + 'md-icon-button', 'md-filled-text-field', 'md-outlined-text-field', 'md-elevation' + ]; - let notes = []; - let currentEditingId = null; + try { + // Wait for all essential MWC components to be defined + await Promise.all(mwcComponents.map(comp => customElements.whenDefined(comp))); + console.log("All MWC components are ready."); + } catch (error) { + console.error("Error waiting for MWC components:", error); + document.body.classList.add('loaded'); // Show body anyway + const errorDiv = document.createElement('div'); + errorDiv.innerHTML = ` +

Error loading application components. The page might not work correctly.

+

Please try refreshing. If the problem persists, check your browser console for details.

+ `; + errorDiv.style.color = "red"; + errorDiv.style.textAlign = "center"; + errorDiv.style.padding = "20px"; + document.body.prepend(errorDiv); + return; // Stop further initialization if critical components failed + } + + // --- Assign DOM elements AFTER MWC components are ready and upgraded --- + notesList = document.getElementById('notesList'); + addNoteFab = document.getElementById('addNoteFab'); + noteDialog = document.getElementById('noteDialog'); + noteForm = document.getElementById('noteForm'); + noteIdInput = document.getElementById('noteIdInput'); + noteTitleInput = document.getElementById('noteTitleInput'); + noteContentInput = document.getElementById('noteContentInput'); + themeToggleBtn = document.getElementById('themeToggleBtn'); + body = document.body; // --- 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); + // Ensure applyTheme is called after themeToggleBtn and body are defined. + loadInitialTheme(); // This will call applyTheme - 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 - } - } - } + // --- Event Listeners (Attach now that elements are MWC-upgraded) --- themeToggleBtn.addEventListener('click', toggleTheme); - - // --- Data Persistence --- - function loadNotes() { - const storedNotes = localStorage.getItem('material-notes'); - notes = storedNotes ? JSON.parse(storedNotes) : []; - renderNotes(); - } - - function saveNotes() { - localStorage.setItem('material-notes', JSON.stringify(notes)); - } - - // --- Rendering Notes --- - function renderNotes() { - notesList.innerHTML = ''; // Clear existing notes - if (notes.length === 0) { - 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'; - - 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 ? '...' : ''); - - const actions = document.createElement('div'); - actions.className = 'note-card-actions'; - - const editButton = document.createElement('md-icon-button'); - editButton.innerHTML = `edit`; - 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: ${note.title}`; - deleteButton.addEventListener('click', (e) => { e.stopPropagation(); deleteNote(note.id); }); - - actions.appendChild(editButton); - actions.appendChild(deleteButton); - - noteCard.appendChild(title); - noteCard.appendChild(content); - noteCard.appendChild(actions); - notesList.appendChild(noteCard); - }); - } - - // --- Note Operations --- - function openNoteDialog(note = null) { - 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; - noteTitleInput.value = note.title; - noteContentInput.value = note.content; // Full content for editing - noteDialog.querySelector('[slot="headline"]').textContent = 'Edit Note'; - } else { - currentEditingId = null; - noteDialog.querySelector('[slot="headline"]').textContent = 'Add New Note'; - } - noteDialog.show(); - } - - 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(); - - 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 = ""; - } - - 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(); - renderNotes(); - } - } - - // --- Event Listeners --- addNoteFab.addEventListener('click', () => openNoteDialog()); noteDialog.addEventListener('closed', handleDialogClose); - // --- Initial Load --- - loadInitialTheme(); - loadNotes(); -}); + + // --- Initial Data Load --- + loadNotes(); // This calls renderNotes, which creates more MWC elements + + // --- Make body visible now that everything is set up --- + document.body.classList.add('loaded'); + console.log("MNotes App Initialized and Visible."); +} + +// --- Theming Functions --- +function loadInitialTheme() { + const savedTheme = localStorage.getItem('mnotes-theme'); + if (savedTheme) { + applyTheme(savedTheme); + } else { + applyTheme(window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'); + } +} + +function applyTheme(theme) { // theme is 'light' or 'dark' + if (!body || !themeToggleBtn) { + console.warn("Body or themeToggleBtn not ready for applyTheme."); + return; + } + body.classList.remove('light-theme', 'dark-theme'); + body.classList.add(theme + '-theme'); + localStorage.setItem('mnotes-theme', theme); + + const iconElement = themeToggleBtn.querySelector('md-icon'); + if (iconElement) { + iconElement.textContent = theme === 'dark' ? 'light_mode' : 'dark_mode'; + themeToggleBtn.ariaLabel = theme === 'dark' ? "Activate light theme" : "Activate dark theme"; + } +} + +function toggleTheme() { + const currentTheme = body.classList.contains('dark-theme') ? 'dark' : 'light'; + applyTheme(currentTheme === 'light' ? 'dark' : 'light'); +} + +// --- Data Persistence --- +function loadNotes() { + const storedNotes = localStorage.getItem('material-notes'); + notes = storedNotes ? JSON.parse(storedNotes) : []; + renderNotes(); +} + +function saveNotes() { + localStorage.setItem('material-notes', JSON.stringify(notes)); +} + +// --- Rendering Notes --- +function renderNotes() { + if (!notesList) { + console.error("notesList element not found during renderNotes. App not fully initialized?"); + return; + } + notesList.innerHTML = ''; // Clear existing notes + if (notes.length === 0) { + 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)); + + notes.forEach(note => { + const noteCard = document.createElement('div'); + noteCard.className = 'note-card'; + + const elevation = document.createElement('md-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 ? '...' : ''); + + const actions = document.createElement('div'); + actions.className = 'note-card-actions'; + + const editButton = document.createElement('md-icon-button'); + editButton.innerHTML = `edit`; + 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: ${note.title}`; + deleteButton.addEventListener('click', (e) => { e.stopPropagation(); deleteNote(note.id); }); + + actions.appendChild(editButton); + actions.appendChild(deleteButton); + + noteCard.appendChild(title); + noteCard.appendChild(content); + noteCard.appendChild(actions); + notesList.appendChild(noteCard); + }); +} + +// --- Note Operations --- +function openNoteDialog(note = null) { + if (!noteForm || !noteTitleInput || !noteContentInput || !noteDialog) { + console.error("Dialog elements not found for openNoteDialog."); + return; + } + noteForm.reset(); + noteTitleInput.error = false; noteTitleInput.errorText = ""; + noteContentInput.error = false; noteContentInput.errorText = ""; + + const headline = noteDialog.querySelector('[slot="headline"]'); + + if (note) { + currentEditingId = note.id; + noteIdInput.value = note.id; // Keep track of ID if needed by form + noteTitleInput.value = note.title; + noteContentInput.value = note.content; + if(headline) headline.textContent = 'Edit Note'; + } else { + currentEditingId = null; + noteIdInput.value = ''; + if(headline) headline.textContent = 'Add New Note'; + } + noteDialog.show(); +} + +function handleDialogClose(event) { + if (event.detail.action === 'save') { // 'action' is the value of the button that closed the dialog + const title = noteTitleInput.value.trim(); + const content = noteContentInput.value.trim(); + + 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 = ""; + } + + if (!isValid) { + // If form is invalid and dialog closed (e.g. via form method="dialog"), reopen it. + // Need a slight delay for the dialog to fully process its closing action. + setTimeout(() => { if (noteDialog) noteDialog.show(); }, 0); + 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, regardless of save or cancel + currentEditingId = null; + if (noteForm) noteForm.reset(); + if (noteTitleInput) { noteTitleInput.error = false; noteTitleInput.errorText = ""; } + if (noteContentInput) { noteContentInput.error = false; noteContentInput.errorText = ""; } +} + +function deleteNote(id) { + // Consider a md-dialog for confirmation for Material consistency + if (confirm('Are you sure you want to delete this note?')) { + notes = notes.filter(note => note.id !== id); + saveNotes(); + renderNotes(); + } +} + +// --- Start the application initialization --- +// This ensures that the script attempts to initialize after the page DOM is loaded +// and MWC has had a chance to load and start defining components. +if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', initializeApp); +} else { + initializeApp(); +} From 617b916ed034914d070c2efc82ee934969b29511 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:21:35 +0200 Subject: [PATCH 04/12] Update index.html The fuq is still happenin with it --- index.html | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/index.html b/index.html index b674e3f..aef3383 100644 --- a/index.html +++ b/index.html @@ -15,7 +15,18 @@ - + + + + + + .toggle-sidebar { + display: block; + } + } + + .hidden { + display: none; + } + + .material-symbols-outlined { + font-size: 24px; + } + -
-
-

MNotes

- - dark_mode - -
+
+
+ +

MNotes

+
+
+ + + +
+
-
- -
- - - add - +
+ - -
Add/Edit Note
- - - - - -
- Cancel - Save -
-
+
+
+ + + + +
+ +
+ 0 words + Not saved yet +
+
+
- + + + + + + + From 95c0ffd6e881af8401f4727b6b06c31b7b5c5df6 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:54:33 +0200 Subject: [PATCH 11/12] Delete script.js --- script.js | 248 ------------------------------------------------------ 1 file changed, 248 deletions(-) delete mode 100644 script.js diff --git a/script.js b/script.js deleted file mode 100644 index 532cd5b..0000000 --- a/script.js +++ /dev/null @@ -1,248 +0,0 @@ -// Define variables in a scope accessible by functions -let notesList, addNoteFab, noteDialog, noteForm, noteIdInput, noteTitleInput, noteContentInput; -let themeToggleBtn, body; -let notes = []; -let currentEditingId = null; - -async function initializeApp() { - // List of MWC components your app uses directly - const mwcComponents = [ - 'md-icon', 'md-fab', 'md-dialog', 'md-filled-button', 'md-text-button', - 'md-icon-button', 'md-filled-text-field', 'md-outlined-text-field', 'md-elevation' - ]; - - try { - // Wait for all essential MWC components to be defined - await Promise.all(mwcComponents.map(comp => customElements.whenDefined(comp))); - console.log("All MWC components are ready."); - } catch (error) { - console.error("Error waiting for MWC components:", error); - document.body.classList.add('loaded'); // Show body anyway - const errorDiv = document.createElement('div'); - errorDiv.innerHTML = ` -

Error loading application components. The page might not work correctly.

-

Please try refreshing. If the problem persists, check your browser console for details.

- `; - errorDiv.style.color = "red"; - errorDiv.style.textAlign = "center"; - errorDiv.style.padding = "20px"; - document.body.prepend(errorDiv); - return; // Stop further initialization if critical components failed - } - - // --- Assign DOM elements AFTER MWC components are ready and upgraded --- - notesList = document.getElementById('notesList'); - addNoteFab = document.getElementById('addNoteFab'); - noteDialog = document.getElementById('noteDialog'); - noteForm = document.getElementById('noteForm'); - noteIdInput = document.getElementById('noteIdInput'); - noteTitleInput = document.getElementById('noteTitleInput'); - noteContentInput = document.getElementById('noteContentInput'); - themeToggleBtn = document.getElementById('themeToggleBtn'); - body = document.body; - - // --- Theming --- - // Ensure applyTheme is called after themeToggleBtn and body are defined. - loadInitialTheme(); // This will call applyTheme - - // --- Event Listeners (Attach now that elements are MWC-upgraded) --- - themeToggleBtn.addEventListener('click', toggleTheme); - addNoteFab.addEventListener('click', () => openNoteDialog()); - noteDialog.addEventListener('closed', handleDialogClose); - - - // --- Initial Data Load --- - loadNotes(); // This calls renderNotes, which creates more MWC elements - - // --- Make body visible now that everything is set up --- - document.body.classList.add('loaded'); - console.log("MNotes App Initialized and Visible."); -} - -// --- Theming Functions --- -function loadInitialTheme() { - const savedTheme = localStorage.getItem('mnotes-theme'); - if (savedTheme) { - applyTheme(savedTheme); - } else { - applyTheme(window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'); - } -} - -function applyTheme(theme) { // theme is 'light' or 'dark' - if (!body || !themeToggleBtn) { - console.warn("Body or themeToggleBtn not ready for applyTheme."); - return; - } - body.classList.remove('light-theme', 'dark-theme'); - body.classList.add(theme + '-theme'); - localStorage.setItem('mnotes-theme', theme); - - const iconElement = themeToggleBtn.querySelector('md-icon'); - if (iconElement) { - iconElement.textContent = theme === 'dark' ? 'light_mode' : 'dark_mode'; - themeToggleBtn.ariaLabel = theme === 'dark' ? "Activate light theme" : "Activate dark theme"; - } -} - -function toggleTheme() { - const currentTheme = body.classList.contains('dark-theme') ? 'dark' : 'light'; - applyTheme(currentTheme === 'light' ? 'dark' : 'light'); -} - -// --- Data Persistence --- -function loadNotes() { - const storedNotes = localStorage.getItem('material-notes'); - notes = storedNotes ? JSON.parse(storedNotes) : []; - renderNotes(); -} - -function saveNotes() { - localStorage.setItem('material-notes', JSON.stringify(notes)); -} - -// --- Rendering Notes --- -function renderNotes() { - if (!notesList) { - console.error("notesList element not found during renderNotes. App not fully initialized?"); - return; - } - notesList.innerHTML = ''; // Clear existing notes - if (notes.length === 0) { - 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)); - - notes.forEach(note => { - const noteCard = document.createElement('div'); - noteCard.className = 'note-card'; - - const elevation = document.createElement('md-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 ? '...' : ''); - - const actions = document.createElement('div'); - actions.className = 'note-card-actions'; - - const editButton = document.createElement('md-icon-button'); - editButton.innerHTML = `edit`; - 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: ${note.title}`; - deleteButton.addEventListener('click', (e) => { e.stopPropagation(); deleteNote(note.id); }); - - actions.appendChild(editButton); - actions.appendChild(deleteButton); - - noteCard.appendChild(title); - noteCard.appendChild(content); - noteCard.appendChild(actions); - notesList.appendChild(noteCard); - }); -} - -// --- Note Operations --- -function openNoteDialog(note = null) { - if (!noteForm || !noteTitleInput || !noteContentInput || !noteDialog) { - console.error("Dialog elements not found for openNoteDialog."); - return; - } - noteForm.reset(); - noteTitleInput.error = false; noteTitleInput.errorText = ""; - noteContentInput.error = false; noteContentInput.errorText = ""; - - const headline = noteDialog.querySelector('[slot="headline"]'); - - if (note) { - currentEditingId = note.id; - noteIdInput.value = note.id; // Keep track of ID if needed by form - noteTitleInput.value = note.title; - noteContentInput.value = note.content; - if(headline) headline.textContent = 'Edit Note'; - } else { - currentEditingId = null; - noteIdInput.value = ''; - if(headline) headline.textContent = 'Add New Note'; - } - noteDialog.show(); -} - -function handleDialogClose(event) { - if (event.detail.action === 'save') { // 'action' is the value of the button that closed the dialog - const title = noteTitleInput.value.trim(); - const content = noteContentInput.value.trim(); - - 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 = ""; - } - - if (!isValid) { - // If form is invalid and dialog closed (e.g. via form method="dialog"), reopen it. - // Need a slight delay for the dialog to fully process its closing action. - setTimeout(() => { if (noteDialog) noteDialog.show(); }, 0); - 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, regardless of save or cancel - currentEditingId = null; - if (noteForm) noteForm.reset(); - if (noteTitleInput) { noteTitleInput.error = false; noteTitleInput.errorText = ""; } - if (noteContentInput) { noteContentInput.error = false; noteContentInput.errorText = ""; } -} - -function deleteNote(id) { - // Consider a md-dialog for confirmation for Material consistency - if (confirm('Are you sure you want to delete this note?')) { - notes = notes.filter(note => note.id !== id); - saveNotes(); - renderNotes(); - } -} - -// --- Start the application initialization --- -// This ensures that the script attempts to initialize after the page DOM is loaded -// and MWC has had a chance to load and start defining components. -if (document.readyState === 'loading') { - document.addEventListener('DOMContentLoaded', initializeApp); -} else { - initializeApp(); -} From c32bf888e7b226dce8ed1e85ec205b9386d99cf6 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:54:45 +0200 Subject: [PATCH 12/12] Delete style.css --- style.css | 124 ------------------------------------------------------ 1 file changed, 124 deletions(-) delete mode 100644 style.css diff --git a/style.css b/style.css deleted file mode 100644 index 0f7d163..0000000 --- a/style.css +++ /dev/null @@ -1,124 +0,0 @@ -/* Global variables for theming (from previous version, ensure they are complete) */ -:root { - /* Light Theme (Default) */ - --app-bg-color: #FEF7FF; - --app-text-color: #1D1B20; - --app-surface-color: #FDFCFB; - --app-primary-color: #6750A4; - --app-on-primary-color: #FFFFFF; - --app-secondary-color: #625B71; - --app-outline-color: #79747E; - --app-note-card-bg: #EADDFF; /* Primary Container */ - --app-note-card-text: #21005D; /* On Primary Container */ - --app-note-card-header: var(--app-primary-color); - - /* MWC Theme Token Mapping */ - --md-sys-color-background: var(--app-bg-color); - --md-sys-color-on-background: var(--app-text-color); - --md-sys-color-surface: var(--app-surface-color); - --md-sys-color-surface-dim: #DED8E1; - --md-sys-color-surface-bright: #FEF7FF; - --md-sys-color-surface-container-lowest: #FFFFFF; - --md-sys-color-surface-container-low: #F7F2FA; - --md-sys-color-surface-container: #F3EDF7; - --md-sys-color-surface-container-high: #ECE6F0; - --md-sys-color-surface-container-highest: #E6E0E9; - --md-sys-color-surface-variant: #E7E0EC; - --md-sys-color-on-surface: var(--app-text-color); - --md-sys-color-on-surface-variant: #49454F; - --md-sys-color-primary: var(--app-primary-color); - --md-sys-color-on-primary: var(--app-on-primary-color); - --md-sys-color-secondary: var(--app-secondary-color); - --md-sys-color-on-secondary: #FFFFFF; - --md-sys-color-tertiary: #7D5260; - --md-sys-color-on-tertiary: #FFFFFF; - --md-sys-color-error: #B3261E; - --md-sys-color-on-error: #FFFFFF; - --md-sys-color-outline: var(--app-outline-color); - --md-sys-color-shadow: #000000; - --md-sys-color-surface-tint-color: var(--app-primary-color); - - /* Expressive shape tokens */ - --md-sys-shape-corner-extra-small: 4px; - --md-sys-shape-corner-small: 8px; - --md-sys-shape-corner-medium: 12px; - --md-sys-shape-corner-large: 16px; /* Used for cards, FABs */ - --md-sys-shape-corner-extra-large: 28px; /* Used for dialogs */ - --md-sys-shape-corner-full: 9999px; /* For fully rounded "pill" shapes */ -} - -body.dark-theme { - /* Dark Theme Overrides */ - --app-bg-color: #141218; - --app-text-color: #E6E0E9; - --app-surface-color: #1C1B1F; - --app-primary-color: #D0BCFF; - --app-on-primary-color: #381E72; - --app-secondary-color: #CCC2DC; - --app-outline-color: #938F99; - --app-note-card-bg: #36343B; /* Surface Container Highest (Dark) */ - --app-note-card-text: #CAC4D0; /* On Surface Variant (Dark) */ - --app-note-card-header: var(--app-primary-color); - - /* MWC Dark Theme Token Mapping (subset) */ - --md-sys-color-surface-dim: #141218; - --md-sys-color-surface-bright: #3B383E; - --md-sys-color-surface-container-lowest: #0F0D13; - --md-sys-color-surface-container-low: #1D1B20; - --md-sys-color-surface-container: #211F26; - --md-sys-color-surface-container-high: #2B2930; - --md-sys-color-surface-container-highest: #36343B; - --md-sys-color-on-secondary: #332D41; - --md-sys-color-on-tertiary: #492532; - --md-sys-color-on-error: #601410; - --md-sys-color-surface-tint-color: var(--app-primary-color); -} - -body { - font-family: 'Roboto', sans-serif; - margin: 0; - background-color: var(--md-sys-color-background); - color: var(--md-sys-color-on-background); - transition: background-color 0.3s, color 0.3s; -} - -.container { max-width: 800px; margin: 0 auto; padding: 16px; } -header { display: flex; align-items: center; justify-content: space-between; padding-bottom: 16px; margin-bottom: 20px; } -header h1 { margin: 0; color: var(--md-sys-color-primary); font-size: 2em; } -#themeToggleBtn md-icon { color: var(--md-sys-color-secondary); } - -.note-card { - margin-bottom: 16px; - padding: 16px; - border-radius: var(--md-sys-shape-corner-large); /* 16px */ - background-color: var(--app-note-card-bg); - color: var(--app-note-card-text); - position: relative; - overflow: hidden; -} -.note-card md-elevation { --md-elevation-level: 1; } -.note-card h3 { margin-top: 0; margin-bottom: 8px; color: var(--app-note-card-header); font-size: 1.25em; font-weight: 500; } -.note-card p { margin-bottom: 8px; white-space: pre-wrap; font-size: 0.95em; line-height: 1.5; } -.note-card-actions { position: absolute; top: 8px; right: 8px; display: flex; } -.note-card-actions md-icon-button { --md-icon-button-icon-color: var(--app-note-card-text); } /* MWC default can be used */ -.note-card-actions md-icon-button:hover { /* MWC handles hover states well */ } - -md-fab { - position: fixed; - bottom: 24px; - right: 24px; - /* MWC FABs use --md-fab-container-shape, which defaults to --md-sys-shape-corner-large (16px for a standard FAB) */ - /* If you want it perfectly circular and it's a regular (non-extended) FAB: */ - /* --md-fab-container-shape: var(--md-sys-shape-corner-full); */ - /* However, the default is usually quite rounded already. Primary variant is good. */ -} - -md-dialog { - /* --md-dialog-container-shape default is --md-sys-shape-corner-extra-large (28px) */ - --md-dialog-container-color: var(--md-sys-color-surface-container-high); - --md-dialog-headline-color: var(--md-sys-color-on-surface-variant); - --md-dialog-supporting-text-color: var(--md-sys-color-on-surface-variant); -} -md-dialog [slot="headline"] { font-size: 1.5em; font-weight: 500; } - -#notesList > p { text-align: center; color: var(--md-sys-color-outline); padding: 20px 0; font-size: 1.1em; }