feat(admin): settings-row pattern, switches, segmented tabs, save bar #327

Merged
Cordy merged 5 commits from feat/admin-srows into main 2026-08-27 17:01:05 +00:00
Owner

Admin redesign, slice B of four.

The problem

The twelve topics are the old Settings dialog's fields relocated into page slots by SET_SLOTS, so they inherited dialog habits: uppercase .field labels with .06em tracking, label-above-input stacking, bare checkboxes, and a right-aligned Save that is always present.

Approach — presentation only, no relocation rewrite

I kept SET_SLOTS and every field id, value binding and save handler exactly as they are. Two things happen after the existing move: each field gains a description, and each run of fields gets wrapped in a .setbox. Everything else is CSS scoped to #adminview, so the settings dialog and every other .field / .dlgactions on the page are untouched. That was the smaller and far less fragile of the two options.

  • Row pattern: name + explanation on the left, control on the right, via explicit grid placement — necessary because a .check row puts the <input> before its <label> in source, so auto-placement would seat the description in the wrong column.
  • Switches: appearance:none on the existing checkbox, styled as a 38×22 switch with a ::after knob. No markup or JS change, so every existing binding keeps working; honours prefers-reduced-motion.
  • Segmented tabs replacing the underline tabs, matching the list/grid toggle.
  • Save bar: .dlgactions containing an adm-save-* button becomes sticky, tinted, and hidden until something changed — with a live count ("1 unsaved change" / "N unsaved changes") and a Discard that restores the snapshot. Selected with :has() so the Updates topic's non-save .dlgactions (the "Check for updates" button) is unaffected.
  • Dirty tracking: admSnapshot() captures the baseline right after loadSettingsValues() — i.e. whatever the server just returned — and re-captures after a successful save by wrapping the existing onclick rather than replacing it.

New i18n keys (en/de/fr/it): admDiscard, admOneChange, admNChanges.

Verification

node --check on all script blocks; four anchored splices at count=1; asserted the wiring function, snapshot calls and .setbox references are present. Operator check: Administration → Sharing / Storage → Limits / Basic settings — toggle something, confirm the bar appears with the right count, Discard reverts, Save persists and clears the bar.

Deliberately deferred (added to the running list on #311)

  • The ten setting descriptions are English-only. They're new copy; inventing four translations would be worse than shipping one honest string. Needs a translation pass (new item 9).
  • The .sdesc map covers the settings I could name with confidence; fields not in it simply render without a description rather than getting invented copy.
  • The mockup's Basic settings moves default share expiry out to Sharing with a pointer panel. Not done here: that's a relocation of a real control, and it belongs with a decision about the Basic topic's scope rather than a styling slice (new item 10).
Admin redesign, slice B of four. ## The problem The twelve topics are the old Settings **dialog's** fields relocated into page slots by `SET_SLOTS`, so they inherited dialog habits: uppercase `.field` labels with `.06em` tracking, label-above-input stacking, bare checkboxes, and a right-aligned Save that is always present. ## Approach — presentation only, no relocation rewrite I kept `SET_SLOTS` and every field id, value binding and save handler exactly as they are. Two things happen after the existing move: each field gains a description, and each run of fields gets wrapped in a `.setbox`. Everything else is CSS **scoped to `#adminview`**, so the settings dialog and every other `.field` / `.dlgactions` on the page are untouched. That was the smaller and far less fragile of the two options. - **Row pattern**: name + explanation on the left, control on the right, via explicit grid placement — necessary because a `.check` row puts the `<input>` *before* its `<label>` in source, so auto-placement would seat the description in the wrong column. - **Switches**: `appearance:none` on the existing checkbox, styled as a 38×22 switch with a `::after` knob. No markup or JS change, so every existing binding keeps working; honours `prefers-reduced-motion`. - **Segmented tabs** replacing the underline tabs, matching the list/grid toggle. - **Save bar**: `.dlgactions` containing an `adm-save-*` button becomes sticky, tinted, and **hidden until something changed** — with a live count ("1 unsaved change" / "N unsaved changes") and a **Discard** that restores the snapshot. Selected with `:has()` so the Updates topic's non-save `.dlgactions` (the "Check for updates" button) is unaffected. - **Dirty tracking**: `admSnapshot()` captures the baseline right after `loadSettingsValues()` — i.e. whatever the server just returned — and re-captures after a successful save by wrapping the existing `onclick` rather than replacing it. New i18n keys (en/de/fr/it): admDiscard, admOneChange, admNChanges. ## Verification `node --check` on all script blocks; four anchored splices at count=1; asserted the wiring function, snapshot calls and `.setbox` references are present. Operator check: Administration → Sharing / Storage → Limits / Basic settings — toggle something, confirm the bar appears with the right count, Discard reverts, Save persists and clears the bar. ## Deliberately deferred (added to the running list on #311) - **The ten setting descriptions are English-only.** They're new copy; inventing four translations would be worse than shipping one honest string. Needs a translation pass (new item 9). - The `.sdesc` map covers the settings I could name with confidence; fields not in it simply render without a description rather than getting invented copy. - The mockup's *Basic settings* moves default share expiry out to Sharing with a pointer panel. Not done here: that's a relocation of a real control, and it belongs with a decision about the Basic topic's scope rather than a styling slice (new item 10).
Cordy merged commit ea87af47e6 into main 2026-08-27 17:01:05 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: Cordy/Cairn#327
No description provided.