Update script.js

This commit is contained in:
Jakub Růžička 2025-05-20 20:14:43 +02:00 committed by GitHub
parent 9a4a97984b
commit df7424d0f1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

200
script.js
View file

@ -1,53 +1,94 @@
document.addEventListener('DOMContentLoaded', () => { // Define variables in a scope accessible by functions
const notesList = document.getElementById('notesList'); let notesList, addNoteFab, noteDialog, noteForm, noteIdInput, noteTitleInput, noteContentInput;
const addNoteFab = document.getElementById('addNoteFab'); let themeToggleBtn, body;
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');
const themeToggleBtn = document.getElementById('themeToggleBtn');
const body = document.body;
let notes = []; let notes = [];
let currentEditingId = null; 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 = `
<p>Error loading application components. The page might not work correctly.</p>
<p>Please try refreshing. If the problem persists, check your browser console for details.</p>
`;
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 --- // --- Theming ---
function applyTheme(theme) { // theme is 'light' or 'dark' // Ensure applyTheme is called after themeToggleBtn and body are defined.
body.classList.remove('light-theme', 'dark-theme'); loadInitialTheme(); // This will call applyTheme
body.classList.add(theme + '-theme'); // e.g., 'dark-theme'
localStorage.setItem('mnotes-theme', theme);
const icon = themeToggleBtn.querySelector('md-icon'); // --- Event Listeners (Attach now that elements are MWC-upgraded) ---
if (theme === 'dark') { themeToggleBtn.addEventListener('click', toggleTheme);
icon.textContent = 'light_mode'; addNoteFab.addEventListener('click', () => openNoteDialog());
themeToggleBtn.ariaLabel = "Activate light theme"; noteDialog.addEventListener('closed', handleDialogClose);
} else {
icon.textContent = 'dark_mode';
themeToggleBtn.ariaLabel = "Activate dark theme"; // --- Initial Data Load ---
} loadNotes(); // This calls renderNotes, which creates more MWC elements
}
// --- Make body visible now that everything is set up ---
function toggleTheme() { document.body.classList.add('loaded');
const currentTheme = localStorage.getItem('mnotes-theme') || (body.classList.contains('dark-theme') ? 'dark' : 'light'); console.log("MNotes App Initialized and Visible.");
applyTheme(currentTheme === 'light' ? 'dark' : 'light');
} }
// --- Theming Functions ---
function loadInitialTheme() { function loadInitialTheme() {
const savedTheme = localStorage.getItem('mnotes-theme'); const savedTheme = localStorage.getItem('mnotes-theme');
if (savedTheme) { if (savedTheme) {
applyTheme(savedTheme); applyTheme(savedTheme);
} else { } else {
// Prefer system theme if no preference saved applyTheme(window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
applyTheme('dark');
} else {
applyTheme('light'); // Default to 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');
} }
themeToggleBtn.addEventListener('click', toggleTheme);
// --- Data Persistence --- // --- Data Persistence ---
function loadNotes() { function loadNotes() {
@ -62,6 +103,10 @@ document.addEventListener('DOMContentLoaded', () => {
// --- Rendering Notes --- // --- Rendering Notes ---
function renderNotes() { function renderNotes() {
if (!notesList) {
console.error("notesList element not found during renderNotes. App not fully initialized?");
return;
}
notesList.innerHTML = ''; // Clear existing notes notesList.innerHTML = ''; // Clear existing notes
if (notes.length === 0) { if (notes.length === 0) {
const p = document.createElement('p'); const p = document.createElement('p');
@ -70,13 +115,13 @@ document.addEventListener('DOMContentLoaded', () => {
return; return;
} }
notes.sort((a, b) => new Date(b.lastModified) - new Date(a.lastModified)); // Sort by most recently modified notes.sort((a, b) => new Date(b.lastModified) - new Date(a.lastModified));
notes.forEach(note => { notes.forEach(note => {
const noteCard = document.createElement('div'); const noteCard = document.createElement('div');
noteCard.className = 'note-card'; noteCard.className = 'note-card';
const elevation = document.createElement('md-elevation'); // Add MWC elevation const elevation = document.createElement('md-elevation');
noteCard.appendChild(elevation); noteCard.appendChild(elevation);
const title = document.createElement('h3'); const title = document.createElement('h3');
@ -110,56 +155,53 @@ document.addEventListener('DOMContentLoaded', () => {
// --- Note Operations --- // --- Note Operations ---
function openNoteDialog(note = null) { function openNoteDialog(note = null) {
noteForm.reset(); // Clear form fields if (!noteForm || !noteTitleInput || !noteContentInput || !noteDialog) {
// Reset MWC text field states (e.g. error messages) console.error("Dialog elements not found for openNoteDialog.");
noteTitleInput.errorText = ""; return;
noteTitleInput.error = false; }
noteContentInput.errorText = ""; noteForm.reset();
noteContentInput.error = false; noteTitleInput.error = false; noteTitleInput.errorText = "";
noteContentInput.error = false; noteContentInput.errorText = "";
const headline = noteDialog.querySelector('[slot="headline"]');
if (note) { if (note) {
currentEditingId = note.id; currentEditingId = note.id;
noteIdInput.value = note.id; // Keep track of ID if needed by form
noteTitleInput.value = note.title; noteTitleInput.value = note.title;
noteContentInput.value = note.content; // Full content for editing noteContentInput.value = note.content;
noteDialog.querySelector('[slot="headline"]').textContent = 'Edit Note'; if(headline) headline.textContent = 'Edit Note';
} else { } else {
currentEditingId = null; currentEditingId = null;
noteDialog.querySelector('[slot="headline"]').textContent = 'Add New Note'; noteIdInput.value = '';
if(headline) headline.textContent = 'Add New Note';
} }
noteDialog.show(); noteDialog.show();
} }
function handleDialogClose(event) { function handleDialogClose(event) {
// event.detail.action contains the `value` of the button that closed it if (event.detail.action === 'save') { // 'action' is the value of the button that closed the dialog
if (event.detail.action === 'save') {
const title = noteTitleInput.value.trim(); const title = noteTitleInput.value.trim();
const content = noteContentInput.value.trim(); const content = noteContentInput.value.trim();
let isValid = true; let isValid = true;
if (!title) { if (!title) {
noteTitleInput.error = true; noteTitleInput.error = true; noteTitleInput.errorText = "Title cannot be empty.";
noteTitleInput.errorText = "Title cannot be empty.";
isValid = false; isValid = false;
} else { } else {
noteTitleInput.error = false; noteTitleInput.error = false; noteTitleInput.errorText = "";
noteTitleInput.errorText = "";
} }
if (!content) { if (!content) {
noteContentInput.error = true; noteContentInput.error = true; noteContentInput.errorText = "Content cannot be empty.";
noteContentInput.errorText = "Content cannot be empty.";
isValid = false; isValid = false;
} else { } else {
noteContentInput.error = false; noteContentInput.error = false; noteContentInput.errorText = "";
noteContentInput.errorText = "";
} }
if (!isValid) { if (!isValid) {
// To prevent dialog from closing on invalid native form submission, // If form is invalid and dialog closed (e.g. via form method="dialog"), reopen it.
// we'd need to handle the 'submit' event on the form, preventDefault, // Need a slight delay for the dialog to fully process its closing action.
// then manually close if valid. setTimeout(() => { if (noteDialog) noteDialog.show(); }, 0);
// 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; return;
} }
@ -173,26 +215,22 @@ document.addEventListener('DOMContentLoaded', () => {
} }
} else { } else {
notes.push({ notes.push({
id: Date.now().toString(), id: Date.now().toString(), title, content,
title, createdAt: now, lastModified: now
content,
createdAt: now,
lastModified: now
}); });
} }
saveNotes(); saveNotes();
renderNotes(); renderNotes();
} }
// Reset for next time dialog opens // Reset for next time dialog opens, regardless of save or cancel
currentEditingId = null; currentEditingId = null;
noteForm.reset(); // Ensure form is clean if (noteForm) noteForm.reset();
noteTitleInput.error = false; noteTitleInput.errorText = ""; if (noteTitleInput) { noteTitleInput.error = false; noteTitleInput.errorText = ""; }
noteContentInput.error = false; noteContentInput.errorText = ""; if (noteContentInput) { noteContentInput.error = false; noteContentInput.errorText = ""; }
} }
function deleteNote(id) { function deleteNote(id) {
// Consider using an md-dialog for confirmation for better UX // Consider a md-dialog for confirmation for Material consistency
// For simplicity, using confirm() for now
if (confirm('Are you sure you want to delete this note?')) { if (confirm('Are you sure you want to delete this note?')) {
notes = notes.filter(note => note.id !== id); notes = notes.filter(note => note.id !== id);
saveNotes(); saveNotes();
@ -200,11 +238,11 @@ document.addEventListener('DOMContentLoaded', () => {
} }
} }
// --- Event Listeners --- // --- Start the application initialization ---
addNoteFab.addEventListener('click', () => openNoteDialog()); // This ensures that the script attempts to initialize after the page DOM is loaded
noteDialog.addEventListener('closed', handleDialogClose); // and MWC has had a chance to load and start defining components.
if (document.readyState === 'loading') {
// --- Initial Load --- document.addEventListener('DOMContentLoaded', initializeApp);
loadInitialTheme(); } else {
loadNotes(); initializeApp();
}); }