import { html, useState, useCallback } from './vendor/htm-preact.js'; import { t } from './i18n.js'; import { Icon } from './icon.js'; /** * The two controls every settings pane is built from. * * Their own module rather than `group-settings.js`, because each app's * settings component now lives in its own file and is reached *through* the * app registry — so importing them from the page that renders them would make * a cycle (`group-settings` → `apps` → `-app-settings` → `group-settings`). * ES modules tolerate that and then hand you a temporal-dead-zone * `ReferenceError` at the first render, which is the same class of defect as * the hook-ordering one already recorded in CLAUDE.md. */ /** * A settings-section that folds — every section but the ones that are * really just a form to fill in (invite, pair-operator, approve-device): * hiding an input the operator is mid-typing-into behind a click they'd * have to undo is friction with nothing to show for it, but a section that * is only ever glanced at once it's configured (TMDB, scan tuning, the * danger zone) benefits from staying out of the way otherwise. `title` (an * already-built string/vnode) wins over `titleKey` when both are given — * the members-table heading needs a live count baked in, not just a * lookup. */ function CollapsibleSection({ titleKey, title, action, defaultOpen = true, children }) { const [open, setOpen] = useState(defaultOpen); return html`
${/* `action` sits *beside* the header button, never inside it: a
${open && html`
${children}
`} `; } /** * A modern on/off switch — replaces a plain checkbox or a "Turn on/off" * button wherever the setting itself is a straight binary (uploads * allowed, TMDB/MusicBrainz enabled). Still a real * under the hood (keyboard/screen-reader behaviour for free), just * restyled — see .toggle-switch in style.css. */ function ToggleSwitch({ checked, onChange, disabled, label }) { return html` `; } /** * The busy/message bookkeeping every settings pane does around one call. * * Each pane owns its own, rather than sharing the page's: two sections saving * at once is ordinary (an operator ticks a toggle in Videos while Music's * folder save is still in flight), and one shared flag would disable both and * then attribute one section's error to the other. */ function useSaver() { const [busy, setBusy] = useState(false); const [msg, setMsg] = useState(''); const run = useCallback(async (work) => { setBusy(true); setMsg(''); try { await work(); return true; } catch (err) { setMsg(err && err.message ? err.message : String(err)); return false; } finally { setBusy(false); } }, []); return { busy, msg, run, setMsg }; } export { CollapsibleSection, ToggleSwitch, useSaver };