@stealthscale/component-screen 0.0.1 → 0.2.1

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.
@@ -0,0 +1,1182 @@
1
+ import { r as FOLDS, t as recipe } from "./recipe-DRMsitrw.js";
2
+ import { c } from "react/compiler-runtime";
3
+ import { createSlotRecipeContext } from "@stealthscale/theme";
4
+ import { createRequiredContext, useConst, useControllableState, useSafeLayoutEffect } from "@stealthscale/hooks";
5
+ import { RovingFocus, VisuallyHidden } from "@stealthscale/component-a11y";
6
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
7
+ import { createContext, use, useId, useState } from "react";
8
+ import { Portal } from "@stealthscale/component-primitives";
9
+ import { Menu, Tooltip } from "@stealthscale/component-disclosure";
10
+ //#region src/toolbar/item.tsx
11
+ /**
12
+ * Renders the control as a roving focus item.
13
+ *
14
+ * @param props - The element or component it renders as, the link's target and the item's props.
15
+ * @returns The control element.
16
+ */
17
+ function Item(t0) {
18
+ const $ = c(10);
19
+ let as;
20
+ let href;
21
+ let rest;
22
+ if ($[0] !== t0) {
23
+ ({as, href, ...rest} = t0);
24
+ $[0] = t0;
25
+ $[1] = as;
26
+ $[2] = href;
27
+ $[3] = rest;
28
+ } else {
29
+ as = $[1];
30
+ href = $[2];
31
+ rest = $[3];
32
+ }
33
+ let t1;
34
+ if ($[4] !== as || $[5] !== href || $[6] !== rest) {
35
+ t1 = as === void 0 ? href === void 0 ? {
36
+ as: "button",
37
+ type: "button",
38
+ ...rest
39
+ } : {
40
+ as: "a",
41
+ href,
42
+ ...rest
43
+ } : {
44
+ as,
45
+ href,
46
+ ...rest
47
+ };
48
+ $[4] = as;
49
+ $[5] = href;
50
+ $[6] = rest;
51
+ $[7] = t1;
52
+ } else t1 = $[7];
53
+ const t2 = t1;
54
+ let t3;
55
+ if ($[8] !== t2) {
56
+ t3 = /* @__PURE__ */ jsx(RovingFocus.Item, { ...t2 });
57
+ $[8] = t2;
58
+ $[9] = t3;
59
+ } else t3 = $[9];
60
+ return t3;
61
+ }
62
+ //#endregion
63
+ //#region src/folding/fold.ts
64
+ /**
65
+ * Registers the actions a narrow row folds into its menu.
66
+ *
67
+ * @remarks
68
+ * A folded action renders nothing in its row and registers with the row, so no hidden control
69
+ * remains in the tab order or in a toolbar's arrow-key order. The row keeps the entries in state
70
+ * and renders its menu from them. An action folds in a layout effect, so the row and its menu
71
+ * render before the browser paints the narrow row.
72
+ */
73
+ /**
74
+ * Context through which a row provides its fold functions to its actions.
75
+ */
76
+ const FoldContext = createContext(void 0);
77
+ /**
78
+ * Returns the entries with an action added at the end, or replaced in place under its id.
79
+ */
80
+ function placed(held, id, action) {
81
+ const at = held.findIndex((entry) => entry.id === id);
82
+ return at === -1 ? [...held, {
83
+ action,
84
+ id
85
+ }] : held.with(at, {
86
+ action,
87
+ id
88
+ });
89
+ }
90
+ /**
91
+ * Keeps a row's folded actions and the functions that register them.
92
+ *
93
+ * @remarks
94
+ * The functions keep their identity for the row's lifetime, so an action's registering effect
95
+ * runs when the action folds or unfolds and not when the row renders.
96
+ * @returns The entries in the order the actions folded, and the functions to provide to them.
97
+ */
98
+ function useFolded() {
99
+ const $ = c(5);
100
+ let t0;
101
+ if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
102
+ t0 = [];
103
+ $[0] = t0;
104
+ } else t0 = $[0];
105
+ const [entries, setEntries] = useState(t0);
106
+ let t1;
107
+ if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
108
+ t1 = () => ({
109
+ put: (id, action) => {
110
+ setEntries((held) => placed(held, id, action));
111
+ },
112
+ remove: (id_0) => {
113
+ setEntries((held_0) => held_0.filter((entry) => entry.id !== id_0));
114
+ }
115
+ });
116
+ $[1] = t1;
117
+ } else t1 = $[1];
118
+ const fold = useConst(t1);
119
+ let t2;
120
+ if ($[2] !== entries || $[3] !== fold) {
121
+ t2 = [entries, fold];
122
+ $[2] = entries;
123
+ $[3] = fold;
124
+ $[4] = t2;
125
+ } else t2 = $[4];
126
+ return t2;
127
+ }
128
+ /**
129
+ * Registers an action with its row while it is folded.
130
+ *
131
+ * @remarks
132
+ * The first effect adds the entry when the action folds and removes it when the action unfolds or
133
+ * unmounts. The second runs after every render of a folded action and replaces the entry's data
134
+ * in place, so an `onClick` written inline keeps the action's place in the menu. An action
135
+ * outside a row that folds registers nothing.
136
+ * @param folded - Whether the row folds the action into its menu.
137
+ * @param action - The action as the menu row renders it.
138
+ * @param id - The id the action registers under. A toolbar control passes the id it writes in
139
+ * `data-folds`, and any other action registers under an id of its own.
140
+ */
141
+ function useFoldable(folded, action, id) {
142
+ const $ = c(10);
143
+ const fold = use(FoldContext);
144
+ const own = useId();
145
+ const key = id ?? own;
146
+ let t0;
147
+ let t1;
148
+ if ($[0] !== fold || $[1] !== folded || $[2] !== key) {
149
+ t0 = () => {
150
+ if (!folded || fold === void 0) return;
151
+ return () => {
152
+ fold.remove(key);
153
+ };
154
+ };
155
+ t1 = [
156
+ fold,
157
+ folded,
158
+ key
159
+ ];
160
+ $[0] = fold;
161
+ $[1] = folded;
162
+ $[2] = key;
163
+ $[3] = t0;
164
+ $[4] = t1;
165
+ } else {
166
+ t0 = $[3];
167
+ t1 = $[4];
168
+ }
169
+ useSafeLayoutEffect(t0, t1);
170
+ let t2;
171
+ if ($[5] !== action || $[6] !== fold || $[7] !== folded || $[8] !== key) {
172
+ t2 = () => {
173
+ if (folded) fold?.put(key, action);
174
+ };
175
+ $[5] = action;
176
+ $[6] = fold;
177
+ $[7] = folded;
178
+ $[8] = key;
179
+ $[9] = t2;
180
+ } else t2 = $[9];
181
+ useSafeLayoutEffect(t2);
182
+ }
183
+ //#endregion
184
+ //#region src/sidebar/state.ts
185
+ /**
186
+ * Provides the sidebar's state to its parts, and a nav block's label identifier to its `NavLabel`.
187
+ *
188
+ * @remarks
189
+ * The root provides whether the sidebar is a rail and how to open the panel it is in, which the
190
+ * search reads to open the panel before it takes focus. A block creates its label identifier and
191
+ * the label reads it, so the two always match without an identifier from the caller. A
192
+ * `NavLabel` outside a block throws.
193
+ */
194
+ /**
195
+ * Size of the search field and the rail's search button per sidebar size.
196
+ */
197
+ const FIELDS = {
198
+ lg: "md",
199
+ md: "sm",
200
+ sm: "sm"
201
+ };
202
+ /**
203
+ * Returns the size of a sidebar's search field and of the rail's search button.
204
+ *
205
+ * @param size - The sidebar's size.
206
+ * @returns The field's size, one smaller than the sidebar, down to `sm`.
207
+ */
208
+ function fieldSizeOf(size) {
209
+ return FIELDS[size];
210
+ }
211
+ /**
212
+ * Provides the sidebar's state and reads it, throwing outside `Sidebar.Root` or returning
213
+ * `undefined` there.
214
+ */
215
+ const [SidebarProvider, useSidebar, useEnclosingSidebar] = createRequiredContext("Sidebar.Root");
216
+ /**
217
+ * Provides the block's state and reads it, throwing outside a `Sidebar.Nav`.
218
+ */
219
+ const [NavProvider, useNav] = createRequiredContext("Sidebar.Nav");
220
+ //#endregion
221
+ //#region src/switcher/context.ts
222
+ /**
223
+ * Binds the switcher's recipe to the elements of its parts.
224
+ *
225
+ * @remarks
226
+ * The binding is in its own module, because an application's compiler reads the recipe at build
227
+ * time and the binding imports the runtime.
228
+ */
229
+ /**
230
+ * Binds the recipe once. The root provides the variants and every other part reads them.
231
+ */
232
+ const { withContext, withRootProvider } = createSlotRecipeContext(recipe);
233
+ //#endregion
234
+ //#region src/switcher/detail.ts
235
+ /**
236
+ * Renders the detail `span` at the switcher's size.
237
+ */
238
+ const Detail = withContext("span", "detail");
239
+ //#endregion
240
+ //#region src/switcher/indicator.ts
241
+ /**
242
+ * Renders the menu's indicator with the indicator slot's class.
243
+ */
244
+ const Indicator = withContext(Menu.Indicator, "indicator");
245
+ //#endregion
246
+ //#region src/switcher/label.ts
247
+ /**
248
+ * Renders the label `span` at the switcher's size.
249
+ */
250
+ const Label = withContext("span", "label");
251
+ //#endregion
252
+ //#region src/switcher/mark.ts
253
+ /**
254
+ * Renders the mark `span` at the switcher's size, hidden from the accessibility tree.
255
+ */
256
+ const Mark = withContext("span", "mark", { defaultProps: { "aria-hidden": true } });
257
+ //#endregion
258
+ //#region src/switcher/name.ts
259
+ /**
260
+ * Renders the name `span` at the switcher's size.
261
+ */
262
+ const Name = withContext("span", "name");
263
+ //#endregion
264
+ //#region src/switcher/choice.ts
265
+ /**
266
+ * Returns the initials of a name: the first letter of each of its first two words, in capitals.
267
+ *
268
+ * @param label - The name.
269
+ * @returns One or two letters.
270
+ */
271
+ function initialsOf(label) {
272
+ return label.split(/\s+/u).filter((word) => word !== "").slice(0, 2).map((word) => word.charAt(0).toUpperCase()).join("");
273
+ }
274
+ //#endregion
275
+ //#region src/switcher/current.tsx
276
+ /**
277
+ * Renders the mark, and the name over the detail.
278
+ *
279
+ * @param props - The current choice.
280
+ * @returns The mark and the label.
281
+ */
282
+ function Current(t0) {
283
+ const $ = c(15);
284
+ const { choice } = t0;
285
+ let t1;
286
+ if ($[0] !== choice.label || $[1] !== choice.mark) {
287
+ t1 = choice.mark ?? initialsOf(choice.label);
288
+ $[0] = choice.label;
289
+ $[1] = choice.mark;
290
+ $[2] = t1;
291
+ } else t1 = $[2];
292
+ let t2;
293
+ if ($[3] !== t1) {
294
+ t2 = /* @__PURE__ */ jsx(Mark, { children: t1 });
295
+ $[3] = t1;
296
+ $[4] = t2;
297
+ } else t2 = $[4];
298
+ let t3;
299
+ if ($[5] !== choice.label) {
300
+ t3 = /* @__PURE__ */ jsx(Name, { children: choice.label });
301
+ $[5] = choice.label;
302
+ $[6] = t3;
303
+ } else t3 = $[6];
304
+ let t4;
305
+ if ($[7] !== choice.detail) {
306
+ t4 = choice.detail === void 0 ? null : /* @__PURE__ */ jsx(Detail, { children: choice.detail });
307
+ $[7] = choice.detail;
308
+ $[8] = t4;
309
+ } else t4 = $[8];
310
+ let t5;
311
+ if ($[9] !== t3 || $[10] !== t4) {
312
+ t5 = /* @__PURE__ */ jsxs(Label, { children: [t3, t4] });
313
+ $[9] = t3;
314
+ $[10] = t4;
315
+ $[11] = t5;
316
+ } else t5 = $[11];
317
+ let t6;
318
+ if ($[12] !== t2 || $[13] !== t5) {
319
+ t6 = /* @__PURE__ */ jsxs(Fragment, { children: [t2, t5] });
320
+ $[12] = t2;
321
+ $[13] = t5;
322
+ $[14] = t6;
323
+ } else t6 = $[14];
324
+ return t6;
325
+ }
326
+ //#endregion
327
+ //#region src/switcher/option.tsx
328
+ /**
329
+ * Renders the row as a checkable radio, or as a link for a choice with a page of its own.
330
+ *
331
+ * @param props - Whether the choice is current, the check's glyph, the choice and the handler.
332
+ * @returns The menu's option item.
333
+ */
334
+ function Option(t0) {
335
+ const $ = c(29);
336
+ const { checked, checkIcon, choice, onChoose } = t0;
337
+ let t1;
338
+ if ($[0] !== choice.href) {
339
+ t1 = choice.href === void 0 ? {} : {
340
+ as: "a",
341
+ href: choice.href
342
+ };
343
+ $[0] = choice.href;
344
+ $[1] = t1;
345
+ } else t1 = $[1];
346
+ const linked = t1;
347
+ let t2;
348
+ if ($[2] !== choice.value || $[3] !== onChoose) {
349
+ t2 = () => {
350
+ onChoose(choice.value);
351
+ };
352
+ $[2] = choice.value;
353
+ $[3] = onChoose;
354
+ $[4] = t2;
355
+ } else t2 = $[4];
356
+ let t3;
357
+ if ($[5] !== checkIcon) {
358
+ t3 = /* @__PURE__ */ jsx(Menu.ItemIndicator, { children: checkIcon });
359
+ $[5] = checkIcon;
360
+ $[6] = t3;
361
+ } else t3 = $[6];
362
+ let t4;
363
+ if ($[7] !== choice.label || $[8] !== choice.mark) {
364
+ t4 = choice.mark ?? initialsOf(choice.label);
365
+ $[7] = choice.label;
366
+ $[8] = choice.mark;
367
+ $[9] = t4;
368
+ } else t4 = $[9];
369
+ let t5;
370
+ if ($[10] !== t4) {
371
+ t5 = /* @__PURE__ */ jsx(Menu.ItemMark, { children: t4 });
372
+ $[10] = t4;
373
+ $[11] = t5;
374
+ } else t5 = $[11];
375
+ let t6;
376
+ if ($[12] !== choice.label) {
377
+ t6 = /* @__PURE__ */ jsx(Menu.ItemText, { children: choice.label });
378
+ $[12] = choice.label;
379
+ $[13] = t6;
380
+ } else t6 = $[13];
381
+ let t7;
382
+ if ($[14] !== choice.detail) {
383
+ t7 = choice.detail === void 0 ? null : /* @__PURE__ */ jsx(Menu.ItemDescription, { children: choice.detail });
384
+ $[14] = choice.detail;
385
+ $[15] = t7;
386
+ } else t7 = $[15];
387
+ let t8;
388
+ if ($[16] !== t6 || $[17] !== t7) {
389
+ t8 = /* @__PURE__ */ jsxs(Menu.ItemLines, { children: [t6, t7] });
390
+ $[16] = t6;
391
+ $[17] = t7;
392
+ $[18] = t8;
393
+ } else t8 = $[18];
394
+ let t9;
395
+ if ($[19] !== checked || $[20] !== choice.disabled || $[21] !== choice.label || $[22] !== choice.value || $[23] !== linked || $[24] !== t2 || $[25] !== t3 || $[26] !== t5 || $[27] !== t8) {
396
+ t9 = /* @__PURE__ */ jsxs(Menu.OptionItem, {
397
+ checked,
398
+ disabled: choice.disabled,
399
+ onCheckedChange: t2,
400
+ type: "radio",
401
+ value: choice.value,
402
+ valueText: choice.label,
403
+ ...linked,
404
+ children: [
405
+ t3,
406
+ t5,
407
+ t8
408
+ ]
409
+ });
410
+ $[19] = checked;
411
+ $[20] = choice.disabled;
412
+ $[21] = choice.label;
413
+ $[22] = choice.value;
414
+ $[23] = linked;
415
+ $[24] = t2;
416
+ $[25] = t3;
417
+ $[26] = t5;
418
+ $[27] = t8;
419
+ $[28] = t9;
420
+ } else t9 = $[28];
421
+ return t9;
422
+ }
423
+ //#endregion
424
+ //#region src/switcher/rows.tsx
425
+ /**
426
+ * Renders a radio row per choice, checked for the current one, and the actions after a separator.
427
+ *
428
+ * @param props - The choices, the current value, the check's glyph, the handler and the actions.
429
+ * @returns A radio row per choice, then a separator and the actions where there are any.
430
+ */
431
+ function Rows(t0) {
432
+ const $ = c(14);
433
+ const { checkIcon, children, choices, chosen, onChoose } = t0;
434
+ let t1;
435
+ if ($[0] !== checkIcon || $[1] !== choices || $[2] !== chosen || $[3] !== onChoose) {
436
+ let t2;
437
+ if ($[5] !== checkIcon || $[6] !== chosen || $[7] !== onChoose) {
438
+ t2 = (choice) => /* @__PURE__ */ jsx(Option, {
439
+ checked: choice.value === chosen,
440
+ checkIcon,
441
+ choice,
442
+ onChoose
443
+ }, choice.value);
444
+ $[5] = checkIcon;
445
+ $[6] = chosen;
446
+ $[7] = onChoose;
447
+ $[8] = t2;
448
+ } else t2 = $[8];
449
+ t1 = choices.map(t2);
450
+ $[0] = checkIcon;
451
+ $[1] = choices;
452
+ $[2] = chosen;
453
+ $[3] = onChoose;
454
+ $[4] = t1;
455
+ } else t1 = $[4];
456
+ let t2;
457
+ if ($[9] !== children) {
458
+ t2 = children === void 0 ? null : /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Menu.Separator, {}), children] });
459
+ $[9] = children;
460
+ $[10] = t2;
461
+ } else t2 = $[10];
462
+ let t3;
463
+ if ($[11] !== t1 || $[12] !== t2) {
464
+ t3 = /* @__PURE__ */ jsxs(Fragment, { children: [t1, t2] });
465
+ $[11] = t1;
466
+ $[12] = t2;
467
+ $[13] = t3;
468
+ } else t3 = $[13];
469
+ return t3;
470
+ }
471
+ //#endregion
472
+ //#region src/switcher/submenu.tsx
473
+ /**
474
+ * Positioning of the submenu, which covers part of the menu where the window has no room beside
475
+ * it, as in a phone-width window.
476
+ */
477
+ const OVERLAPPING = { overlap: true };
478
+ /**
479
+ * Renders the row and the submenu it opens.
480
+ *
481
+ * @param props - The rows' props, the current choice, the indicator and the label.
482
+ * @returns The submenu's root.
483
+ */
484
+ function Submenu(t0) {
485
+ const $ = c(20);
486
+ let current;
487
+ let indicator;
488
+ let label;
489
+ let rows;
490
+ if ($[0] !== t0) {
491
+ ({current, indicator, label, ...rows} = t0);
492
+ $[0] = t0;
493
+ $[1] = current;
494
+ $[2] = indicator;
495
+ $[3] = label;
496
+ $[4] = rows;
497
+ } else {
498
+ current = $[1];
499
+ indicator = $[2];
500
+ label = $[3];
501
+ rows = $[4];
502
+ }
503
+ let t1;
504
+ if ($[5] !== current) {
505
+ t1 = current === void 0 ? null : /* @__PURE__ */ jsx(Menu.ItemDescription, { children: current.label });
506
+ $[5] = current;
507
+ $[6] = t1;
508
+ } else t1 = $[6];
509
+ let t2;
510
+ if ($[7] !== label || $[8] !== t1) {
511
+ t2 = /* @__PURE__ */ jsxs(Menu.ItemLines, { children: [label, t1] });
512
+ $[7] = label;
513
+ $[8] = t1;
514
+ $[9] = t2;
515
+ } else t2 = $[9];
516
+ let t3;
517
+ if ($[10] !== indicator) {
518
+ t3 = indicator === void 0 ? null : /* @__PURE__ */ jsx(Menu.Indicator, { children: indicator });
519
+ $[10] = indicator;
520
+ $[11] = t3;
521
+ } else t3 = $[11];
522
+ let t4;
523
+ if ($[12] !== t2 || $[13] !== t3) {
524
+ t4 = /* @__PURE__ */ jsxs(Menu.TriggerItem, { children: [t2, t3] });
525
+ $[12] = t2;
526
+ $[13] = t3;
527
+ $[14] = t4;
528
+ } else t4 = $[14];
529
+ let t5;
530
+ if ($[15] !== rows) {
531
+ t5 = /* @__PURE__ */ jsx(Portal, { children: /* @__PURE__ */ jsx(Menu.Positioner, { children: /* @__PURE__ */ jsx(Menu.Content, { children: /* @__PURE__ */ jsx(Rows, { ...rows }) }) }) });
532
+ $[15] = rows;
533
+ $[16] = t5;
534
+ } else t5 = $[16];
535
+ let t6;
536
+ if ($[17] !== t4 || $[18] !== t5) {
537
+ t6 = /* @__PURE__ */ jsxs(Menu.Root, {
538
+ positioning: OVERLAPPING,
539
+ children: [t4, t5]
540
+ });
541
+ $[17] = t4;
542
+ $[18] = t5;
543
+ $[19] = t6;
544
+ } else t6 = $[19];
545
+ return t6;
546
+ }
547
+ //#endregion
548
+ //#region src/toolbar/state.ts
549
+ /**
550
+ * Provides the toolbar's measured width, the controls it moves into its menu and its size to the
551
+ * controls in it.
552
+ *
553
+ * @remarks
554
+ * An action reads the width and the moved ids to fold itself into the row's menu, and the size to
555
+ * render the library's button at the toolbar's size.
556
+ */
557
+ /**
558
+ * Creates the context through which the root provides the toolbar state, with a reader that throws
559
+ * outside a toolbar and one that returns `undefined` there.
560
+ */
561
+ const [ToolbarProvider, useToolbar, useEnclosingToolbar] = createRequiredContext("Toolbar");
562
+ //#endregion
563
+ //#region src/switcher/moved.tsx
564
+ /**
565
+ * Moves a switcher into its toolbar's menu while the narrow row has no room for it.
566
+ */
567
+ /**
568
+ * Registers the switcher's row with the toolbar's menu while the row moves the switcher there.
569
+ *
570
+ * @remarks
571
+ * The toolbar moves only a control that wrote its id in `data-folds`, which a switcher's trigger
572
+ * does inside a toolbar. Outside a toolbar the switcher is never moved. The registration takes
573
+ * the row again after every render of a moved switcher, so the row shows the current choice.
574
+ * @param id - The id the switcher's trigger writes in `data-folds`.
575
+ * @param submenu - The props of the row that opens the switcher's choices.
576
+ * @returns `true` while the toolbar moves the switcher into its menu.
577
+ */
578
+ function useMoved(id, submenu) {
579
+ const $ = c(5);
580
+ const moved = useEnclosingToolbar()?.moved.has(id) === true;
581
+ let t0;
582
+ if ($[0] !== submenu) {
583
+ t0 = /* @__PURE__ */ jsx(Submenu, { ...submenu });
584
+ $[0] = submenu;
585
+ $[1] = t0;
586
+ } else t0 = $[1];
587
+ let t1;
588
+ if ($[2] !== submenu.label || $[3] !== t0) {
589
+ t1 = {
590
+ label: submenu.label,
591
+ row: t0
592
+ };
593
+ $[2] = submenu.label;
594
+ $[3] = t0;
595
+ $[4] = t1;
596
+ } else t1 = $[4];
597
+ useFoldable(moved, t1, id);
598
+ return moved;
599
+ }
600
+ //#endregion
601
+ //#region src/switcher/state.ts
602
+ /**
603
+ * Derives where a switcher renders from the sidebar or the toolbar around it, and provides it to
604
+ * the switcher's parts.
605
+ *
606
+ * @remarks
607
+ * A switcher in a sidebar is a row at the sidebar's size, and its mark alone while the sidebar
608
+ * is a rail. A switcher in a toolbar is one of the row's controls at the toolbar's size, and a
609
+ * switcher with a mark hides its name while the toolbar is narrow. Anywhere else it is one line
610
+ * at its own size. A caller's `placement` or `size` applies over the derived one.
611
+ */
612
+ /**
613
+ * Maps a toolbar's size to the nearest switcher size: `sm` up to `sm`, and `lg` from `lg`.
614
+ */
615
+ const SIZES = {
616
+ "2xl": "lg",
617
+ "3xl": "lg",
618
+ "4xl": "lg",
619
+ lg: "lg",
620
+ md: "md",
621
+ sm: "sm",
622
+ xl: "lg",
623
+ xs: "sm"
624
+ };
625
+ /**
626
+ * Provides the switcher's state and reads it, throwing outside `Switcher.Root`.
627
+ */
628
+ const [SwitcherProvider, useSwitcher] = createRequiredContext("Switcher.Root");
629
+ /**
630
+ * Returns the placement a switcher takes from what is around it.
631
+ *
632
+ * @param inSidebar - Whether a sidebar is around the switcher.
633
+ * @param inToolbar - Whether a toolbar is around the switcher.
634
+ * @returns `sidebar`, `toolbar` or `alone`, the nearest sidebar first.
635
+ */
636
+ function placementOf(inSidebar, inToolbar) {
637
+ if (inSidebar) return "sidebar";
638
+ if (inToolbar) return "toolbar";
639
+ return "alone";
640
+ }
641
+ /**
642
+ * Returns the state of a switcher in a sidebar: a row at the sidebar's size, iconic on a rail.
643
+ *
644
+ * @param sidebar - The sidebar around the switcher, if any.
645
+ * @param size - The caller's size.
646
+ * @returns The state.
647
+ */
648
+ function rowOf(sidebar, size) {
649
+ return {
650
+ iconic: sidebar?.iconic ?? false,
651
+ narrow: false,
652
+ placement: "sidebar",
653
+ roving: false,
654
+ size: size ?? sidebar?.size ?? "md"
655
+ };
656
+ }
657
+ /**
658
+ * Returns the state of a switcher in a toolbar: a control at the toolbar's size, narrow with it.
659
+ *
660
+ * @param toolbar - The toolbar around the switcher, if any.
661
+ * @param size - The caller's size.
662
+ * @returns The state.
663
+ */
664
+ function controlOf(toolbar, size) {
665
+ return {
666
+ iconic: false,
667
+ narrow: toolbar?.narrow ?? false,
668
+ placement: "toolbar",
669
+ roving: toolbar !== void 0,
670
+ size: size ?? SIZES[toolbar?.size ?? "md"]
671
+ };
672
+ }
673
+ /**
674
+ * Returns where a switcher renders, from the caller's settings and the sidebar or toolbar around
675
+ * it.
676
+ *
677
+ * @param placement - The caller's placement, which applies over the derived one.
678
+ * @param size - The caller's size, which applies over the derived one.
679
+ * @returns The placement, the size, and whether the switcher is iconic or narrow.
680
+ */
681
+ function usePlaced(placement, size) {
682
+ const $ = c(13);
683
+ const sidebar = useEnclosingSidebar();
684
+ const toolbar = useEnclosingToolbar();
685
+ let t0;
686
+ if ($[0] !== placement || $[1] !== sidebar || $[2] !== toolbar) {
687
+ t0 = placement ?? placementOf(sidebar !== void 0, toolbar !== void 0);
688
+ $[0] = placement;
689
+ $[1] = sidebar;
690
+ $[2] = toolbar;
691
+ $[3] = t0;
692
+ } else t0 = $[3];
693
+ const placed = t0;
694
+ if (placed === "sidebar") {
695
+ let t1;
696
+ if ($[4] !== sidebar || $[5] !== size) {
697
+ t1 = rowOf(sidebar, size);
698
+ $[4] = sidebar;
699
+ $[5] = size;
700
+ $[6] = t1;
701
+ } else t1 = $[6];
702
+ return t1;
703
+ }
704
+ if (placed === "toolbar") {
705
+ let t1;
706
+ if ($[7] !== size || $[8] !== toolbar) {
707
+ t1 = controlOf(toolbar, size);
708
+ $[7] = size;
709
+ $[8] = toolbar;
710
+ $[9] = t1;
711
+ } else t1 = $[9];
712
+ return t1;
713
+ }
714
+ const t1 = size ?? "md";
715
+ let t2;
716
+ if ($[10] !== placed || $[11] !== t1) {
717
+ t2 = {
718
+ iconic: false,
719
+ narrow: false,
720
+ placement: placed,
721
+ roving: false,
722
+ size: t1
723
+ };
724
+ $[10] = placed;
725
+ $[11] = t1;
726
+ $[12] = t2;
727
+ } else t2 = $[12];
728
+ return t2;
729
+ }
730
+ //#endregion
731
+ //#region src/switcher/trigger.tsx
732
+ /**
733
+ * Renders the menu's trigger with the root slot's class.
734
+ */
735
+ const Pressed = withContext(Menu.Trigger, "root");
736
+ /**
737
+ * Renders the control with its label before its children.
738
+ *
739
+ * @param props - `label` and the menu trigger's props.
740
+ * @returns The menu's trigger with the hidden label before the children.
741
+ */
742
+ function Trigger(t0) {
743
+ const $ = c(12);
744
+ let children;
745
+ let label;
746
+ let rest;
747
+ if ($[0] !== t0) {
748
+ ({children, label, ...rest} = t0);
749
+ $[0] = t0;
750
+ $[1] = children;
751
+ $[2] = label;
752
+ $[3] = rest;
753
+ } else {
754
+ children = $[1];
755
+ label = $[2];
756
+ rest = $[3];
757
+ }
758
+ const { iconic, narrow } = useSwitcher();
759
+ const t1 = iconic ? "" : void 0;
760
+ const t2 = narrow ? "" : void 0;
761
+ let t3;
762
+ if ($[4] !== label) {
763
+ t3 = /* @__PURE__ */ jsx(VisuallyHidden, { children: label });
764
+ $[4] = label;
765
+ $[5] = t3;
766
+ } else t3 = $[5];
767
+ let t4;
768
+ if ($[6] !== children || $[7] !== rest || $[8] !== t1 || $[9] !== t2 || $[10] !== t3) {
769
+ t4 = /* @__PURE__ */ jsxs(Pressed, {
770
+ ...rest,
771
+ "data-iconic": t1,
772
+ "data-narrow": t2,
773
+ children: [t3, children]
774
+ });
775
+ $[6] = children;
776
+ $[7] = rest;
777
+ $[8] = t1;
778
+ $[9] = t2;
779
+ $[10] = t3;
780
+ $[11] = t4;
781
+ } else t4 = $[11];
782
+ return t4;
783
+ }
784
+ //#endregion
785
+ //#region src/switcher/placed.tsx
786
+ /**
787
+ * Placement of the tooltip: beside the rail, towards the page.
788
+ */
789
+ const BESIDE = { placement: "right" };
790
+ /**
791
+ * Renders the trigger as a toolbar control, with a tooltip on a rail, or on its own.
792
+ *
793
+ * @param props - The trigger's content and label, the current name and the trigger's id.
794
+ * @returns The trigger in its placement's form.
795
+ */
796
+ function Placed(t0) {
797
+ const $ = c(22);
798
+ const { children, folds, label, name, triggerId } = t0;
799
+ const { iconic, roving } = useSwitcher();
800
+ if (roving) {
801
+ let t1;
802
+ if ($[0] !== folds) {
803
+ t1 = { [FOLDS]: folds };
804
+ $[0] = folds;
805
+ $[1] = t1;
806
+ } else t1 = $[1];
807
+ let t2;
808
+ if ($[2] !== children || $[3] !== label || $[4] !== t1) {
809
+ t2 = /* @__PURE__ */ jsx(Item, {
810
+ as: Trigger,
811
+ label,
812
+ ...t1,
813
+ children
814
+ });
815
+ $[2] = children;
816
+ $[3] = label;
817
+ $[4] = t1;
818
+ $[5] = t2;
819
+ } else t2 = $[5];
820
+ return t2;
821
+ }
822
+ if (!iconic || name === void 0) {
823
+ let t1;
824
+ if ($[6] !== children || $[7] !== label) {
825
+ t1 = /* @__PURE__ */ jsx(Trigger, {
826
+ label,
827
+ children
828
+ });
829
+ $[6] = children;
830
+ $[7] = label;
831
+ $[8] = t1;
832
+ } else t1 = $[8];
833
+ return t1;
834
+ }
835
+ let t1;
836
+ if ($[9] !== triggerId) {
837
+ t1 = { trigger: triggerId };
838
+ $[9] = triggerId;
839
+ $[10] = t1;
840
+ } else t1 = $[10];
841
+ let t2;
842
+ if ($[11] !== label) {
843
+ t2 = { label };
844
+ $[11] = label;
845
+ $[12] = t2;
846
+ } else t2 = $[12];
847
+ let t3;
848
+ if ($[13] !== children || $[14] !== t2) {
849
+ t3 = /* @__PURE__ */ jsx(Tooltip.Trigger, {
850
+ as: Trigger,
851
+ ...t2,
852
+ children
853
+ });
854
+ $[13] = children;
855
+ $[14] = t2;
856
+ $[15] = t3;
857
+ } else t3 = $[15];
858
+ let t4;
859
+ if ($[16] !== name) {
860
+ t4 = /* @__PURE__ */ jsx(Portal, { children: /* @__PURE__ */ jsx(Tooltip.Positioner, { children: /* @__PURE__ */ jsx(Tooltip.Content, { children: name }) }) });
861
+ $[16] = name;
862
+ $[17] = t4;
863
+ } else t4 = $[17];
864
+ let t5;
865
+ if ($[18] !== t1 || $[19] !== t3 || $[20] !== t4) {
866
+ t5 = /* @__PURE__ */ jsxs(Tooltip.Root, {
867
+ ids: t1,
868
+ positioning: BESIDE,
869
+ children: [t3, t4]
870
+ });
871
+ $[18] = t1;
872
+ $[19] = t3;
873
+ $[20] = t4;
874
+ $[21] = t5;
875
+ } else t5 = $[21];
876
+ return t5;
877
+ }
878
+ //#endregion
879
+ //#region src/switcher/listed.tsx
880
+ /**
881
+ * Renders a switcher from a list of choices: the trigger with the current choice, and the menu of
882
+ * every choice with the caller's actions after a separator.
883
+ *
884
+ * @remarks
885
+ * The current choice is the caller's `value`, or the component's own state from `defaultValue`,
886
+ * which defaults to the first choice. The trigger shows the current choice's mark, its name and
887
+ * its detail. The glyphs are the caller's: `indicator` at the trigger's end and `checkIcon` on
888
+ * the current row. A narrow toolbar that has no room for the switcher renders no trigger, and its
889
+ * menu lists a row that opens the same choices.
890
+ */
891
+ /**
892
+ * Renders the trigger and the menu of the choices.
893
+ *
894
+ * @param props - The choices, the current value, the glyphs, the label and the actions.
895
+ * @returns The trigger and the portalled menu.
896
+ */
897
+ function Listed(t0) {
898
+ const $ = c(32);
899
+ const { checkIcon, children, choices, defaultValue, indicator, label, onValueChange, triggerId, value } = t0;
900
+ const id = useId();
901
+ const t1 = defaultValue ?? choices[0]?.value ?? "";
902
+ let t2;
903
+ if ($[0] !== onValueChange || $[1] !== t1 || $[2] !== value) {
904
+ t2 = {
905
+ defaultValue: t1,
906
+ onChange: onValueChange,
907
+ value
908
+ };
909
+ $[0] = onValueChange;
910
+ $[1] = t1;
911
+ $[2] = value;
912
+ $[3] = t2;
913
+ } else t2 = $[3];
914
+ const [chosen, setChosen] = useControllableState(t2);
915
+ let t3;
916
+ if ($[4] !== choices || $[5] !== chosen) {
917
+ let t4;
918
+ if ($[7] !== chosen) {
919
+ t4 = (choice) => choice.value === chosen;
920
+ $[7] = chosen;
921
+ $[8] = t4;
922
+ } else t4 = $[8];
923
+ t3 = choices.find(t4);
924
+ $[4] = choices;
925
+ $[5] = chosen;
926
+ $[6] = t3;
927
+ } else t3 = $[6];
928
+ const current = t3;
929
+ let t4;
930
+ if ($[9] !== checkIcon || $[10] !== children || $[11] !== choices || $[12] !== chosen || $[13] !== setChosen) {
931
+ t4 = {
932
+ checkIcon,
933
+ children,
934
+ choices,
935
+ chosen,
936
+ onChoose: setChosen
937
+ };
938
+ $[9] = checkIcon;
939
+ $[10] = children;
940
+ $[11] = choices;
941
+ $[12] = chosen;
942
+ $[13] = setChosen;
943
+ $[14] = t4;
944
+ } else t4 = $[14];
945
+ const rows = t4;
946
+ let t5;
947
+ if ($[15] !== current || $[16] !== indicator || $[17] !== label || $[18] !== rows) {
948
+ t5 = {
949
+ ...rows,
950
+ current,
951
+ indicator,
952
+ label
953
+ };
954
+ $[15] = current;
955
+ $[16] = indicator;
956
+ $[17] = label;
957
+ $[18] = rows;
958
+ $[19] = t5;
959
+ } else t5 = $[19];
960
+ const moved = useMoved(id, t5);
961
+ let t6;
962
+ if ($[20] !== current || $[21] !== id || $[22] !== indicator || $[23] !== label || $[24] !== moved || $[25] !== triggerId) {
963
+ t6 = moved ? null : /* @__PURE__ */ jsxs(Placed, {
964
+ folds: id,
965
+ label,
966
+ name: current?.label,
967
+ triggerId,
968
+ children: [current === void 0 ? null : /* @__PURE__ */ jsx(Current, { choice: current }), indicator === void 0 ? null : /* @__PURE__ */ jsx(Indicator, { children: indicator })]
969
+ });
970
+ $[20] = current;
971
+ $[21] = id;
972
+ $[22] = indicator;
973
+ $[23] = label;
974
+ $[24] = moved;
975
+ $[25] = triggerId;
976
+ $[26] = t6;
977
+ } else t6 = $[26];
978
+ let t7;
979
+ if ($[27] !== rows) {
980
+ t7 = /* @__PURE__ */ jsx(Portal, { children: /* @__PURE__ */ jsx(Menu.Positioner, { children: /* @__PURE__ */ jsx(Menu.Content, { children: /* @__PURE__ */ jsx(Rows, { ...rows }) }) }) });
981
+ $[27] = rows;
982
+ $[28] = t7;
983
+ } else t7 = $[28];
984
+ let t8;
985
+ if ($[29] !== t6 || $[30] !== t7) {
986
+ t8 = /* @__PURE__ */ jsxs(Fragment, { children: [t6, t7] });
987
+ $[29] = t6;
988
+ $[30] = t7;
989
+ $[31] = t8;
990
+ } else t8 = $[31];
991
+ return t8;
992
+ }
993
+ //#endregion
994
+ //#region src/switcher/sized.tsx
995
+ /**
996
+ * Renders the menu at the step.
997
+ *
998
+ * @param props - The step and the menu's props.
999
+ * @returns The menu at that size.
1000
+ */
1001
+ function Sized(t0) {
1002
+ const $ = c(6);
1003
+ let rest;
1004
+ let step;
1005
+ if ($[0] !== t0) {
1006
+ ({step, ...rest} = t0);
1007
+ $[0] = t0;
1008
+ $[1] = rest;
1009
+ $[2] = step;
1010
+ } else {
1011
+ rest = $[1];
1012
+ step = $[2];
1013
+ }
1014
+ let t1;
1015
+ if ($[3] !== rest || $[4] !== step) {
1016
+ t1 = /* @__PURE__ */ jsx(Menu.Root, {
1017
+ ...rest,
1018
+ size: step
1019
+ });
1020
+ $[3] = rest;
1021
+ $[4] = step;
1022
+ $[5] = t1;
1023
+ } else t1 = $[5];
1024
+ return t1;
1025
+ }
1026
+ //#endregion
1027
+ //#region src/switcher/root.tsx
1028
+ /**
1029
+ * Renders the switcher's menu, derives where the switcher renders, and provides both to the
1030
+ * control's parts.
1031
+ *
1032
+ * @remarks
1033
+ * `Switcher.Root` is the disclosure package's `Menu.Root`, so the roles, the keyboard, Escape and
1034
+ * the positioning come from the menu. With `items` the root renders the trigger and the menu of
1035
+ * the choices itself, and its children are `Switcher.Action` rows after them. Without `items`
1036
+ * the children are the caller's trigger and menu. The root reads the sidebar or the toolbar
1037
+ * around it for the placement and the size, unless the caller states them, and passes the size
1038
+ * to the menu, so the rows render at the control's size. In a sidebar the menu is as wide as the
1039
+ * control, and on a rail it opens beside the rail.
1040
+ */
1041
+ /**
1042
+ * The binding, with its props typed as `ProvidedProps`.
1043
+ *
1044
+ * @remarks
1045
+ * The binding types a variant without `undefined`, and under `exactOptionalPropertyTypes` an
1046
+ * omitted variant arrives as `undefined`, which the recipe's props allow. The runtime is the
1047
+ * same: the binding splits the switcher's variants off before the menu receives the rest.
1048
+ */
1049
+ const Provided = withRootProvider(Sized);
1050
+ /**
1051
+ * Renders the menu with the switcher's variants, at the switcher's size.
1052
+ *
1053
+ * @remarks
1054
+ * A caller's `positioning` applies over the placement's, and a caller's `ids` over the trigger id
1055
+ * the root shares with the rail's tooltip.
1056
+ * @param props - The choices, the glyphs, the switcher's variants and the menu's props.
1057
+ * @returns The menu, providing the variants and the placement.
1058
+ */
1059
+ function Root(t0) {
1060
+ const $ = c(39);
1061
+ let checkIcon;
1062
+ let children;
1063
+ let defaultValue;
1064
+ let indicator;
1065
+ let items;
1066
+ let onValueChange;
1067
+ let placement;
1068
+ let positioning;
1069
+ let rest;
1070
+ let size;
1071
+ let t1;
1072
+ let value;
1073
+ if ($[0] !== t0) {
1074
+ ({checkIcon, children, defaultValue, indicator, items, label: t1, onValueChange, placement, positioning, size, value, ...rest} = t0);
1075
+ $[0] = t0;
1076
+ $[1] = checkIcon;
1077
+ $[2] = children;
1078
+ $[3] = defaultValue;
1079
+ $[4] = indicator;
1080
+ $[5] = items;
1081
+ $[6] = onValueChange;
1082
+ $[7] = placement;
1083
+ $[8] = positioning;
1084
+ $[9] = rest;
1085
+ $[10] = size;
1086
+ $[11] = t1;
1087
+ $[12] = value;
1088
+ } else {
1089
+ checkIcon = $[1];
1090
+ children = $[2];
1091
+ defaultValue = $[3];
1092
+ indicator = $[4];
1093
+ items = $[5];
1094
+ onValueChange = $[6];
1095
+ placement = $[7];
1096
+ positioning = $[8];
1097
+ rest = $[9];
1098
+ size = $[10];
1099
+ t1 = $[11];
1100
+ value = $[12];
1101
+ }
1102
+ const label = t1 === void 0 ? "" : t1;
1103
+ const state = usePlaced(placement, size);
1104
+ const triggerId = useId();
1105
+ const row = state.placement === "sidebar";
1106
+ let t2;
1107
+ if ($[13] !== triggerId) {
1108
+ t2 = { trigger: triggerId };
1109
+ $[13] = triggerId;
1110
+ $[14] = t2;
1111
+ } else t2 = $[14];
1112
+ const t3 = state.iconic ? "right-start" : "bottom-start";
1113
+ const t4 = row && !state.iconic;
1114
+ let t5;
1115
+ if ($[15] !== positioning || $[16] !== t3 || $[17] !== t4) {
1116
+ t5 = {
1117
+ placement: t3,
1118
+ sameWidth: t4,
1119
+ ...positioning
1120
+ };
1121
+ $[15] = positioning;
1122
+ $[16] = t3;
1123
+ $[17] = t4;
1124
+ $[18] = t5;
1125
+ } else t5 = $[18];
1126
+ let t6;
1127
+ if ($[19] !== checkIcon || $[20] !== children || $[21] !== defaultValue || $[22] !== indicator || $[23] !== items || $[24] !== label || $[25] !== onValueChange || $[26] !== triggerId || $[27] !== value) {
1128
+ t6 = items === void 0 ? children : /* @__PURE__ */ jsx(Listed, {
1129
+ checkIcon,
1130
+ choices: items,
1131
+ defaultValue,
1132
+ indicator,
1133
+ label,
1134
+ onValueChange,
1135
+ triggerId,
1136
+ value,
1137
+ children
1138
+ });
1139
+ $[19] = checkIcon;
1140
+ $[20] = children;
1141
+ $[21] = defaultValue;
1142
+ $[22] = indicator;
1143
+ $[23] = items;
1144
+ $[24] = label;
1145
+ $[25] = onValueChange;
1146
+ $[26] = triggerId;
1147
+ $[27] = value;
1148
+ $[28] = t6;
1149
+ } else t6 = $[28];
1150
+ let t7;
1151
+ if ($[29] !== rest || $[30] !== state.placement || $[31] !== state.size || $[32] !== t2 || $[33] !== t5 || $[34] !== t6) {
1152
+ t7 = /* @__PURE__ */ jsx(Provided, {
1153
+ ids: t2,
1154
+ ...rest,
1155
+ placement: state.placement,
1156
+ positioning: t5,
1157
+ size: state.size,
1158
+ step: state.size,
1159
+ children: t6
1160
+ });
1161
+ $[29] = rest;
1162
+ $[30] = state.placement;
1163
+ $[31] = state.size;
1164
+ $[32] = t2;
1165
+ $[33] = t5;
1166
+ $[34] = t6;
1167
+ $[35] = t7;
1168
+ } else t7 = $[35];
1169
+ let t8;
1170
+ if ($[36] !== state || $[37] !== t7) {
1171
+ t8 = /* @__PURE__ */ jsx(SwitcherProvider, {
1172
+ value: state,
1173
+ children: t7
1174
+ });
1175
+ $[36] = state;
1176
+ $[37] = t7;
1177
+ $[38] = t8;
1178
+ } else t8 = $[38];
1179
+ return t8;
1180
+ }
1181
+ //#endregion
1182
+ export { useFolded as _, Name as a, Indicator as c, SidebarProvider as d, fieldSizeOf as f, useFoldable as g, FoldContext as h, useToolbar as i, Detail as l, useSidebar as m, Trigger as n, Mark as o, useNav as p, ToolbarProvider as r, Label as s, Root as t, NavProvider as u, Item as v };