Settings rows on phones: switches level with the name, boxes as form fields

This commit is contained in:
Astra 2026-10-03 14:25:51 +01:00
parent 3c0655b512
commit daa7ec2dbc
2 changed files with 21 additions and 7 deletions

View file

@ -556,12 +556,10 @@ async function notifications() {
// The count row is only worth showing when there's a device besides this one. // The count row is only worth showing when there's a device besides this one.
const total = d.devices.length; const total = d.devices.length;
const others = total - (here ? 1 : 0); const others = total - (here ? 1 : 0);
const row = (label, help, control, cls = "") => `<div class="setting${cls}"> const row = (label, help, control) => `<div class="setting">
<div><b>${label}</b>${help ? `<p>${help}</p>` : ""}</div> <div><b>${label}</b>${help ? `<p>${help}</p>` : ""}</div>
<div class="control">${control}</div> <div class="control">${control}</div>
</div>`; </div>`;
// A row whose control is one switch: on phones the switch goes before the text.
const toggle = (label, help, sw) => row(label, help, sw, " toggle");
let device; let device;
if (!d.available) device = row("This device", "Notifications aren't set up on the panel. Its log says why.", '<span class="tag red">Unavailable</span>'); if (!d.available) device = row("This device", "Notifications aren't set up on the panel. Its log says why.", '<span class="tag red">Unavailable</span>');
@ -584,13 +582,13 @@ async function notifications() {
<section class="panel"> <section class="panel">
<div class="panel-head"><div><h2>Quiet hours</h2><p>Nothing is sent between these times, in your timezone (${esc(browserTimezone())}).</p></div></div> <div class="panel-head"><div><h2>Quiet hours</h2><p>Nothing is sent between these times, in your timezone (${esc(browserTimezone())}).</p></div></div>
${toggle("Quiet hours", "", `<button class="switch" type="button" role="switch" aria-checked="${p.quiet.enabled}" aria-label="Quiet hours" data-notif="quiet" ${dis}></button>`)} ${row("Quiet hours", "", `<button class="switch" type="button" role="switch" aria-checked="${p.quiet.enabled}" aria-label="Quiet hours" data-notif="quiet" ${dis}></button>`)}
${row("From and until", "", `<div class="quiet-times"><input class="input" type="time" id="quiet-start" value="${esc(p.quiet.start)}" aria-label="Quiet from" data-notif-time ${dis}><span class="muted">to</span><input class="input" type="time" id="quiet-end" value="${esc(p.quiet.end)}" aria-label="Quiet until" data-notif-time ${dis}></div>`)} ${row("From and until", "", `<div class="quiet-times"><input class="input" type="time" id="quiet-start" value="${esc(p.quiet.start)}" aria-label="Quiet from" data-notif-time ${dis}><span class="muted">to</span><input class="input" type="time" id="quiet-end" value="${esc(p.quiet.end)}" aria-label="Quiet until" data-notif-time ${dis}></div>`)}
</section> </section>
<section class="panel"> <section class="panel">
<div class="panel-head"><div><h2>What to send</h2><p>For all your devices.</p></div></div> <div class="panel-head"><div><h2>What to send</h2><p>For all your devices.</p></div></div>
${d.types.map((t) => toggle(esc(t.label) + (t.ready ? "" : ' <span class="tag">Coming later</span>'), t.help ? esc(t.help) : "", ${d.types.map((t) => row(esc(t.label) + (t.ready ? "" : ' <span class="tag">Coming later</span>'), t.help ? esc(t.help) : "",
`<button class="switch" type="button" role="switch" aria-checked="${t.ready && p.types.includes(t.key)}" aria-label="${esc(t.label)}" ${t.ready ? `data-ntype="${esc(t.key)}" ${dis}` : "disabled"}></button>`)).join("")} `<button class="switch" type="button" role="switch" aria-checked="${t.ready && p.types.includes(t.key)}" aria-label="${esc(t.label)}" ${t.ready ? `data-ntype="${esc(t.key)}" ${dis}` : "disabled"}></button>`)).join("")}
</section> </section>
</div>`; </div>`;

View file

@ -846,8 +846,24 @@ dialog::backdrop { background: rgba(5, 6, 9, 0.65); }
.live-map { font-size: 42px; } .live-map { font-size: 42px; }
.setting { grid-template-columns: 1fr; gap: 10px; } .setting { grid-template-columns: 1fr; gap: 10px; }
.setting .control { justify-content: flex-start; } .setting .control { justify-content: flex-start; }
.setting.toggle { grid-template-columns: auto minmax(0, 1fr); gap: 14px; } /* A setting that's one switch: the switch goes first, level with the name, and the description
.setting.toggle .control { order: -1; } and cvar stay under the name. */
.setting:has(> .control > .switch:only-child) { grid-template-columns: auto minmax(0, 1fr); align-items: start; gap: 14px; }
.setting:has(> .control > .switch:only-child) > .control { order: -1; margin-top: 1px; }
/* A text box, number or dropdown reads as a form field: the name with the cvar on its right, the
box, then the description as a hint. Rows with buttons or status tags keep the plain stack. */
.setting:has(> .control > :is(input, select, .quiet-times)) {
grid-template-columns: minmax(0, 1fr) auto;
grid-template-areas: "label key" "control control" "help help";
gap: 0;
align-items: baseline;
}
.setting:has(> .control > :is(input, select, .quiet-times)) > div:first-child { display: contents; }
.setting:has(> .control > :is(input, select, .quiet-times)) b { grid-area: label; }
.setting:has(> .control > :is(input, select, .quiet-times)) code { grid-area: key; margin-left: 12px; font-size: 12px; }
.setting:has(> .control > :is(input, select, .quiet-times)) > .control { grid-area: control; margin: 8px 0 6px; }
.setting:has(> .control > :is(input, select, .quiet-times)) p { grid-area: help; margin: 0; font-size: 13px; }
.quiet-times { flex: 1; }
.quiet-times .input { flex: 1; width: auto; } .quiet-times .input { flex: 1; width: auto; }
.action-grid { grid-template-columns: repeat(2, 1fr); } .action-grid { grid-template-columns: repeat(2, 1fr); }
.hide-sm { display: none; } .hide-sm { display: none; }