Fix note entry dialog
This commit is contained in:
parent
13042bcfa3
commit
55c8180693
3 changed files with 117 additions and 49 deletions
88
app.js
88
app.js
|
|
@ -21,6 +21,7 @@ let editingOriginalRefIds = []; // ref IDs present when edit modal opened
|
||||||
// notes modal state
|
// notes modal state
|
||||||
let notesTuneId = null;
|
let notesTuneId = null;
|
||||||
let notesEntryId = null;
|
let notesEntryId = null;
|
||||||
|
let noteEditorState = null; // { type: 'tune'|'inst', noteId: null|number }
|
||||||
|
|
||||||
let unlocked = false;
|
let unlocked = false;
|
||||||
|
|
||||||
|
|
@ -840,35 +841,62 @@ function renderInstNotes(notes) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
$('btn-add-tune-note').addEventListener('click', async () => {
|
$('btn-add-tune-note').addEventListener('click', () => showNoteEditor('tune', null));
|
||||||
const text = prompt('Note:');
|
$('btn-add-inst-note').addEventListener('click', () => showNoteEditor('inst', null));
|
||||||
if (!text) return;
|
|
||||||
try {
|
|
||||||
await apiFetch(`/tunes/${notesTuneId}/notes`, { method: 'POST', body: JSON.stringify({ note: text }) });
|
|
||||||
await reloadNotesModal();
|
|
||||||
} catch (e) { setStatus(e.message, true); }
|
|
||||||
});
|
|
||||||
|
|
||||||
$('btn-add-inst-note').addEventListener('click', async () => {
|
function editTuneNote(n) { showNoteEditor('tune', n.id, n.note); }
|
||||||
const text = prompt('Note:');
|
function editInstNote(n) { showNoteEditor('inst', n.id, n.note); }
|
||||||
if (!text) return;
|
|
||||||
try {
|
|
||||||
await apiFetch(`/tunes/${notesTuneId}/instruments/${notesEntryId}/notes`,
|
|
||||||
{ method: 'POST', body: JSON.stringify({ note: text }) });
|
|
||||||
await reloadNotesModal();
|
|
||||||
} catch (e) { setStatus(e.message, true); }
|
|
||||||
});
|
|
||||||
|
|
||||||
async function editTuneNote(n) {
|
function showNoteEditor(type, noteId, existingText = '') {
|
||||||
const text = prompt('Edit note:', n.note);
|
noteEditorState = { type, noteId };
|
||||||
if (text === null) return;
|
$('note-editor-title').textContent = noteId ? 'Edit note' : 'Add note';
|
||||||
try {
|
$('note-editor-ta').value = existingText;
|
||||||
await apiFetch(`/tunes/${notesTuneId}/notes/${n.id}`,
|
$('note-editor-modal').showModal();
|
||||||
{ method: 'PATCH', body: JSON.stringify({ note: text }) });
|
$('note-editor-ta').focus();
|
||||||
await reloadNotesModal();
|
|
||||||
} catch (e) { setStatus(e.message, true); }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function hideNoteEditor() {
|
||||||
|
noteEditorState = null;
|
||||||
|
$('note-editor-ta').value = '';
|
||||||
|
$('note-editor-modal').close();
|
||||||
|
}
|
||||||
|
|
||||||
|
$('btn-note-cancel').addEventListener('click', hideNoteEditor);
|
||||||
|
|
||||||
|
$('note-editor-ta').addEventListener('keydown', e => {
|
||||||
|
if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') $('btn-note-save').click();
|
||||||
|
if (e.key === 'Escape') hideNoteEditor();
|
||||||
|
});
|
||||||
|
|
||||||
|
$('btn-note-save').addEventListener('click', async () => {
|
||||||
|
if (!noteEditorState) return;
|
||||||
|
const text = $('note-editor-ta').value.trim();
|
||||||
|
if (!text) return;
|
||||||
|
const { type, noteId } = noteEditorState;
|
||||||
|
$('btn-note-save').disabled = true;
|
||||||
|
try {
|
||||||
|
if (type === 'tune') {
|
||||||
|
await apiFetch(
|
||||||
|
noteId ? `/tunes/${notesTuneId}/notes/${noteId}` : `/tunes/${notesTuneId}/notes`,
|
||||||
|
{ method: noteId ? 'PATCH' : 'POST', body: JSON.stringify({ note: text }) }
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
await apiFetch(
|
||||||
|
noteId
|
||||||
|
? `/tunes/${notesTuneId}/instruments/${notesEntryId}/notes/${noteId}`
|
||||||
|
: `/tunes/${notesTuneId}/instruments/${notesEntryId}/notes`,
|
||||||
|
{ method: noteId ? 'PATCH' : 'POST', body: JSON.stringify({ note: text }) }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
hideNoteEditor();
|
||||||
|
await reloadNotesModal();
|
||||||
|
} catch (e) {
|
||||||
|
setStatus(e.message, true);
|
||||||
|
} finally {
|
||||||
|
$('btn-note-save').disabled = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
async function deleteTuneNote(noteId) {
|
async function deleteTuneNote(noteId) {
|
||||||
if (!confirm('Delete this note?')) return;
|
if (!confirm('Delete this note?')) return;
|
||||||
try {
|
try {
|
||||||
|
|
@ -877,16 +905,6 @@ async function deleteTuneNote(noteId) {
|
||||||
} catch (e) { setStatus(e.message, true); }
|
} catch (e) { setStatus(e.message, true); }
|
||||||
}
|
}
|
||||||
|
|
||||||
async function editInstNote(n) {
|
|
||||||
const text = prompt('Edit note:', n.note);
|
|
||||||
if (text === null) return;
|
|
||||||
try {
|
|
||||||
await apiFetch(`/tunes/${notesTuneId}/instruments/${notesEntryId}/notes/${n.id}`,
|
|
||||||
{ method: 'PATCH', body: JSON.stringify({ note: text }) });
|
|
||||||
await reloadNotesModal();
|
|
||||||
} catch (e) { setStatus(e.message, true); }
|
|
||||||
}
|
|
||||||
|
|
||||||
async function deleteInstNote(noteId) {
|
async function deleteInstNote(noteId) {
|
||||||
if (!confirm('Delete this note?')) return;
|
if (!confirm('Delete this note?')) return;
|
||||||
try {
|
try {
|
||||||
|
|
|
||||||
12
index.html
12
index.html
|
|
@ -171,7 +171,7 @@
|
||||||
</div>
|
</div>
|
||||||
</dialog>
|
</dialog>
|
||||||
|
|
||||||
<!-- ── Notes modal ──────────────────────────────────────────── -->
|
<!-- ── Notes modal ───────────────────────────────── -->
|
||||||
<dialog id="notes-modal">
|
<dialog id="notes-modal">
|
||||||
<h2 id="notes-modal-title">Notes</h2>
|
<h2 id="notes-modal-title">Notes</h2>
|
||||||
<div id="tune-notes-section">
|
<div id="tune-notes-section">
|
||||||
|
|
@ -189,6 +189,16 @@
|
||||||
</div>
|
</div>
|
||||||
</dialog>
|
</dialog>
|
||||||
|
|
||||||
|
<!-- ── Note editor modal (fullscreen, separate from notes list) ─ -->
|
||||||
|
<dialog id="note-editor-modal">
|
||||||
|
<h2 id="note-editor-title">Add note</h2>
|
||||||
|
<textarea id="note-editor-ta" placeholder="Write your note… (Ctrl+↵ to save)"></textarea>
|
||||||
|
<div class="modal-actions">
|
||||||
|
<button id="btn-note-save">Save note</button>
|
||||||
|
<button id="btn-note-cancel">Cancel</button>
|
||||||
|
</div>
|
||||||
|
</dialog>
|
||||||
|
|
||||||
<!-- ── Call modal ─────────────────────────────────────────── -->
|
<!-- ── Call modal ─────────────────────────────────────────── -->
|
||||||
<dialog id="call-modal">
|
<dialog id="call-modal">
|
||||||
<div id="call-header">
|
<div id="call-header">
|
||||||
|
|
|
||||||
62
style.css
62
style.css
|
|
@ -255,6 +255,9 @@ th.sort-desc .sort-indicator::after { content: '▼'; }
|
||||||
|
|
||||||
/* ── Modal ────────────────────────────────────────────────── */
|
/* ── Modal ────────────────────────────────────────────────── */
|
||||||
dialog {
|
dialog {
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
margin: auto;
|
||||||
background: var(--surface);
|
background: var(--surface);
|
||||||
color: var(--text);
|
color: var(--text);
|
||||||
border: 1px solid var(--border);
|
border: 1px solid var(--border);
|
||||||
|
|
@ -356,7 +359,7 @@ fieldset.active .fs-body { display: contents; }
|
||||||
border-bottom: 1px solid var(--border);
|
border-bottom: 1px solid var(--border);
|
||||||
}
|
}
|
||||||
.note-row:last-child { border-bottom: none; }
|
.note-row:last-child { border-bottom: none; }
|
||||||
.note-text { flex: 1; font-size: .875rem; }
|
.note-text { flex: 1; font-size: .875rem; white-space: pre-wrap; word-break: break-word; }
|
||||||
.note-edit-controls { display: flex; gap: .25rem; }
|
.note-edit-controls { display: flex; gap: .25rem; }
|
||||||
|
|
||||||
.btn-edit-row { background: transparent; border: none; font-size: 1rem; padding: .2rem .3rem; }
|
.btn-edit-row { background: transparent; border: none; font-size: 1rem; padding: .2rem .3rem; }
|
||||||
|
|
@ -392,6 +395,43 @@ fieldset.active .fs-body { display: contents; }
|
||||||
#btn-save-tune { background: var(--accent); }
|
#btn-save-tune { background: var(--accent); }
|
||||||
#btn-cancel { background: transparent; border: 1px solid var(--border); }
|
#btn-cancel { background: transparent; border: 1px solid var(--border); }
|
||||||
|
|
||||||
|
#notes-modal {
|
||||||
|
max-width: 700px;
|
||||||
|
height: 70vh;
|
||||||
|
max-height: 85vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
#note-editor-modal {
|
||||||
|
max-width: 700px;
|
||||||
|
height: 90vh;
|
||||||
|
max-height: 95vh;
|
||||||
|
}
|
||||||
|
#note-editor-modal[open] {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
#note-editor-ta {
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
resize: vertical;
|
||||||
|
background: var(--bg);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
color: var(--text);
|
||||||
|
border-radius: var(--radius);
|
||||||
|
padding: .5rem .65rem;
|
||||||
|
font-size: .875rem;
|
||||||
|
font-family: var(--font);
|
||||||
|
line-height: 1.5;
|
||||||
|
margin-bottom: .5rem;
|
||||||
|
}
|
||||||
|
#note-editor-ta:focus {
|
||||||
|
outline: 2px solid var(--accent2);
|
||||||
|
outline-offset: -1px;
|
||||||
|
}
|
||||||
|
|
||||||
/* ── Detail modal ─────────────────────────────────────────── */
|
/* ── Detail modal ─────────────────────────────────────────── */
|
||||||
#detail-content h2 { margin-bottom: .75rem; }
|
#detail-content h2 { margin-bottom: .75rem; }
|
||||||
#detail-content table { width: 100%; border-collapse: collapse; font-size: .85rem; }
|
#detail-content table { width: 100%; border-collapse: collapse; font-size: .85rem; }
|
||||||
|
|
@ -480,17 +520,17 @@ fieldset.active .fs-body { display: contents; }
|
||||||
}
|
}
|
||||||
.mob-actions { display: flex; gap: .2rem; flex-shrink: 0; }
|
.mob-actions { display: flex; gap: .2rem; flex-shrink: 0; }
|
||||||
|
|
||||||
/* Modals: fullscreen */
|
/* Modals: fullscreen — !important beats any ID-specific desktop size rules */
|
||||||
dialog {
|
dialog {
|
||||||
position: fixed;
|
position: fixed !important;
|
||||||
inset: 0;
|
inset: 0 !important;
|
||||||
width: 100%;
|
width: 100% !important;
|
||||||
max-width: 100%;
|
max-width: 100% !important;
|
||||||
height: 100%;
|
height: 100% !important;
|
||||||
max-height: 100%;
|
max-height: 100% !important;
|
||||||
border-radius: 0;
|
border-radius: 0 !important;
|
||||||
border: none;
|
border: none !important;
|
||||||
margin: 0;
|
margin: 0 !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue