Skip to content

Commit 8240648

Browse files
committed
better disabled css
1 parent 65206af commit 8240648

3 files changed

Lines changed: 75 additions & 1 deletion

File tree

‎src/ui/shell.ts‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,10 @@ const MARKUP = `
175175
stroke="currentColor" stroke-width="1.3" stroke-linejoin="round" />
176176
</svg>
177177
</button>
178-
<span class="select-pill">
178+
<!-- The pill, not the select, carries the locked look and its tooltip: a
179+
disabled select takes no pointer events, so a title on it would never
180+
show. -->
181+
<span class="select-pill" id="presetPill">
179182
<select id="presetSelect" aria-label="Project source"></select>
180183
</span>
181184
</div>
@@ -306,6 +309,7 @@ export interface ShellElements {
306309
exportBtn: HTMLButtonElement;
307310
restartBtn: HTMLButtonElement;
308311
presetSelect: HTMLSelectElement;
312+
presetPill: HTMLElement;
309313
openFolderBtn: HTMLButtonElement;
310314
tree: HTMLElement;
311315
tabs: HTMLElement;
@@ -370,6 +374,7 @@ export function buildShell(root: Element): ShellElements {
370374
exportBtn: pick("exportBtn"),
371375
restartBtn: pick("restartBtn"),
372376
presetSelect: pick("presetSelect"),
377+
presetPill: pick("presetPill"),
373378
openFolderBtn: pick("openFolderBtn"),
374379
tree: pick("tree"),
375380
tabs: pick("tabs"),

‎src/ui/styles.css‎

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -490,6 +490,59 @@ select {
490490
pointer-events: none;
491491
}
492492

493+
/* Locked while a program holds the worker, because switching source reloads the page.
494+
The native disabled `<select>` is a text shade nobody notices, so the pill goes flat
495+
and grey with a padlock where the chevron was — and the pill takes the cursor and the
496+
`title`, since the disabled control inside it gets no pointer events. */
497+
.select-pill[data-locked="true"] {
498+
cursor: not-allowed;
499+
}
500+
501+
/* `--ink-mid` rather than the fainter `--ink-soft`, because which project is loaded stays
502+
worth reading while it runs — the flat fill and the padlock carry the unavailability. */
503+
.select-pill select:disabled {
504+
border-color: var(--line-soft);
505+
background: var(--track);
506+
color: var(--ink-mid);
507+
cursor: not-allowed;
508+
}
509+
510+
.select-pill select:disabled:hover {
511+
border-color: var(--line-soft);
512+
}
513+
514+
/* The padlock, drawn from the pill's two pseudo-elements — `::after` is the body (the
515+
chevron's borders traded for a fill), `::before` the shackle sitting on top of it.
516+
Both keep the chevron's footprint, so locking does not resize the pill.
517+
518+
`box-sizing` is set here because the `*` reset does not reach pseudo-elements: on
519+
content-box the shackle's borders would sit outside its width and shift it off the
520+
body's centre. With both boxes measured the same way, the two `right` offsets put
521+
their centres on the same 12.5px. */
522+
.select-pill[data-locked="true"]::after {
523+
box-sizing: border-box;
524+
width: 9px;
525+
height: 7px;
526+
border: none;
527+
border-radius: 1.5px;
528+
background: var(--ink-mid);
529+
transform: translateY(1.5px);
530+
}
531+
532+
.select-pill[data-locked="true"]::before {
533+
content: "";
534+
box-sizing: border-box;
535+
position: absolute;
536+
right: 10px;
537+
width: 5px;
538+
height: 5px;
539+
border: 1.4px solid var(--ink-mid);
540+
border-bottom: none;
541+
border-radius: 3px 3px 0 0;
542+
transform: translateY(-4px);
543+
pointer-events: none;
544+
}
545+
493546
/* --------------------------------------------------------- file tree */
494547

495548
.tree {

‎src/workspace.ts‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,9 @@ async function boot(
264264

265265
renderSources(el, manifest.templates, project, rememberedFolder, idleLabel);
266266
if (!localDirSupported()) {
267+
// Marked as well as disabled so `setBusy` can tell a browser that will never do this
268+
// from a run that is only holding it for now, and leave the former alone.
269+
el.openFolderBtn.dataset.unsupported = "true";
267270
el.openFolderBtn.disabled = true;
268271
el.openFolderBtn.title =
269272
"this browser cannot open a folder from your device — Chrome, Edge and other Chromium browsers can";
@@ -803,6 +806,19 @@ function setBusy(el: ShellElements, busy: boolean, idleLabel: string) {
803806
el.exportBtn.disabled = busy;
804807
el.restartBtn.disabled = busy;
805808
el.presetSelect.disabled = busy;
809+
// Says *why* it is unavailable, which the greying alone cannot: a switch reloads the page,
810+
// so it has to wait for whatever holds the worker. See `.select-pill[data-locked]`.
811+
el.presetPill.dataset.locked = busy ? "true" : "false";
812+
if (busy) {
813+
el.presetPill.title = "Stop what is running first — switching project reloads the page";
814+
} else {
815+
el.presetPill.removeAttribute("title");
816+
}
817+
// Opening a folder swaps the project the same way the select does, so it waits for the same
818+
// reason — `swapSource` already declines while busy, and this stops the click that goes
819+
// nowhere. The button keeps its own title: a disabled one shows no tooltip in every browser,
820+
// which is why the reason sits on the pill beside it.
821+
el.openFolderBtn.disabled = busy || el.openFolderBtn.dataset.unsupported === "true";
806822
}
807823

808824
function showPreview(el: ShellElements, port: number, peerToken?: string) {

0 commit comments

Comments
 (0)