staffa 0.8.1 → 0.9.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 +29 -21
- package/dist/components/main.d.ts +61 -1
- package/dist/components/main.js +58 -3
- package/dist/components/menu.js +8 -1
- package/dist/components/panels.d.ts +98 -15
- package/dist/components/panels.js +180 -52
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/staffa.esm.js +1 -1
- package/package.json +2 -2
- package/skill/AncestorTable.md +10 -0
- package/skill/MainOptions.md +41 -0
- package/skill/SKILL.md +50 -21
- package/skill/closeNav.md +23 -0
- package/skill/panels.md +1 -1
- package/src/components/main.ts +103 -4
- package/src/components/menu.ts +7 -1
- package/src/components/panels.ts +199 -52
- package/src/index.ts +2 -2
|
@@ -33,11 +33,12 @@ function splitPath(path) {
|
|
|
33
33
|
return p === "/" ? [] : p.slice(1).split("/");
|
|
34
34
|
}
|
|
35
35
|
/**
|
|
36
|
-
* Turn a
|
|
36
|
+
* Turn a path template into segment tokens, throwing on malformed ones. A
|
|
37
37
|
* segment is a param only when it is *entirely* a bracket group, so a literal
|
|
38
|
-
* segment that merely contains brackets (`/v[1]beta`) stays literal.
|
|
38
|
+
* segment that merely contains brackets (`/v[1]beta`) stays literal. Used for
|
|
39
|
+
* both tables keyed by a path template: `routes` and `ancestors`.
|
|
39
40
|
*/
|
|
40
|
-
function
|
|
41
|
+
function compileKey(key) {
|
|
41
42
|
const parts = splitPath(key);
|
|
42
43
|
const segs = parts.map((part, i) => {
|
|
43
44
|
if (!part.startsWith("[") || !part.endsWith("]"))
|
|
@@ -57,7 +58,7 @@ function compileRoute(key, draw) {
|
|
|
57
58
|
}
|
|
58
59
|
return { kind: "param", name, matcher };
|
|
59
60
|
});
|
|
60
|
-
return { key, segs
|
|
61
|
+
return { key, segs };
|
|
61
62
|
}
|
|
62
63
|
/** Percent-decode a path segment, leaving it alone when it isn't valid encoding. */
|
|
63
64
|
function decodeSeg(value) {
|
|
@@ -218,6 +219,8 @@ A.insertGlobalCss({
|
|
|
218
219
|
let active = null;
|
|
219
220
|
export class PanelController {
|
|
220
221
|
compiled;
|
|
222
|
+
/** The `ancestors` table, compiled like the routes it is keyed by. */
|
|
223
|
+
ancestors;
|
|
221
224
|
opts;
|
|
222
225
|
/** The live stack, shallow-to-deep. Closing panels are no longer part of it. */
|
|
223
226
|
live = [];
|
|
@@ -237,13 +240,22 @@ export class PanelController {
|
|
|
237
240
|
lastBodyW = -1;
|
|
238
241
|
layoutQueued = false;
|
|
239
242
|
timers = new Set();
|
|
243
|
+
/** The stack the navigation in flight is heading for; see {@link intended}. */
|
|
244
|
+
intent = null;
|
|
245
|
+
/** The navigation the router hasn't settled yet, if any. */
|
|
246
|
+
settling = null;
|
|
247
|
+
/** The one navigation waiting behind it; see {@link issue}. */
|
|
248
|
+
queued = null;
|
|
240
249
|
constructor(opts) {
|
|
241
250
|
if (active) {
|
|
242
251
|
throw new Error("Staffa: only one routed S.main() (one with `routes`) can be active at a time");
|
|
243
252
|
}
|
|
244
253
|
active = this;
|
|
245
254
|
this.opts = opts;
|
|
246
|
-
this.compiled = Object.entries(opts.routes).map(([key, draw]) =>
|
|
255
|
+
this.compiled = Object.entries(opts.routes).map(([key, draw]) => ({ ...compileKey(key), draw }));
|
|
256
|
+
this.ancestors = Object.entries(opts.ancestors ?? {})
|
|
257
|
+
.filter((entry) => entry[1] != null)
|
|
258
|
+
.map(([key, fn]) => ({ ...compileKey(key), fn }));
|
|
247
259
|
// The router consults this guard before any navigation is applied — ours,
|
|
248
260
|
// a link's, browser back/forward, even a direct route.go() by app code —
|
|
249
261
|
// so every panel the change would remove gets its requestClose asked,
|
|
@@ -274,6 +286,10 @@ export class PanelController {
|
|
|
274
286
|
for (const t of this.timers)
|
|
275
287
|
clearTimeout(t);
|
|
276
288
|
this.timers.clear();
|
|
289
|
+
// Nothing is going to navigate a shell that isn't there: whatever was
|
|
290
|
+
// waiting its turn is answered rather than left hanging.
|
|
291
|
+
this.queued?.settle(false);
|
|
292
|
+
this.queued = null;
|
|
277
293
|
route.setGuard(appGuard);
|
|
278
294
|
if (active === this)
|
|
279
295
|
active = null;
|
|
@@ -295,23 +311,54 @@ export class PanelController {
|
|
|
295
311
|
return this.compiled.some((r) => matchRoute(r, segments) != null);
|
|
296
312
|
}
|
|
297
313
|
/**
|
|
298
|
-
* The
|
|
299
|
-
*
|
|
300
|
-
*
|
|
301
|
-
*
|
|
302
|
-
*
|
|
314
|
+
* The stack for origin-less navigation: a cold deep link, a nav item, a
|
|
315
|
+
* `route.go()` — anything arriving without a panel to build on and without a
|
|
316
|
+
* snapshot to restore.
|
|
317
|
+
*
|
|
318
|
+
* The app's {@link PanelStackOptions.ancestors} gets first say, since only it
|
|
319
|
+
* can know what belongs under a path that doesn't spell its own context out
|
|
320
|
+
* (a `/thread/[id]` reached from a notification). Failing that — or when it
|
|
321
|
+
* has no opinion — every prefix of the path is probed against the route table
|
|
322
|
+
* and the matching ones become the stack. Either way, a path with no route is
|
|
323
|
+
* skipped rather than opened as a "not found" column, so an app that doesn't
|
|
324
|
+
* want one screen stacked under another simply doesn't route it. The path
|
|
325
|
+
* itself is always the top panel, matched or not.
|
|
303
326
|
*/
|
|
304
327
|
deriveStack(path) {
|
|
305
|
-
const
|
|
328
|
+
const top = normalizePath(path);
|
|
329
|
+
const asked = this.askAncestors(top);
|
|
330
|
+
const beneath = asked ? asked.map(normalizePath) : this.prefixesOf(top);
|
|
306
331
|
const stack = [];
|
|
307
|
-
for (
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
stack.push(prefix);
|
|
332
|
+
for (const ancestor of beneath) {
|
|
333
|
+
if (ancestor !== top && !stack.includes(ancestor) && this.matches(ancestor))
|
|
334
|
+
stack.push(ancestor);
|
|
311
335
|
}
|
|
312
|
-
stack.push(
|
|
336
|
+
stack.push(top);
|
|
313
337
|
return stack;
|
|
314
338
|
}
|
|
339
|
+
/**
|
|
340
|
+
* Ask the `ancestors` table what belongs beneath `path`. The first key that
|
|
341
|
+
* matches answers — with its own matched params, so it never has to take the
|
|
342
|
+
* path apart itself — and `undefined` from it means "no opinion", leaving the
|
|
343
|
+
* path to the prefix derivation just as an unlisted one is.
|
|
344
|
+
*/
|
|
345
|
+
askAncestors(path) {
|
|
346
|
+
const segments = splitPath(path);
|
|
347
|
+
for (const entry of this.ancestors) {
|
|
348
|
+
const params = matchRoute(entry, segments);
|
|
349
|
+
if (params)
|
|
350
|
+
return entry.fn(params, path) ?? undefined;
|
|
351
|
+
}
|
|
352
|
+
return undefined;
|
|
353
|
+
}
|
|
354
|
+
/** Every prefix of `path` that has a route, shallowest first. */
|
|
355
|
+
prefixesOf(path) {
|
|
356
|
+
const segments = splitPath(path);
|
|
357
|
+
const found = [];
|
|
358
|
+
for (let i = 1; i < segments.length; i++)
|
|
359
|
+
found.push("/" + segments.slice(0, i).join("/"));
|
|
360
|
+
return found;
|
|
361
|
+
}
|
|
315
362
|
/** The stack a route implies: its snapshot topped by its path, or — without a snapshot — derived. */
|
|
316
363
|
targetFor(path, snapshot) {
|
|
317
364
|
if (Array.isArray(snapshot))
|
|
@@ -413,7 +460,7 @@ export class PanelController {
|
|
|
413
460
|
entry.$page = A.proxy({
|
|
414
461
|
params,
|
|
415
462
|
path,
|
|
416
|
-
close: () => this.
|
|
463
|
+
close: () => this.closePath(entry.path),
|
|
417
464
|
});
|
|
418
465
|
return entry;
|
|
419
466
|
}
|
|
@@ -468,6 +515,58 @@ export class PanelController {
|
|
|
468
515
|
this.timers.add(timer);
|
|
469
516
|
}
|
|
470
517
|
// ── Navigation ─────────────────────────────────────────────────────────
|
|
518
|
+
/**
|
|
519
|
+
* The stack navigation works from: the one we're on the way to while a change
|
|
520
|
+
* is still settling, and the one on screen otherwise.
|
|
521
|
+
*
|
|
522
|
+
* Settling takes a moment more often than it looks: an async
|
|
523
|
+
* {@link Page.requestClose}, and every `route.back()`, which travels through
|
|
524
|
+
* the browser's history and lands on a `popstate`. Working from the committed
|
|
525
|
+
* stack in that window would make a second Escape ask for the panel the first
|
|
526
|
+
* one is already taking away — so two quick Escapes would peel one panel.
|
|
527
|
+
*/
|
|
528
|
+
intended() {
|
|
529
|
+
return this.intent ?? this.paths();
|
|
530
|
+
}
|
|
531
|
+
/**
|
|
532
|
+
* Put a navigation to the router, or — while one is still settling — behind
|
|
533
|
+
* the one that is. Only the newest waits: each was worked out against
|
|
534
|
+
* {@link intended}, so the newest is the one that means what the user last
|
|
535
|
+
* asked for, and the one it displaces resolves `false`.
|
|
536
|
+
*
|
|
537
|
+
* A refusal empties the queue instead of running it. A veto is a "no, keep
|
|
538
|
+
* this open", and the Escape queued behind it was aimed a panel deeper — with
|
|
539
|
+
* the veto standing, running it would close the very panel that just said no.
|
|
540
|
+
*/
|
|
541
|
+
issue(target, run) {
|
|
542
|
+
this.intent = target;
|
|
543
|
+
if (this.settling) {
|
|
544
|
+
this.queued?.settle(false);
|
|
545
|
+
return new Promise((settle) => { this.queued = { run, settle }; });
|
|
546
|
+
}
|
|
547
|
+
return this.start(run);
|
|
548
|
+
}
|
|
549
|
+
start(run) {
|
|
550
|
+
const done = (ok) => {
|
|
551
|
+
this.settling = null;
|
|
552
|
+
const next = this.queued;
|
|
553
|
+
this.queued = null;
|
|
554
|
+
// The router applies a change (and runs Aberdeen's queue, so our own
|
|
555
|
+
// commit has happened) before it settles us, which is what lets the next
|
|
556
|
+
// one go straight out: it asks the guards of the panels it removes from
|
|
557
|
+
// the stack as it stands now, not the one it was queued against.
|
|
558
|
+
if (ok && next)
|
|
559
|
+
this.start(next.run).then(next.settle, () => next.settle(false));
|
|
560
|
+
else {
|
|
561
|
+
this.intent = null;
|
|
562
|
+
next?.settle(false);
|
|
563
|
+
}
|
|
564
|
+
return ok;
|
|
565
|
+
};
|
|
566
|
+
const settling = Promise.resolve(run()).then(done, (e) => { console.error(e); return done(false); });
|
|
567
|
+
this.settling = settling;
|
|
568
|
+
return settling;
|
|
569
|
+
}
|
|
471
570
|
/**
|
|
472
571
|
* Navigate back to a stack that is a truncation of the current one — the shared
|
|
473
572
|
* implementation of Escape, a page closing itself, return-links and
|
|
@@ -478,21 +577,23 @@ export class PanelController {
|
|
|
478
577
|
* promise reports its verdict.
|
|
479
578
|
*/
|
|
480
579
|
goBackTo(target) {
|
|
481
|
-
return route.back({ path: target[target.length - 1] }, { state: { panels: target.slice(0, -1) } });
|
|
580
|
+
return this.issue(target, () => route.back({ path: target[target.length - 1] }, { state: { panels: target.slice(0, -1) } }));
|
|
482
581
|
}
|
|
483
582
|
/** Close every panel above `index` (guarded). Resolves `false` when vetoed. */
|
|
484
583
|
closeDownTo(index) {
|
|
485
|
-
|
|
584
|
+
const paths = this.intended();
|
|
585
|
+
if (index < 0 || index >= paths.length - 1)
|
|
486
586
|
return Promise.resolve(false);
|
|
487
|
-
return this.goBackTo(
|
|
587
|
+
return this.goBackTo(paths.slice(0, index + 1));
|
|
488
588
|
}
|
|
489
589
|
/** Guarded close of the top panel. */
|
|
490
590
|
closeTop() {
|
|
491
|
-
return this.closeDownTo(this.
|
|
591
|
+
return this.closeDownTo(this.intended().length - 2);
|
|
492
592
|
}
|
|
493
593
|
/**
|
|
494
|
-
* Guarded close of
|
|
495
|
-
* page's own close affordances ({@link Page.close}, a box's ✕) come
|
|
594
|
+
* Guarded close of whichever panel is open at `path`, top of the stack or not
|
|
595
|
+
* — what a page's own close affordances ({@link Page.close}, a box's ✕) come
|
|
596
|
+
* down to. `false` when that path isn't open.
|
|
496
597
|
*
|
|
497
598
|
* The top panel pops back to the snapshot beneath it. Any other panel is
|
|
498
599
|
* *spliced* out: its guard runs, the columns above it keep their place and
|
|
@@ -502,13 +603,15 @@ export class PanelController {
|
|
|
502
603
|
* why it goes through `route.go` here rather than through `navigate()`, whose
|
|
503
604
|
* "link to the panel we're already on" check would see a no-op.
|
|
504
605
|
*/
|
|
505
|
-
|
|
506
|
-
|
|
606
|
+
closePath(path) {
|
|
607
|
+
const paths = this.intended();
|
|
608
|
+
const index = paths.indexOf(normalizePath(path));
|
|
609
|
+
if (index < 0)
|
|
507
610
|
return Promise.resolve(false);
|
|
508
|
-
if (index ===
|
|
509
|
-
return this.
|
|
510
|
-
const target =
|
|
511
|
-
return
|
|
611
|
+
if (index === paths.length - 1)
|
|
612
|
+
return this.closeDownTo(index - 1);
|
|
613
|
+
const target = paths.filter((_, i) => i !== index);
|
|
614
|
+
return this.issue(target, () => route.go({
|
|
512
615
|
path: target[target.length - 1],
|
|
513
616
|
// The top panel keeps its search params and hash: it isn't going
|
|
514
617
|
// anywhere, and `go()` would otherwise default them away.
|
|
@@ -517,22 +620,19 @@ export class PanelController {
|
|
|
517
620
|
state: { panels: target.slice(0, -1) },
|
|
518
621
|
}));
|
|
519
622
|
}
|
|
520
|
-
/** Guarded close of whichever panel `path` is open as. False when it isn't open. */
|
|
521
|
-
closeByPath(path) {
|
|
522
|
-
const wanted = normalizePath(path);
|
|
523
|
-
return this.closePanelAt(this.live.findIndex((entry) => entry.path === wanted));
|
|
524
|
-
}
|
|
525
623
|
/** Guarded close of the panel whose `.s-panel` element this is. */
|
|
526
624
|
closePanelEl(el) {
|
|
527
|
-
|
|
625
|
+
const entry = this.live.find((e) => e.el === el);
|
|
626
|
+
return entry ? this.closePath(entry.path) : Promise.resolve(false);
|
|
528
627
|
}
|
|
529
628
|
/**
|
|
530
|
-
* Navigate to `href`. `
|
|
531
|
-
*
|
|
532
|
-
* the whole stack instead). `replace` swaps the
|
|
533
|
-
* stacking on top of it
|
|
629
|
+
* Navigate to `href`. `origin` is the path of the panel the link lives in, or
|
|
630
|
+
* `null` when it has none — a nav item, or a programmatic call, which builds
|
|
631
|
+
* the whole stack instead (see {@link deriveStack}). `replace` swaps the
|
|
632
|
+
* originating panel rather than stacking on top of it, and `beneath` says what
|
|
633
|
+
* the stack under the target is outright, for callers that know.
|
|
534
634
|
*/
|
|
535
|
-
navigate(href,
|
|
635
|
+
navigate(href, origin, replace = false, beneath) {
|
|
536
636
|
let url;
|
|
537
637
|
try {
|
|
538
638
|
url = new URL(href, location.href);
|
|
@@ -543,21 +643,22 @@ export class PanelController {
|
|
|
543
643
|
const path = normalizePath(url.pathname);
|
|
544
644
|
const search = Object.fromEntries(new URLSearchParams(url.search));
|
|
545
645
|
const hash = url.hash;
|
|
646
|
+
const paths = this.intended();
|
|
546
647
|
// A link to a panel that is already open is a return, not a navigation —
|
|
547
648
|
// so a stack can never hold the same path twice.
|
|
548
|
-
const open =
|
|
549
|
-
if (open >= 0 && open <
|
|
649
|
+
const open = paths.indexOf(path);
|
|
650
|
+
if (open >= 0 && open < paths.length - 1 && !beneath) {
|
|
550
651
|
void this.closeDownTo(open);
|
|
551
652
|
return;
|
|
552
653
|
}
|
|
553
|
-
if (open >= 0) {
|
|
654
|
+
if (open >= 0 && !beneath) {
|
|
554
655
|
// The target is the panel we're already on. Going nowhere — but the link
|
|
555
656
|
// may still carry a different search or hash, which belong to the top
|
|
556
657
|
// panel: record that as a history entry, leaving the stack alone (the
|
|
557
658
|
// panel reconciles by path, so it isn't even redrawn).
|
|
558
659
|
if (url.search === location.search && (url.hash || "") === (location.hash || ""))
|
|
559
660
|
return;
|
|
560
|
-
route.go({ path, search, hash, state: { panels:
|
|
661
|
+
void this.issue(paths, () => route.go({ path, search, hash, state: { panels: paths.slice(0, -1) } }));
|
|
561
662
|
return;
|
|
562
663
|
}
|
|
563
664
|
// Without an originating panel there is no stack to build on, so derive
|
|
@@ -565,14 +666,22 @@ export class PanelController {
|
|
|
565
666
|
// The route guard (checkChange) asks every panel this removes — a set
|
|
566
667
|
// defined by the target stack, wherever those panels happen to sit —
|
|
567
668
|
// before the change is applied; a veto leaves everything untouched.
|
|
568
|
-
const
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
669
|
+
const originIndex = origin == null ? -1 : paths.indexOf(origin);
|
|
670
|
+
const under = beneath
|
|
671
|
+
? beneath.map(normalizePath).filter((p) => p !== path)
|
|
672
|
+
: originIndex < 0
|
|
673
|
+
? this.deriveStack(path).slice(0, -1)
|
|
674
|
+
: paths.slice(0, replace ? originIndex : originIndex + 1);
|
|
675
|
+
void this.issue([...under, path], () => route.go({ path, search, hash, state: { panels: under } }));
|
|
572
676
|
}
|
|
573
677
|
/** Programmatic push/replace, with the top panel as the implied origin. */
|
|
574
678
|
pushPath(path, replace) {
|
|
575
|
-
this.
|
|
679
|
+
const paths = this.intended();
|
|
680
|
+
this.navigate(path, paths[paths.length - 1] ?? null, replace);
|
|
681
|
+
}
|
|
682
|
+
/** Programmatic open-as-a-whole-stack: `beneath` as given, or derived. */
|
|
683
|
+
openPath(path, beneath) {
|
|
684
|
+
this.navigate(path, null, false, beneath);
|
|
576
685
|
}
|
|
577
686
|
// ── Link interception ──────────────────────────────────────────────────
|
|
578
687
|
/**
|
|
@@ -586,8 +695,8 @@ export class PanelController {
|
|
|
586
695
|
interceptLinks() {
|
|
587
696
|
route.interceptLinks((url, anchor) => {
|
|
588
697
|
const panel = anchor.closest(".s-panel");
|
|
589
|
-
const
|
|
590
|
-
this.navigate(url.href,
|
|
698
|
+
const origin = panel ? this.live.find((entry) => entry.el === panel) : undefined;
|
|
699
|
+
this.navigate(url.href, origin?.path ?? null, anchor.getAttribute("data-panel") === "replace");
|
|
591
700
|
return true;
|
|
592
701
|
});
|
|
593
702
|
}
|
|
@@ -991,6 +1100,25 @@ export const panels = {
|
|
|
991
1100
|
replace(path) {
|
|
992
1101
|
requireActive().pushPath(path, true);
|
|
993
1102
|
},
|
|
1103
|
+
/**
|
|
1104
|
+
* Opens `path` as a whole arrangement rather than on top of what's there: the
|
|
1105
|
+
* same thing a nav item or a fresh tab does. Without `beneath`, the stack under
|
|
1106
|
+
* it is worked out the way a cold link's is (see `S.main()`'s `ancestors`);
|
|
1107
|
+
* with it, the paths you give are opened underneath, shallowest first.
|
|
1108
|
+
*
|
|
1109
|
+
* That's the one for a screen whose URL doesn't say where it belongs — the
|
|
1110
|
+
* thread a notification opens — and for seeding a stack from code in general.
|
|
1111
|
+
* Panels the new arrangement also holds stay as they are, and any it drops are
|
|
1112
|
+
* asked their {@link Page.requestClose} first.
|
|
1113
|
+
*
|
|
1114
|
+
* @example
|
|
1115
|
+
* ```ts
|
|
1116
|
+
* S.panels.open(`/thread/${id}`, [`/mailbox/${mailboxId}`]);
|
|
1117
|
+
* ```
|
|
1118
|
+
*/
|
|
1119
|
+
open(path, beneath) {
|
|
1120
|
+
requireActive().openPath(path, beneath);
|
|
1121
|
+
},
|
|
994
1122
|
/**
|
|
995
1123
|
* Closes the top panel, or, given a `path`, whichever panel is open at it,
|
|
996
1124
|
* asking {@link Page.requestClose} first. A panel that isn't on top is taken
|
|
@@ -1001,7 +1129,7 @@ export const panels = {
|
|
|
1001
1129
|
*/
|
|
1002
1130
|
close(path) {
|
|
1003
1131
|
const ctl = requireActive();
|
|
1004
|
-
return path == null ? ctl.closeTop() : ctl.
|
|
1132
|
+
return path == null ? ctl.closeTop() : ctl.closePath(path);
|
|
1005
1133
|
},
|
|
1006
1134
|
/** The paths of the open panels, oldest first. Reactive: safe to read in a scope. */
|
|
1007
1135
|
get stack() {
|
package/dist/index.d.ts
CHANGED
|
@@ -37,8 +37,8 @@ export { buttonChooser, type ButtonChooserOptions } from "./components/buttonCho
|
|
|
37
37
|
export { buttonGroup, type ButtonGroupOptions } from "./components/buttonGroup.js";
|
|
38
38
|
export { checkbox, type CheckboxOptions } from "./components/checkbox.js";
|
|
39
39
|
export { form, type FormOptions } from "./components/form.js";
|
|
40
|
-
export { main, type MainOptions } from "./components/main.js";
|
|
41
|
-
export { panels, type Page, type Routes, type RouteHandler, type RouteTable, type PathParams, type SegParams } from "./components/panels.js";
|
|
40
|
+
export { main, closeNav, type MainOptions } from "./components/main.js";
|
|
41
|
+
export { panels, type Page, type Routes, type RouteHandler, type RouteTable, type AncestorsHandler, type AncestorTable, type PathParams, type SegParams } from "./components/panels.js";
|
|
42
42
|
export { menuButton, showFloatingMenu, addContextMenu, isFloatingMenuOpen, closeFloatingMenu, type MenuOptions, type MenuEntry, type MenuItem, type MenuSeparator, type FloatingMenuOptions, type ContextMenuOptions } from "./components/menu.js";
|
|
43
43
|
export { dialog, alert, confirm, prompt, isDialogOpen, type DialogOptions } from "./components/dialog.js";
|
|
44
44
|
export { select, type SelectOptions, type SelectOptionInput } from "./components/select.js";
|
package/dist/index.js
CHANGED
|
@@ -40,7 +40,7 @@ export { buttonChooser } from "./components/buttonChooser.js";
|
|
|
40
40
|
export { buttonGroup } from "./components/buttonGroup.js";
|
|
41
41
|
export { checkbox } from "./components/checkbox.js";
|
|
42
42
|
export { form } from "./components/form.js";
|
|
43
|
-
export { main } from "./components/main.js";
|
|
43
|
+
export { main, closeNav } from "./components/main.js";
|
|
44
44
|
export { panels } from "./components/panels.js";
|
|
45
45
|
export { menuButton, showFloatingMenu, addContextMenu, isFloatingMenuOpen, closeFloatingMenu } from "./components/menu.js";
|
|
46
46
|
export { dialog, alert, confirm, prompt, isDialogOpen } from "./components/dialog.js";
|