staffa 0.15.0 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +99 -271
- package/dist/components/autocomplete.js +4 -5
- package/dist/components/box.js +11 -21
- package/dist/components/button.d.ts +20 -5
- package/dist/components/button.js +55 -47
- package/dist/components/buttonChooser.js +1 -3
- package/dist/components/checkbox.js +1 -2
- package/dist/components/dialog.d.ts +9 -2
- package/dist/components/dialog.js +28 -32
- package/dist/components/field.d.ts +5 -8
- package/dist/components/field.js +4 -6
- package/dist/components/form.d.ts +5 -7
- package/dist/components/form.js +6 -9
- package/dist/components/keyhelp.d.ts +22 -0
- package/dist/components/keyhelp.js +91 -0
- package/dist/components/main.js +188 -271
- package/dist/components/menu.d.ts +48 -10
- package/dist/components/menu.js +228 -147
- package/dist/components/panels.d.ts +151 -239
- package/dist/components/panels.js +331 -558
- package/dist/components/select.js +1 -3
- package/dist/components/tabs.d.ts +10 -13
- package/dist/components/tabs.js +38 -58
- package/dist/components/textline.d.ts +3 -5
- package/dist/components/textline.js +3 -5
- package/dist/components/toast.d.ts +1 -3
- package/dist/components/toast.js +2 -3
- package/dist/components/tooltip.d.ts +4 -5
- package/dist/components/tooltip.js +13 -11
- package/dist/core.d.ts +17 -24
- package/dist/core.js +13 -18
- package/dist/icons-helpers.d.ts +3 -3
- package/dist/icons-helpers.js +6 -11
- package/dist/index.d.ts +3 -1
- package/dist/index.js +5 -4
- package/dist/keys.d.ts +92 -0
- package/dist/keys.js +279 -0
- package/dist/staffa.esm.js +1 -1
- package/dist/theme.d.ts +4 -10
- package/dist/theme.js +58 -123
- package/package.json +2 -2
- package/skill/ButtonOptions.md +12 -0
- package/skill/DialogOptions.md +11 -2
- package/skill/FieldOptions.md +3 -5
- package/skill/IconButtonOptions.md +8 -0
- package/skill/MenuItem.md +32 -3
- package/skill/Panel.md +11 -3
- package/skill/SKILL.md +161 -294
- package/skill/addTooltip.md +4 -5
- package/skill/bindKey.md +51 -0
- package/skill/box.md +1 -1
- package/skill/form.md +5 -7
- package/skill/formatKey.md +21 -0
- package/skill/iconButton.md +4 -5
- package/skill/scrollStrip.md +7 -9
- package/skill/showFloatingMenu.md +2 -2
- package/skill/showKeyHelp.md +17 -0
- package/skill/tabs.md +3 -4
- package/skill/textline.md +3 -5
- package/src/components/autocomplete.ts +4 -5
- package/src/components/box.ts +11 -21
- package/src/components/button.ts +70 -47
- package/src/components/buttonChooser.ts +1 -3
- package/src/components/checkbox.ts +1 -2
- package/src/components/dialog.ts +38 -34
- package/src/components/field.ts +7 -11
- package/src/components/form.ts +6 -9
- package/src/components/keyhelp.ts +96 -0
- package/src/components/main.ts +191 -270
- package/src/components/menu.ts +258 -150
- package/src/components/panels.ts +378 -623
- package/src/components/select.ts +1 -3
- package/src/components/tabs.ts +38 -58
- package/src/components/textline.ts +3 -5
- package/src/components/toast.ts +3 -6
- package/src/components/tooltip.ts +12 -11
- package/src/core.ts +17 -24
- package/src/icons-helpers.ts +6 -11
- package/src/index.ts +5 -4
- package/src/keys.ts +300 -0
- package/src/theme.ts +58 -123
- package/skill/Attributes.md +0 -10
|
@@ -3,31 +3,20 @@ import { type Slot } from "../core.js";
|
|
|
3
3
|
/**
|
|
4
4
|
* Routed, multi-column panel navigation for {@link main}.
|
|
5
5
|
*
|
|
6
|
-
* Each route draws one screen
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* ending at the current one — on a phone that is one at a time, on a wider
|
|
10
|
-
* screen the panels that would have covered each other sit side by side
|
|
11
|
-
* instead. The app's own code is the same either way.
|
|
6
|
+
* Each route draws one screen, called a *panel*. The open panels form a
|
|
7
|
+
* **stack** whose last panel is the **current** one: the panel the URL names,
|
|
8
|
+
* and the rightmost column on screen. As many as fit are shown, ending there.
|
|
12
9
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* Two kinds of panel survive that: the ones the user pinned, which ride along,
|
|
20
|
-
* and the ones holding unsaved work, which no navigation ever tears down.
|
|
21
|
-
* Those wait parked out of sight past the rightmost column, which is the only
|
|
22
|
-
* way a panel ever sits *after* the current one. Escape closes the current
|
|
23
|
-
* panel — or just steps left, when it holds unsaved work or panels sit parked
|
|
24
|
-
* beyond it.
|
|
10
|
+
* A navigation's target becomes the top of the stack, and the same path is
|
|
11
|
+
* never in it twice: opening a panel closes everything after the one it came
|
|
12
|
+
* from, while a plain link to an already-open panel returns to its place. Two
|
|
13
|
+
* kinds survive that — pinned panels, which ride along, and unsaved ones, which
|
|
14
|
+
* nothing tears down. Both wait parked past the rightmost column, the only way
|
|
15
|
+
* a panel ever sits *after* the current one.
|
|
25
16
|
*
|
|
26
|
-
* Navigation runs through `aberdeen/route`: the URL holds the current panel
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
* So back and forward step through whole arrangements of columns, and a reload
|
|
30
|
-
* (or a shared link) brings the same columns back.
|
|
17
|
+
* Navigation runs through `aberdeen/route`: the URL holds the current panel and
|
|
18
|
+
* the rest of the arrangement is stored beside it in the history entry, so back
|
|
19
|
+
* and forward step through whole arrangements of columns.
|
|
31
20
|
*/
|
|
32
21
|
/** Flattens an intersection into a single object type, so hovers read nicely. */
|
|
33
22
|
type Prettify<T> = {
|
|
@@ -111,6 +100,10 @@ export interface Panel<P = Record<string, string | number | string[]>> {
|
|
|
111
100
|
* The params matched from this panel's path, typed per its route key:
|
|
112
101
|
* `[x]` is a `string`, `[x=integer]` a `number`, `[...x]` a `string`.
|
|
113
102
|
* Read-only.
|
|
103
|
+
*
|
|
104
|
+
* Single-segment params are percent-decoded; `[...x]` is not, since decoding
|
|
105
|
+
* it would make an encoded slash indistinguishable from a separator. Split it
|
|
106
|
+
* yourself: `x.split("/").map(decodeURIComponent)`.
|
|
114
107
|
*/
|
|
115
108
|
readonly params: P;
|
|
116
109
|
/**
|
|
@@ -168,9 +161,9 @@ export interface Panel<P = Record<string, string | number | string[]>> {
|
|
|
168
161
|
* The widest this panel can usefully be — a ceiling the shell never
|
|
169
162
|
* exceeds, so the draw function never has to look right past it. It is
|
|
170
163
|
* counted in the shell's *columns*: the content area divides into the
|
|
171
|
-
*
|
|
172
|
-
*
|
|
173
|
-
*
|
|
164
|
+
* fewest whole number of columns of at most 540px each (two columns of 540
|
|
165
|
+
* in a 1080px area, three of ~507 in 1520px), and a column is never
|
|
166
|
+
* narrower than 360 — where the area holds just one, a small centres in it
|
|
174
167
|
* rather than stretching. So an ask never exceeds its column count × 540:
|
|
175
168
|
*
|
|
176
169
|
* - `"small"` — one column, never above 540px: lists, detail forms.
|
|
@@ -306,10 +299,9 @@ export interface PanelStackOptions {
|
|
|
306
299
|
/** The shell's own title, used as the suffix of `document.title`. */
|
|
307
300
|
title?: unknown;
|
|
308
301
|
/**
|
|
309
|
-
* The shell's live narrow flag (see `main()`),
|
|
310
|
-
* chrome
|
|
311
|
-
* than measured again
|
|
312
|
-
* which regime they are in.
|
|
302
|
+
* The shell's live narrow flag (see `main()`), deciding whether a panel's
|
|
303
|
+
* chrome sits in its own column or is promoted into the top bar. Shared
|
|
304
|
+
* rather than measured again, so the two can't disagree about the regime.
|
|
313
305
|
*/
|
|
314
306
|
$shell: {
|
|
315
307
|
narrow: boolean;
|
|
@@ -418,10 +410,9 @@ export interface PanelStack {
|
|
|
418
410
|
*/
|
|
419
411
|
export declare class PanelStackController implements PanelStack {
|
|
420
412
|
/**
|
|
421
|
-
* Kept out of Aberdeen's proxy wrapping:
|
|
422
|
-
*
|
|
423
|
-
*
|
|
424
|
-
* from `$state` and the panels, which are proxies in their own right.
|
|
413
|
+
* Kept out of Aberdeen's proxy wrapping: a class instance holding DOM nodes,
|
|
414
|
+
* timers and route handlers, riding inside every {@link Panel.stack}. Its
|
|
415
|
+
* reactivity comes from `$state` and the panels, which are proxies already.
|
|
425
416
|
*/
|
|
426
417
|
readonly [OPAQUE] = true;
|
|
427
418
|
private compiled;
|
|
@@ -429,35 +420,24 @@ export declare class PanelStackController implements PanelStack {
|
|
|
429
420
|
private ancestors;
|
|
430
421
|
private opts;
|
|
431
422
|
/**
|
|
432
|
-
* The live stack, oldest first (closing panels
|
|
433
|
-
*
|
|
434
|
-
*
|
|
435
|
-
*
|
|
436
|
-
*
|
|
437
|
-
*
|
|
438
|
-
*
|
|
439
|
-
*
|
|
440
|
-
* stack
|
|
441
|
-
*
|
|
442
|
-
* One rule makes this safe to touch from anywhere: **queries subscribe,
|
|
443
|
-
* commands peek**. The getters below are the queries. Every navigation
|
|
444
|
-
* entry point (`navigate`, `closePath`, `back`, …) wraps itself in
|
|
445
|
-
* `A.peek`, so an app calling one from inside a reactive scope (a
|
|
446
|
-
* redirect in a route handler, say) can't subscribe that scope to the
|
|
447
|
-
* very stack it is changing — and everything those commands call through
|
|
448
|
-
* to, `propose` and `commit` included, inherits the same guarantee and
|
|
449
|
-
* reads the stack plainly.
|
|
423
|
+
* The live stack, oldest first (closing panels have left it), and which panel
|
|
424
|
+
* is current — the one reactive fact about the stack's *shape*. Each commit
|
|
425
|
+
* publishes the next shape by assigning a fresh `live` array; the entries are
|
|
426
|
+
* opaque, so a panel renaming itself doesn't redraw the stack.
|
|
427
|
+
*
|
|
428
|
+
* The invariant that makes this safe to touch from anywhere: **queries
|
|
429
|
+
* subscribe, commands peek**. Every navigation entry point (`navigate`,
|
|
430
|
+
* `closePath`, `back`, …) wraps itself in `A.peek`, so an app calling one
|
|
431
|
+
* from a reactive scope can't subscribe that scope to the stack it is
|
|
432
|
+
* changing; `propose` and `commit` inherit that and read the stack plainly.
|
|
450
433
|
*/
|
|
451
434
|
private $state;
|
|
452
435
|
/**
|
|
453
|
-
* The open panels again, keyed by path — the shape
|
|
454
|
-
* `drawColumns`' `onEach` mounts
|
|
455
|
-
*
|
|
456
|
-
*
|
|
457
|
-
*
|
|
458
|
-
* and a splice renumbers every index after it, redrawing them all.) A
|
|
459
|
-
* key's value is its entry, by reference, and is never reassigned, so a
|
|
460
|
-
* panel only ever redraws wholesale when its path closes.
|
|
436
|
+
* The open panels again, keyed by path — the shape the DOM consumes.
|
|
437
|
+
* `drawColumns`' `onEach` mounts by key, so splicing a panel out of the
|
|
438
|
+
* middle touches exactly one key and the retained columns keep their scroll
|
|
439
|
+
* positions and half-typed forms. (Iterating `live` would key by array index,
|
|
440
|
+
* which a splice renumbers, redrawing every panel after it.)
|
|
461
441
|
*/
|
|
462
442
|
private $open;
|
|
463
443
|
/** Feeds {@link PanelEntry.order}: one shared counter, so keys never tie. */
|
|
@@ -483,24 +463,18 @@ export declare class PanelStackController implements PanelStack {
|
|
|
483
463
|
private matches;
|
|
484
464
|
/**
|
|
485
465
|
* The stack for origin-less navigation: a cold deep link, a nav item, a
|
|
486
|
-
* `route.go()` — anything
|
|
487
|
-
*
|
|
488
|
-
*
|
|
489
|
-
*
|
|
490
|
-
*
|
|
491
|
-
*
|
|
492
|
-
* has no opinion — every prefix of the path is probed against the route table
|
|
493
|
-
* and the matching ones become the stack. Either way, a path with no route is
|
|
494
|
-
* skipped rather than opened as a "not found" column, so an app that doesn't
|
|
495
|
-
* want one screen stacked under another simply doesn't route it. The path
|
|
496
|
-
* itself always ends the derived stack, matched or not.
|
|
466
|
+
* `route.go()` — anything with no panel to build on and no snapshot to
|
|
467
|
+
* restore. {@link PanelStackOptions.ancestors} gets first say; failing that,
|
|
468
|
+
* every prefix of the path is probed against the route table. A path with no
|
|
469
|
+
* route is skipped rather than opened as a "not found" column, so not routing
|
|
470
|
+
* a screen keeps it from appearing under another. The path itself always ends
|
|
471
|
+
* the derived stack, matched or not.
|
|
497
472
|
*/
|
|
498
473
|
private deriveStack;
|
|
499
474
|
/**
|
|
500
|
-
* Ask the `ancestors` table what belongs beneath `path`. The first
|
|
501
|
-
*
|
|
502
|
-
*
|
|
503
|
-
* path to the prefix derivation just as an unlisted one is.
|
|
475
|
+
* Ask the `ancestors` table what belongs beneath `path`. The first matching
|
|
476
|
+
* key answers, with its own matched params; `undefined` means "no opinion",
|
|
477
|
+
* leaving the path to the prefix derivation as an unlisted one would be.
|
|
504
478
|
*/
|
|
505
479
|
private askAncestors;
|
|
506
480
|
/** Every prefix of `path` that has a route, shallowest first. */
|
|
@@ -514,70 +488,57 @@ export declare class PanelStackController implements PanelStack {
|
|
|
514
488
|
/** Whether the panel open at `path` (if any) holds unsaved work. */
|
|
515
489
|
private unsavedAt;
|
|
516
490
|
/**
|
|
517
|
-
* The arrangement a route implies: its snapshot around its path, or —
|
|
518
|
-
*
|
|
519
|
-
* any pinned panels carried along beneath it.
|
|
491
|
+
* The arrangement a route implies: its snapshot around its path, or — without
|
|
492
|
+
* one — derived, the new panel current at the end with pinned panels beneath.
|
|
520
493
|
*
|
|
521
|
-
* The snapshot reads subscribe
|
|
522
|
-
*
|
|
523
|
-
*
|
|
524
|
-
* and subscribing to it would re-run the observer once per commit.
|
|
494
|
+
* The snapshot reads subscribe (they are the URL's). The derivation is peeked:
|
|
495
|
+
* it reads the live stack for its pins, which the route observer rewrites, so
|
|
496
|
+
* subscribing would re-run that observer once per commit.
|
|
525
497
|
*/
|
|
526
498
|
private targetFor;
|
|
527
499
|
/** The arrangement the current history entry asks for. Subscribes to path + snapshot. */
|
|
528
500
|
private computeTarget;
|
|
529
501
|
private paths;
|
|
530
502
|
/**
|
|
531
|
-
* Adopt an arrangement proposed by the URL
|
|
532
|
-
*
|
|
533
|
-
*
|
|
534
|
-
*
|
|
535
|
-
*
|
|
536
|
-
* deliberately not written into history entries: the work they protect
|
|
537
|
-
* lives in the page's DOM, which a reload clears anyway.)
|
|
503
|
+
* Adopt an arrangement proposed by the URL, after repairing it: a panel holding
|
|
504
|
+
* unsaved work is never torn down by any navigation — including a back to an
|
|
505
|
+
* entry from before it existed — so whatever the target drops, it stays, parked
|
|
506
|
+
* after the current panel. Parked panels are deliberately kept out of history
|
|
507
|
+
* entries: the work they protect lives in DOM a reload clears anyway.
|
|
538
508
|
*/
|
|
539
509
|
private propose;
|
|
540
510
|
/**
|
|
541
511
|
* Apply a target arrangement: unmount what's gone, mount what's new, animate
|
|
542
512
|
* the difference.
|
|
543
513
|
*
|
|
544
|
-
* Reconciliation is BY PATH
|
|
545
|
-
*
|
|
546
|
-
*
|
|
547
|
-
*
|
|
548
|
-
* remount every one of them, throwing away exactly the scroll and form state
|
|
549
|
-
* rule 5 promises to keep.
|
|
514
|
+
* Reconciliation is BY PATH, not by index: a panel in both stacks stays mounted
|
|
515
|
+
* even if its index shifted, which is what lets one be spliced out of the middle
|
|
516
|
+
* without disturbing the columns above it. A common-prefix diff would remount
|
|
517
|
+
* them all, throwing away their scroll and form state.
|
|
550
518
|
*/
|
|
551
519
|
private commit;
|
|
552
520
|
private createEntry;
|
|
553
521
|
/**
|
|
554
|
-
* Take a panel out of the shell. The *scope* goes now
|
|
555
|
-
* tick, so
|
|
556
|
-
*
|
|
557
|
-
*
|
|
558
|
-
* is what the `destroy=` hook in `drawPanel` is for: Aberdeen hands the
|
|
559
|
-
* element to {@link playExit} instead of removing it.
|
|
522
|
+
* Take a panel out of the shell. The *scope* goes now — its `A.clean` hooks run
|
|
523
|
+
* this tick, so subscriptions, timers and portals stop at the close rather than
|
|
524
|
+
* at the end of the animation. Only the element lingers to play that animation,
|
|
525
|
+
* which is what `drawPanel`'s `destroy=` hook hands to {@link playExit}.
|
|
560
526
|
*/
|
|
561
527
|
private beginClose;
|
|
562
528
|
/**
|
|
563
|
-
* A closed panel's send-off, run by Aberdeen once
|
|
564
|
-
*
|
|
565
|
-
*
|
|
566
|
-
*
|
|
567
|
-
*
|
|
568
|
-
* then vanish. The timeout is just a fallback for when no `transitionend` is
|
|
569
|
-
* coming at all (transitions off, or an element that never got placed).
|
|
529
|
+
* A closed panel's send-off, run by Aberdeen once its scope is gone: it fades
|
|
530
|
+
* where it stands, inert, and leaves the DOM on `transitionend`. A fixed timer
|
|
531
|
+
* would race the transition and pull the element a frame early, making the
|
|
532
|
+
* panel appear to fade half-way and vanish; the timeout is only a fallback for
|
|
533
|
+
* when no `transitionend` is coming (transitions off, element never placed).
|
|
570
534
|
*/
|
|
571
535
|
private playExit;
|
|
572
536
|
/**
|
|
573
537
|
* The arrangement navigation works from: the one we're on the way to while a
|
|
574
|
-
* change is still settling,
|
|
575
|
-
*
|
|
576
|
-
*
|
|
577
|
-
*
|
|
578
|
-
* app-registered route guard may be async. Working from the committed
|
|
579
|
-
* arrangement in that window would make a second Escape aim at the panel the
|
|
580
|
-
* first one is already taking away — so two quick Escapes would peel one panel.
|
|
538
|
+
* change is still settling, the one on screen otherwise. That window is common
|
|
539
|
+
* (every `route.back()` waits for a `popstate`), and working from the committed
|
|
540
|
+
* arrangement inside it would aim a second Escape at the panel the first is
|
|
541
|
+
* already taking away — two quick Escapes would peel one panel.
|
|
581
542
|
*/
|
|
582
543
|
private intended;
|
|
583
544
|
/** The history `state` describing `arr` — exactly what `targetFor` reads back. */
|
|
@@ -585,96 +546,67 @@ export declare class PanelStackController implements PanelStack {
|
|
|
585
546
|
/** The paths of the pinned panels — the pin list history entries persist. */
|
|
586
547
|
private pinnedPaths;
|
|
587
548
|
/**
|
|
588
|
-
* Put a navigation to the router, or — while one is still settling — behind
|
|
589
|
-
*
|
|
590
|
-
*
|
|
591
|
-
*
|
|
592
|
-
*
|
|
593
|
-
* A refusal empties the queue instead of running it: a navigation can still
|
|
594
|
-
* fail to land — an app-registered route guard vetoes it, or another one
|
|
595
|
-
* supersedes it — and what was queued behind it was worked out against the
|
|
596
|
-
* arrangement it would have produced.
|
|
549
|
+
* Put a navigation to the router, or — while one is still settling — behind the
|
|
550
|
+
* one that is. Only the newest waits; the one it displaces resolves `false`.
|
|
551
|
+
* A refusal empties the queue rather than running it, since what was queued
|
|
552
|
+
* was worked out against the arrangement the refused one would have produced.
|
|
597
553
|
*/
|
|
598
554
|
private issue;
|
|
599
555
|
private start;
|
|
600
556
|
/**
|
|
601
557
|
* Make the stack's `index`th panel current without closing anything, leaving
|
|
602
|
-
* the panels right of it parked out of sight
|
|
603
|
-
*
|
|
604
|
-
* Only ever a step around a panel that refuses to close — nothing else is
|
|
605
|
-
* left sitting after the current one — so this is Escape's way past an
|
|
606
|
-
* unsaved panel, and the way back to one. It is a history entry, so the
|
|
607
|
-
* browser's back button returns the focus to where it was.
|
|
558
|
+
* the panels right of it parked out of sight — Escape's way past an unsaved
|
|
559
|
+
* panel, and back to one. A history entry, so back returns the focus.
|
|
608
560
|
*/
|
|
609
561
|
private focusAt;
|
|
610
562
|
/**
|
|
611
|
-
* One step back along the stack — what Escape does (`main()` calls this;
|
|
612
|
-
*
|
|
613
|
-
*
|
|
614
|
-
*
|
|
615
|
-
* simply moves to the panel on its left.
|
|
616
|
-
* Resolves `false` at the stack's start, where there is no left to go.
|
|
563
|
+
* One step back along the stack — what Escape does (`main()` calls this; it is
|
|
564
|
+
* not {@link PanelStack} API). Normally that closes the current panel; when it
|
|
565
|
+
* holds {@link Panel.unsaved} work, or panels sit parked beyond it, the focus
|
|
566
|
+
* moves left instead. Resolves `false` at the stack's start.
|
|
617
567
|
*/
|
|
618
568
|
back(): Promise<boolean>;
|
|
619
569
|
/**
|
|
620
570
|
* Close whichever panel is open at `path`, current or not — what
|
|
621
571
|
* {@link Panel.close}, {@link PanelStack.closePanel} and the crumb menu's
|
|
622
|
-
* Close come down to. `false` when that path isn't open, is the stack's
|
|
623
|
-
* only panel, or holds {@link Panel.unsaved} work
|
|
624
|
-
*
|
|
625
|
-
*
|
|
626
|
-
*
|
|
627
|
-
*
|
|
628
|
-
*
|
|
629
|
-
*
|
|
630
|
-
*
|
|
631
|
-
* close is a *splice*: the columns around the closed one keep their place
|
|
632
|
-
* and state (the commit reconciles by path). That still gets its own
|
|
633
|
-
* history entry, so the browser's back button restores the closed column
|
|
634
|
-
* like any other arrangement — which is why it goes through `route.go` here
|
|
635
|
-
* rather than through `navigate()`, whose "link to an open panel" check
|
|
636
|
-
* would turn it into a focus move.
|
|
572
|
+
* Close all come down to. `false` when that path isn't open, is the stack's
|
|
573
|
+
* only panel, or holds {@link Panel.unsaved} work.
|
|
574
|
+
*
|
|
575
|
+
* Closing the current panel at the stack's end pops back through history to
|
|
576
|
+
* the entry beneath it (restoring its scroll and search state); the
|
|
577
|
+
* arrangement is part of the match, so an entry where the closing panel was
|
|
578
|
+
* merely parked won't do. Every other close is a *splice*, which still earns
|
|
579
|
+
* its own history entry — hence `route.go` here rather than `navigate()`,
|
|
580
|
+
* whose "link to an open panel" check would turn it into a focus move.
|
|
637
581
|
*/
|
|
638
582
|
private closePath;
|
|
639
583
|
/**
|
|
640
584
|
* Navigate to `href` — the one implementation behind a link click,
|
|
641
|
-
* {@link Panel.open} and the stack's own methods, so none
|
|
642
|
-
*
|
|
643
|
-
*
|
|
644
|
-
*
|
|
645
|
-
*
|
|
646
|
-
*
|
|
647
|
-
*
|
|
648
|
-
*
|
|
649
|
-
*
|
|
650
|
-
*
|
|
651
|
-
*
|
|
652
|
-
*
|
|
653
|
-
*
|
|
654
|
-
* `"open"` keeps nothing — the target arrives with its own stack, the way
|
|
655
|
-
* a nav item's link does. Absent, it is the shell's `linkNavigation`
|
|
656
|
-
* default, like a link without the attribute. A target that is already
|
|
657
|
-
* open is returned to by a push, and *moved* — alive, state intact — by
|
|
658
|
-
* the other two: the stack never holds a path twice.
|
|
659
|
-
*
|
|
660
|
-
* Resolves the way every {@link PanelStack} method does: `true` once the
|
|
661
|
-
* navigation lands, `false` when it doesn't (already there counts as
|
|
662
|
-
* landed).
|
|
585
|
+
* {@link Panel.open} and the stack's own methods, so none can drift.
|
|
586
|
+
*
|
|
587
|
+
* `from` is the panel the navigation starts from, absent when it has none (a
|
|
588
|
+
* nav item), in which case the stack is derived or taken from `beneath`.
|
|
589
|
+
*
|
|
590
|
+
* `how` is the link's `data-panel` attribute, picking how much of `from`'s
|
|
591
|
+
* context the target keeps: a push (the default, and the fallback for
|
|
592
|
+
* unrecognised values) builds on `from`, `"replace"` keeps only what is
|
|
593
|
+
* beneath it, `"open"` keeps nothing. A target that is already open is
|
|
594
|
+
* returned to by a push and *moved* — alive — by the other two, since the
|
|
595
|
+
* stack never holds a path twice.
|
|
596
|
+
*
|
|
597
|
+
* Resolves `true` once the navigation lands (already being there counts).
|
|
663
598
|
*/
|
|
664
599
|
private navigate;
|
|
665
600
|
/** Programmatic push/replace, with the current panel as the implied origin. */
|
|
666
601
|
private pushPath;
|
|
667
602
|
/**
|
|
668
|
-
* Link handling through `route.interceptLinks()`, whose
|
|
669
|
-
*
|
|
670
|
-
*
|
|
671
|
-
* close guards run in `checkChange` when our navigation reaches the router.
|
|
603
|
+
* Link handling through `route.interceptLinks()`, whose hook hands us the
|
|
604
|
+
* anchor so we can decide what the click means. The exclusion rules (targets,
|
|
605
|
+
* downloads, modified clicks, external URLs) live in Aberdeen.
|
|
672
606
|
*
|
|
673
|
-
* A link inside a panel is that panel's {@link Panel.open},
|
|
674
|
-
*
|
|
675
|
-
*
|
|
676
|
-
* panel to build on, so it replaces the stack as a whole, exactly as a cold
|
|
677
|
-
* link to the same URL would open it.
|
|
607
|
+
* A link inside a panel is that panel's {@link Panel.open}, with `data-panel`
|
|
608
|
+
* as its `how`. A link outside every panel — a nav item, one in a dialog —
|
|
609
|
+
* has nothing to build on, so it replaces the stack as a cold link would.
|
|
678
610
|
*/
|
|
679
611
|
private interceptLinks;
|
|
680
612
|
get currentPanel(): Panel | undefined;
|
|
@@ -689,101 +621,81 @@ export declare class PanelStackController implements PanelStack {
|
|
|
689
621
|
/** Adopt a changed `linkNavigation` default; the next click reads it. */
|
|
690
622
|
setLinkNavigation(mode: "push" | "replace" | "open" | undefined): void;
|
|
691
623
|
/**
|
|
692
|
-
* The breadcrumb stack, drawn by `main()` into the top bar: every open
|
|
693
|
-
*
|
|
694
|
-
*
|
|
695
|
-
*
|
|
696
|
-
* so clicking a crumb goes back to that panel and closes what was stacked on
|
|
697
|
-
* top of it, pinned and unsaved panels excepted. Right-click (or long-press)
|
|
698
|
-
* offers pinning, and closing just that one panel — the close that splices
|
|
699
|
-
* it out of the middle when it isn't last.
|
|
624
|
+
* The breadcrumb stack, drawn by `main()` into the top bar: every open panel,
|
|
625
|
+
* oldest first, the ones on screen in bold. Every crumb but the current one is
|
|
626
|
+
* a plain link, and a link to an open panel returns to it (see `navigate`),
|
|
627
|
+
* closing what was stacked on top. Right-click offers pinning and closing.
|
|
700
628
|
*/
|
|
701
629
|
drawCrumbs(): void;
|
|
702
630
|
private drawCrumb;
|
|
703
631
|
/**
|
|
704
|
-
* Flip a panel's pin (see {@link Panel.pinned}). The
|
|
705
|
-
*
|
|
706
|
-
* the pin — a same-panel state tweak, which the router applies unguarded.
|
|
632
|
+
* Flip a panel's pin (see {@link Panel.pinned}). The current history entry's
|
|
633
|
+
* snapshot is rewritten too, so a reload keeps the pin.
|
|
707
634
|
*/
|
|
708
635
|
private togglePin;
|
|
709
636
|
/**
|
|
710
|
-
* `"<panel title> · <app title>"`, kept in sync with the current panel
|
|
711
|
-
* prefixed `"• "` while *any* open panel holds unsaved work
|
|
712
|
-
*
|
|
713
|
-
* losing the work is tab-wide, so the mark on the tab is too.
|
|
637
|
+
* `"<panel title> · <app title>"`, kept in sync with the current panel, and
|
|
638
|
+
* prefixed `"• "` while *any* open panel holds unsaved work — not just the
|
|
639
|
+
* current one, since the risk of losing it is tab-wide.
|
|
714
640
|
*/
|
|
715
641
|
private watchTitle;
|
|
716
642
|
/**
|
|
717
|
-
* While any open panel holds unsaved work, closing the tab
|
|
718
|
-
*
|
|
719
|
-
*
|
|
720
|
-
* holding the tab is in front of the user rather than parked out of sight.
|
|
643
|
+
* While any open panel holds unsaved work, closing the tab runs into the
|
|
644
|
+
* browser's own are-you-sure, with that panel brought on screen as the
|
|
645
|
+
* question is raised rather than left parked out of sight.
|
|
721
646
|
*/
|
|
722
647
|
private guardTabClose;
|
|
723
648
|
/**
|
|
724
649
|
* Draw the column viewport into the current element. Called by `main()`.
|
|
725
650
|
*
|
|
726
|
-
* A column is the panel's own content
|
|
727
|
-
* places for it: its {@link Panel.actions}
|
|
728
|
-
* the top bar on narrow ones (see {@link drawActions}).
|
|
651
|
+
* A column is the panel's own content plus the one bit of chrome the shell
|
|
652
|
+
* places for it: its {@link Panel.actions} (see {@link drawActions}).
|
|
729
653
|
*/
|
|
730
654
|
drawColumns(): void;
|
|
731
655
|
private drawPanel;
|
|
732
656
|
/**
|
|
733
657
|
* The one bit of column chrome the shell draws: the panel's actions, in a
|
|
734
658
|
* quiet strip above the scroll area — and only while the shell is wide, the
|
|
735
|
-
* top bar carrying them otherwise. Everything else
|
|
736
|
-
* own content: a screen that wants a heading or a card draws them itself.
|
|
737
|
-
* Going back isn't here either — that is the breadcrumbs' job, in the bar.
|
|
659
|
+
* top bar carrying them otherwise. Everything else is the panel's own content.
|
|
738
660
|
*/
|
|
739
661
|
private drawActions;
|
|
740
662
|
scheduleLayout(): void;
|
|
741
663
|
/**
|
|
742
|
-
* Run the pending layout pass now
|
|
743
|
-
*
|
|
744
|
-
* `$panel.
|
|
745
|
-
* option. Does nothing when no pass is owed.
|
|
664
|
+
* Run the pending layout pass now instead of on the frame it waits for, for
|
|
665
|
+
* callers that must read what only the pass knows (`$panel.visible`,
|
|
666
|
+
* `$panel.width`) and can't wait. Does nothing when no pass is owed.
|
|
746
667
|
*/
|
|
747
668
|
private flushLayout;
|
|
748
669
|
/**
|
|
749
670
|
* Measure the content area, and with it the width a panel of each size gets.
|
|
671
|
+
* The column region *is* that area (CSS's doing), so there is nothing to add
|
|
672
|
+
* up here that could drift from the bars above and below. Widths stay
|
|
673
|
+
* fractional: a rounded column edge would drift a pixel away from that chrome.
|
|
750
674
|
*
|
|
751
|
-
* The
|
|
752
|
-
*
|
|
753
|
-
*
|
|
754
|
-
*
|
|
755
|
-
*
|
|
756
|
-
*
|
|
757
|
-
*
|
|
758
|
-
*
|
|
759
|
-
*
|
|
760
|
-
*
|
|
761
|
-
* and 1520px four of 380. An area too narrow for two is a single column,
|
|
762
|
-
* itself capped at {@link SMALL_MAX_PX}: a small centres there instead of
|
|
763
|
-
* stretching toward 720, so its ceiling holds, while the larger sizes still
|
|
764
|
-
* take the whole area. A width is thus a pure function of the window: a panel
|
|
765
|
-
* NEVER resizes because a neighbour came or went, and only a window resize
|
|
766
|
-
* (the snap pass in `layout`) changes one.
|
|
767
|
-
*
|
|
768
|
-
* `undefined` while the shell has no width to speak of (it isn't in a document
|
|
769
|
-
* yet, or it's `display:none`); the next pass tries again.
|
|
675
|
+
* The area divides into the fewest whole number of columns of at most
|
|
676
|
+
* {@link SMALL_MAX_PX} — so 1080px is two of 540, 1520px three of ~507 —
|
|
677
|
+
* keeping columns as wide as the cap allows rather than as narrow as
|
|
678
|
+
* {@link SMALL_MIN_PX} permits. Only below 720px can that division dip under
|
|
679
|
+
* the minimum; there a single column, capped at 540, centres in the area
|
|
680
|
+
* instead. A width is therefore a pure function of the window: a panel NEVER
|
|
681
|
+
* resizes because a neighbour came or went.
|
|
682
|
+
*
|
|
683
|
+
* `undefined` while the shell has no width yet (not in a document, or
|
|
684
|
+
* `display:none`); the next pass tries again.
|
|
770
685
|
*/
|
|
771
686
|
private measure;
|
|
772
687
|
/**
|
|
773
|
-
* The measurements this pass runs on
|
|
774
|
-
*
|
|
775
|
-
*
|
|
776
|
-
* would be a forced reflow each, in the middle of building their DOM.
|
|
688
|
+
* The measurements this pass runs on, taken once per pass and per commit and
|
|
689
|
+
* shared with the panels drawn in between: they must all size against the
|
|
690
|
+
* same shell, and a `getBoundingClientRect()` each is a forced reflow each.
|
|
777
691
|
*/
|
|
778
692
|
private geometry;
|
|
779
693
|
/** How wide a panel asking for this is, right now; 0 while the shell can't be measured. */
|
|
780
694
|
private roomFor;
|
|
781
695
|
/**
|
|
782
|
-
* Size and position every panel
|
|
783
|
-
*
|
|
784
|
-
*
|
|
785
|
-
* of them are visible, how wide each one is and where it sits. All the motion
|
|
786
|
-
* between two of these arrangements is CSS's job.
|
|
696
|
+
* Size and position every panel — everything CSS can't work out for itself:
|
|
697
|
+
* which panels exist, which are visible, how wide each is and where it sits.
|
|
698
|
+
* The motion between two such arrangements is CSS's job.
|
|
787
699
|
*/
|
|
788
700
|
private layout;
|
|
789
701
|
/** Let a `loading` panel's enter animation wait — but not indefinitely. */
|