Admin: a module card is a status line until you open it
Every module's settings were always spread out, so the page read as a wall of forms. A card now shows what you check at a glance — dot, name, version, description, status readout, the enabled switch, Uninstall — with one button at the bottom right: Configure opens the settings and becomes Apply, which saves without closing them. Clicking the module name opens and closes the settings too (keyboard as well: it is a focusable button). A card stays open across a reload for the rest of the session, so re-checking something after an Apply doesn't fold it away. A module with nothing to configure has no button. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
b9d9e2894a
commit
e7b0ea6ebf
@@ -295,13 +295,16 @@ details.field-group > summary.field-group-title:hover { color: var(--text); }
|
|||||||
}
|
}
|
||||||
.field.switch-field .switch-label { font-size: 13px; }
|
.field.switch-field .switch-label { font-size: 13px; }
|
||||||
|
|
||||||
/* status on the left, Apply on the right */
|
/* the card's one action row: status on the left, Configure / Apply on the right */
|
||||||
.module-actions {
|
.module-actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
margin-top: 4px;
|
margin-top: 10px;
|
||||||
}
|
}
|
||||||
|
.module-name.is-toggle { cursor: pointer; }
|
||||||
|
.module-name.is-toggle:hover,
|
||||||
|
.module-name.is-toggle:focus-visible { color: var(--accent); outline: none; }
|
||||||
.module-actions .admin-status { flex: 1; margin-top: 0; }
|
.module-actions .admin-status { flex: 1; margin-top: 0; }
|
||||||
.module-actions .btn { margin-left: auto; }
|
.module-actions .btn { margin-left: auto; }
|
||||||
|
|
||||||
|
|||||||
+25
-3
@@ -585,15 +585,37 @@ function buildModuleCard(mod) {
|
|||||||
const actions = document.createElement('div');
|
const actions = document.createElement('div');
|
||||||
actions.className = 'module-actions';
|
actions.className = 'module-actions';
|
||||||
const save = document.createElement('button');
|
const save = document.createElement('button');
|
||||||
save.className = 'btn primary';
|
|
||||||
save.textContent = 'Apply';
|
|
||||||
const status = document.createElement('span');
|
const status = document.createElement('span');
|
||||||
status.className = 'admin-status';
|
status.className = 'admin-status';
|
||||||
actions.append(status, save);
|
actions.append(status, save);
|
||||||
form.appendChild(actions);
|
// A card is a status line until someone wants to change something: the
|
||||||
|
// form starts folded, and the row at the bottom holds one button that
|
||||||
|
// does both jobs — "Configure" opens the form and becomes "Apply", which
|
||||||
|
// saves without closing it. The name is the other way in and out.
|
||||||
card.appendChild(form);
|
card.appendChild(form);
|
||||||
|
card.appendChild(actions);
|
||||||
|
const openKey = 'proddash:admin:open:' + mod.id;
|
||||||
|
const setOpen = (open) => {
|
||||||
|
form.hidden = !open;
|
||||||
|
status.hidden = !open;
|
||||||
|
save.textContent = open ? 'Apply' : 'Configure';
|
||||||
|
save.className = open ? 'btn primary' : 'btn';
|
||||||
|
try { sessionStorage.setItem(openKey, open ? '1' : '0'); } catch { /* fine */ }
|
||||||
|
};
|
||||||
|
let wasOpen = false;
|
||||||
|
try { wasOpen = sessionStorage.getItem(openKey) === '1'; } catch { /* fine */ }
|
||||||
|
setOpen(wasOpen);
|
||||||
|
name.classList.add('is-toggle');
|
||||||
|
name.title = 'Show or hide this module\'s settings';
|
||||||
|
name.tabIndex = 0;
|
||||||
|
name.setAttribute('role', 'button');
|
||||||
|
name.addEventListener('click', () => setOpen(form.hidden));
|
||||||
|
name.addEventListener('keydown', (e) => {
|
||||||
|
if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); setOpen(form.hidden); }
|
||||||
|
});
|
||||||
|
|
||||||
save.addEventListener('click', async () => {
|
save.addEventListener('click', async () => {
|
||||||
|
if (form.hidden) return setOpen(true);
|
||||||
const config = {};
|
const config = {};
|
||||||
for (const [key, read] of readers) config[key] = read();
|
for (const [key, read] of readers) config[key] = read();
|
||||||
save.disabled = true;
|
save.disabled = true;
|
||||||
|
|||||||
Reference in New Issue
Block a user