mirror of
https://github.com/thejakubruzicka/MNotes.git
synced 2025-07-10 22:44:05 +02:00
Update script.js
This commit is contained in:
parent
9a4a97984b
commit
df7424d0f1
1 changed files with 239 additions and 201 deletions
236
script.js
236
script.js
|
@ -1,67 +1,112 @@
|
||||||
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');
|
let notes = [];
|
||||||
const noteForm = document.getElementById('noteForm');
|
let currentEditingId = null;
|
||||||
const noteIdInput = document.getElementById('noteIdInput');
|
|
||||||
const noteTitleInput = document.getElementById('noteTitleInput');
|
|
||||||
const noteContentInput = document.getElementById('noteContentInput');
|
|
||||||
|
|
||||||
const themeToggleBtn = document.getElementById('themeToggleBtn');
|
async function initializeApp() {
|
||||||
const body = document.body;
|
// 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 = [];
|
try {
|
||||||
let currentEditingId = null;
|
// 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";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function toggleTheme() {
|
|
||||||
const currentTheme = localStorage.getItem('mnotes-theme') || (body.classList.contains('dark-theme') ? 'dark' : 'light');
|
|
||||||
applyTheme(currentTheme === 'light' ? 'dark' : 'light');
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadInitialTheme() {
|
// --- 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');
|
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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
themeToggleBtn.addEventListener('click', toggleTheme);
|
|
||||||
|
|
||||||
// --- Data Persistence ---
|
function applyTheme(theme) { // theme is 'light' or 'dark'
|
||||||
function loadNotes() {
|
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');
|
const storedNotes = localStorage.getItem('material-notes');
|
||||||
notes = storedNotes ? JSON.parse(storedNotes) : [];
|
notes = storedNotes ? JSON.parse(storedNotes) : [];
|
||||||
renderNotes();
|
renderNotes();
|
||||||
}
|
}
|
||||||
|
|
||||||
function saveNotes() {
|
function saveNotes() {
|
||||||
localStorage.setItem('material-notes', JSON.stringify(notes));
|
localStorage.setItem('material-notes', JSON.stringify(notes));
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- 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');
|
||||||
|
@ -106,60 +151,57 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||||
noteCard.appendChild(actions);
|
noteCard.appendChild(actions);
|
||||||
notesList.appendChild(noteCard);
|
notesList.appendChild(noteCard);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- 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,38 +215,34 @@ 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();
|
||||||
renderNotes();
|
renderNotes();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- 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();
|
||||||
});
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue