/ proof-of-concept / midiboot
Sign in
proof-of-concept / midiboot
midiboot / src / components / Library.tsx
39 lines · 1.6 KBBlameHistoryRaw
1import { store, useApp } from '../state/store.ts';
3export function Library() {
4 const app = useApp();
5 if (!app.libraryOpen) return null;
6 return (
7 <div className="modal-backdrop" onClick={() => store.setLibraryOpen(false)}>
8 <div className="modal" onClick={(e) => e.stopPropagation()}>
9 <div className="modal-head">
10 <h2>Songs</h2>
11 <button onClick={() => void store.newSong()}>New song</button>
12 <span className="spacer" />
13 <button onClick={() => store.setLibraryOpen(false)}>Close</button>
14 </div>
15 <div className="songlist">
16 {app.songs.length === 0 && <p className="dim">No songs yet.</p>}
17 {app.songs.map((s) => (
18 <div key={s.id} className={`songrow ${s.id === app.rec?.id ? 'active' : ''}`}>
19 <button className="songopen" onClick={() => void store.openSong(s.id)} title="Open">
20 <span className="songname">{s.name}</span>
21 <span className="dim">
22 {s.trackCount} track{s.trackCount === 1 ? '' : 's'}, {s.noteCount} notes, {new Date(s.updatedAt).toLocaleString()}
23 </span>
24 </button>
25 <button className="mini" title="Duplicate" onClick={() => void store.duplicateSong(s.id)}>⧉</button>
26 <button
27 className="mini danger"
28 title="Delete"
29 onClick={() => {
30 if (window.confirm(`Delete "${s.name}"? This cannot be undone.`)) void store.deleteSongById(s.id);
31 }}
32 >✕</button>
33 </div>
34 ))}
35 </div>
36 </div>
37 </div>
38 );
↑↓ move↵openescclose