@stealthscale/component-disclosure 0.1.0 → 0.2.3

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.
@@ -1,708 +0,0 @@
1
- import { below, controlSizes, defineSlotRecipe, divider, highlightVariants, iconSizes, insetSizes, interactive, justifyVariants, motion, onSlot, onSlots, row, sizeVariants, textSizes, truncate } from "@stealthscale/theme/authoring";
2
- //#region src/collapsible/recipe.ts
3
- /**
4
- * States what a collapsible is: a control that shows and hides the block beneath it.
5
- *
6
- * @remarks
7
- * Four parts. The root frames the pair, the trigger is what a person presses, the content is what
8
- * appears, and the indicator is the mark that turns as it does.
9
- * The trigger fills the width it is given and pushes its indicator to the end, because a
10
- * disclosure is read as a row and a mark floating beside the words reads as part of them. It
11
- * reads the control scale, so a collapsible lines up with a button of the same name beside it.
12
- * The content animates from the height the machine measures. Both motions are animation styles
13
- * the theme owns, so a reader who asked for less motion is answered in the theme rather than
14
- * here, and the height the animation runs to is a custom property the machine sets.
15
- */
16
- /**
17
- * Draws an unframed collapsible at the middle size, sliding open, until a caller says otherwise.
18
- */
19
- const recipe$4 = defineSlotRecipe({
20
- base: {
21
- content: { overflow: "hidden" },
22
- indicator: {
23
- _motionReduce: { transitionDuration: "none" },
24
- _open: { rotate: "180deg" },
25
- alignItems: "center",
26
- display: "inline-flex",
27
- flexShrink: "0",
28
- justifyContent: "center",
29
- transitionDuration: "fast",
30
- transitionProperty: "transform",
31
- transitionTimingFunction: "out"
32
- },
33
- root: { width: "full" },
34
- trigger: {
35
- ...interactive(),
36
- alignItems: "center",
37
- display: "flex",
38
- justifyContent: "space-between",
39
- textAlign: "start",
40
- width: "full"
41
- }
42
- },
43
- className: "collapsible",
44
- defaultVariants: {
45
- motion: "slide",
46
- size: "md",
47
- variant: "plain"
48
- },
49
- jsx: [/^Collapsible(\.\w+)?$/u],
50
- slots: [
51
- "root",
52
- "trigger",
53
- "content",
54
- "indicator"
55
- ],
56
- variants: {
57
- /**
58
- * How the block appears and goes.
59
- */
60
- motion: {
61
- fade: { content: {
62
- _closed: { animationStyle: "fade.out" },
63
- _open: { animationStyle: "fade.in" }
64
- } },
65
- none: { content: { animation: "none" } },
66
- slide: { content: {
67
- _closed: { animationStyle: "collapse.out" },
68
- _open: { animationStyle: "collapse.in" }
69
- } }
70
- },
71
- /**
72
- * How much room the pair takes, which the trigger, the block and the mark step together.
73
- */
74
- size: onSlots({
75
- content: insetSizes(),
76
- indicator: iconSizes(),
77
- trigger: controlSizes()
78
- }),
79
- /**
80
- * How the pair is set off from the page around it.
81
- */
82
- variant: {
83
- outline: {
84
- root: {
85
- borderColor: "border",
86
- borderRadius: "l2",
87
- borderWidth: "sm"
88
- },
89
- trigger: { _open: {
90
- borderBlockEndColor: "border",
91
- borderBlockEndWidth: "sm"
92
- } }
93
- },
94
- plain: { root: { borderWidth: "0" } },
95
- subtle: {
96
- root: {
97
- background: "bg.muted",
98
- borderRadius: "l2"
99
- },
100
- trigger: { _hover: { background: "bg.emphasized" } }
101
- },
102
- surface: {
103
- root: {
104
- background: "bg.panel",
105
- borderColor: "border",
106
- borderRadius: "l2",
107
- borderWidth: "sm"
108
- },
109
- trigger: { _open: {
110
- borderBlockEndColor: "border",
111
- borderBlockEndWidth: "sm"
112
- } }
113
- }
114
- }
115
- }
116
- });
117
- //#endregion
118
- //#region src/menu/recipe.ts
119
- /**
120
- * States what a menu is: a list of things a reader chooses from, opened from a control and closed
121
- * by choosing.
122
- *
123
- * @remarks
124
- * The machine names no root, because a menu is a control and a panel that floats beside it rather
125
- * than a thing that frames the two. This recipe adds one anyway, drawn with `display: contents`
126
- * so it takes part in no layout, because the control and the panel are siblings and a slot recipe
127
- * hands its variants down from an element above them both. The same root carries a submenu, whose
128
- * control sits among the rows of the menu above it.
129
- * The panel is placed by the machine, which writes its position as inline styles beside four
130
- * custom properties the positioner is measured into. The panel reads one of them for its height,
131
- * so a menu opened near the edge of a window scrolls inside itself rather than running off the
132
- * page. It states no width: the machine sets `max-content`, so the panel is as wide as its widest
133
- * row, and a caller wanting the control's width asks the machine for it.
134
- * The panel enters from the side it was placed on rather than always from the top, which is what
135
- * the `slide-fade` motion reads off the placement the machine writes.
136
- * A row that carries a mark leaves room for one in a gutter the size axis measures, and every row
137
- * of a menu that offers marks leaves the same gutter, so a list of options does not step sideways
138
- * as the marks appear.
139
- */
140
- /**
141
- * Lists the three steps a menu is offered at, which is what a list of rows needs and no more.
142
- */
143
- const SIZES = [
144
- "sm",
145
- "md",
146
- "lg"
147
- ];
148
- /**
149
- * Fixes the property the gutter for a row's mark is measured into, which the size axis writes and
150
- * the inset axis and every marked row read.
151
- */
152
- const GUTTER = "--menu-gutter";
153
- /**
154
- * Fixes the property the panel's fill is stated in, which the arrow reads so it is drawn in the
155
- * same fill as the panel it points away from.
156
- */
157
- const SURFACE = "var(--menu-surface)";
158
- /**
159
- * Measures the gutter one step leaves for a mark: the room at the panel's edge, the mark itself,
160
- * and the gap between the mark and the words beside it.
161
- */
162
- function gutter(size) {
163
- return `calc({spacing.inset.${size}} + {sizes.icon.${size}} + {spacing.gap.${size}})`;
164
- }
165
- /**
166
- * Draws a surfaced menu at the middle size, tinting the row the reader is on, until a caller says
167
- * otherwise.
168
- */
169
- const recipe$3 = defineSlotRecipe({
170
- base: {
171
- arrow: {
172
- "--arrow-background": SURFACE,
173
- "--arrow-size": "sizes.icon.sm"
174
- },
175
- arrowTip: {
176
- borderInlineStartWidth: "sm",
177
- borderTopWidth: "sm"
178
- },
179
- content: {
180
- ...motion("slide-fade.in", "slide-fade.out"),
181
- _focusVisible: { focusVisibleRing: "outside" },
182
- display: "flex",
183
- flexDirection: "column",
184
- maxBlockSize: "var(--available-height)",
185
- outline: "0",
186
- overflowY: "auto",
187
- overscrollBehavior: "contain",
188
- zIndex: "dropdown"
189
- },
190
- contextTrigger: { cursor: "menuitem" },
191
- indicator: {
192
- _open: { rotate: "180deg" },
193
- transitionDuration: "fast"
194
- },
195
- item: {
196
- ...row(),
197
- "&[data-tone=critical]": {
198
- color: "colorPalette.fg",
199
- colorPalette: "error"
200
- },
201
- [`&[data-type]`]: { paddingInlineStart: `var(${GUTTER})` },
202
- colorPalette: "neutral"
203
- },
204
- itemGroup: {
205
- display: "flex",
206
- flexDirection: "column"
207
- },
208
- itemGroupLabel: {
209
- color: "fg.muted",
210
- fontWeight: "semibold"
211
- },
212
- itemIndicator: {
213
- alignItems: "center",
214
- display: "inline-flex",
215
- insetBlockStart: "50%",
216
- justifyContent: "center",
217
- position: "absolute",
218
- translate: "0 -50%"
219
- },
220
- itemText: {
221
- ...truncate(),
222
- flex: "1"
223
- },
224
- positioner: { position: "relative" },
225
- root: { display: "contents" },
226
- separator: {
227
- ...divider(),
228
- borderColor: "border.muted"
229
- },
230
- trigger: { ...interactive() },
231
- triggerItem: {
232
- ...row(),
233
- colorPalette: "neutral"
234
- }
235
- },
236
- className: "menu",
237
- defaultVariants: {
238
- highlight: "tint",
239
- size: "md",
240
- variant: "surface"
241
- },
242
- jsx: [/^Menu(\.\w+)?$/u],
243
- slots: [
244
- "root",
245
- "trigger",
246
- "contextTrigger",
247
- "triggerItem",
248
- "indicator",
249
- "positioner",
250
- "content",
251
- "itemGroup",
252
- "itemGroupLabel",
253
- "item",
254
- "itemText",
255
- "itemIndicator",
256
- "separator",
257
- "arrow",
258
- "arrowTip"
259
- ],
260
- variants: {
261
- /**
262
- * How the row the reader is on is marked.
263
- */
264
- highlight: onSlots({
265
- item: highlightVariants(),
266
- triggerItem: highlightVariants()
267
- }),
268
- /**
269
- * Whether every row leaves the gutter a mark sits in, for a menu whose rows lead with an icon.
270
- */
271
- inset: { true: {
272
- item: { paddingInlineStart: `var(${GUTTER})` },
273
- triggerItem: { paddingInlineStart: `var(${GUTTER})` }
274
- } },
275
- /**
276
- * How much room a row takes, and how loud its words are.
277
- */
278
- size: onSlots({
279
- content: sizeVariants((size) => ({
280
- padding: `gap.${below(size)}`,
281
- scrollPadding: `gap.${below(size)}`
282
- }), SIZES),
283
- item: sizeVariants((size) => ({
284
- gap: `gap.${below(size)}`,
285
- [GUTTER]: gutter(below(size)),
286
- minBlockSize: `control.${below(size)}`,
287
- paddingInline: `inset.${below(size)}`,
288
- textStyle: `label.${below(size)}`
289
- }), SIZES),
290
- itemGroupLabel: sizeVariants((size) => ({
291
- paddingBlock: `gap.${below(below(size))}`,
292
- paddingInline: `inset.${below(size)}`,
293
- textStyle: `label.${below(below(size))}`
294
- }), SIZES),
295
- itemIndicator: sizeVariants((size) => ({
296
- boxSize: `icon.${below(size)}`,
297
- insetInlineStart: `inset.${below(size)}`
298
- }), SIZES),
299
- separator: sizeVariants((size) => ({ marginBlock: `gap.${below(size)}` }), SIZES),
300
- triggerItem: sizeVariants((size) => ({
301
- gap: `gap.${below(size)}`,
302
- [GUTTER]: gutter(below(size)),
303
- minBlockSize: `control.${below(size)}`,
304
- paddingInline: `inset.${below(size)}`,
305
- textStyle: `label.${below(size)}`
306
- }), SIZES)
307
- }),
308
- /**
309
- * How the panel is set off from the page behind it.
310
- */
311
- variant: {
312
- elevated: {
313
- arrowTip: { borderColor: SURFACE },
314
- content: {
315
- "--menu-surface": "colors.bg.panel",
316
- background: SURFACE,
317
- borderRadius: "l3",
318
- boxShadow: "xl"
319
- }
320
- },
321
- glass: {
322
- arrowTip: { borderColor: "border" },
323
- content: {
324
- "--menu-surface": "colors.bg.popover",
325
- borderRadius: "l3",
326
- layerStyle: "glass"
327
- }
328
- },
329
- surface: {
330
- arrowTip: { borderColor: "border" },
331
- content: {
332
- "--menu-surface": "colors.bg.popover",
333
- background: SURFACE,
334
- borderColor: "border",
335
- borderRadius: "l3",
336
- borderWidth: "sm",
337
- boxShadow: "lg"
338
- }
339
- }
340
- }
341
- }
342
- });
343
- //#endregion
344
- //#region src/popover/recipe.ts
345
- /**
346
- * States what a popover is: a panel that opens beside a control and holds more than a few words.
347
- *
348
- * @remarks
349
- * The machine names no root, because a popover is a control and a panel that floats beside it
350
- * rather than a thing that frames the two. This recipe adds one anyway, drawn with
351
- * `display: contents` so it takes part in no layout, because the control and the panel are
352
- * siblings and a slot recipe hands its variants down from an element above them both.
353
- * The positioner is placed by the machine, which measures the control and writes the panel's
354
- * position as inline styles, so this recipe states nothing about where the panel goes. It grows
355
- * from whichever corner the machine placed it against, which is a custom property the machine
356
- * sets.
357
- * A popover is louder than a tooltip. It holds a heading, a paragraph and often a control, so it
358
- * reads at body text and takes the room a panel needs.
359
- */
360
- /**
361
- * Draws a surfaced popover at the middle size until a caller says otherwise.
362
- */
363
- const recipe$2 = defineSlotRecipe({
364
- base: {
365
- arrow: {
366
- "--arrow-background": "var(--popover-surface)",
367
- "--arrow-size": "sizes.icon.sm"
368
- },
369
- arrowTip: {
370
- borderInlineStartWidth: "sm",
371
- borderTopWidth: "sm"
372
- },
373
- closeTrigger: {
374
- ...interactive(),
375
- _hover: { color: "fg" },
376
- alignItems: "center",
377
- borderRadius: "l1",
378
- color: "fg.muted",
379
- display: "inline-flex",
380
- insetBlockStart: "0",
381
- insetInlineEnd: "0",
382
- justifyContent: "center",
383
- position: "absolute"
384
- },
385
- content: {
386
- ...motion("scale-fade.in", "scale-fade.out"),
387
- _focusVisible: { focusVisibleRing: "outside" },
388
- display: "flex",
389
- flexDirection: "column",
390
- gap: "gap.sm",
391
- position: "relative",
392
- transformOrigin: "var(--transform-origin)",
393
- zIndex: "popover"
394
- },
395
- description: { color: "fg.muted" },
396
- indicator: {
397
- _open: { rotate: "180deg" },
398
- transitionDuration: "fast"
399
- },
400
- positioner: { position: "relative" },
401
- root: { display: "contents" },
402
- title: { fontWeight: "semibold" }
403
- },
404
- className: "popover",
405
- defaultVariants: {
406
- size: "md",
407
- variant: "surface"
408
- },
409
- jsx: [/^Popover(\.\w+)?$/u],
410
- slots: [
411
- "root",
412
- "anchor",
413
- "trigger",
414
- "indicator",
415
- "positioner",
416
- "content",
417
- "title",
418
- "description",
419
- "closeTrigger",
420
- "arrow",
421
- "arrowTip"
422
- ],
423
- variants: {
424
- /**
425
- * How much room the panel takes, and how loud its heading is.
426
- */
427
- size: onSlots({
428
- content: insetSizes(),
429
- description: textSizes("body"),
430
- title: textSizes("heading")
431
- }),
432
- /**
433
- * How the panel is set off from the page behind it.
434
- */
435
- variant: {
436
- elevated: {
437
- arrowTip: { borderColor: "var(--popover-surface)" },
438
- content: {
439
- "--popover-surface": "colors.bg.panel",
440
- background: "var(--popover-surface)",
441
- borderRadius: "l3",
442
- boxShadow: "xl"
443
- }
444
- },
445
- glass: {
446
- arrowTip: { borderColor: "border" },
447
- content: {
448
- "--popover-surface": "colors.bg.popover",
449
- borderRadius: "l3",
450
- layerStyle: "glass"
451
- }
452
- },
453
- surface: {
454
- arrowTip: { borderColor: "border" },
455
- content: {
456
- "--popover-surface": "colors.bg.popover",
457
- background: "var(--popover-surface)",
458
- borderColor: "border",
459
- borderRadius: "l3",
460
- borderWidth: "sm",
461
- boxShadow: "lg"
462
- }
463
- }
464
- }
465
- }
466
- });
467
- //#endregion
468
- //#region src/tabs/recipe.ts
469
- /**
470
- * States what a set of tabs is: one strip of controls, and the panel each of them shows.
471
- *
472
- * @remarks
473
- * Five parts. The root frames the whole, the list is the strip, a trigger is one tab, the content
474
- * is a panel, and the indicator is the bar that slides under the tab in force.
475
- * Which way the set runs is the machine's, not an axis of its own. It writes the orientation onto
476
- * every part, and the compiler ships a condition that reads it, so the strip turns into a column
477
- * and the indicator moves to its inline edge without a caller stating anything twice.
478
- * The indicator is positioned from custom properties the machine measures, so the recipe states
479
- * its thickness and its colour and never its place.
480
- */
481
- /**
482
- * Draws a line of tabs at the middle size until a caller says otherwise.
483
- */
484
- const recipe$1 = defineSlotRecipe({
485
- base: {
486
- content: {
487
- _focusVisible: { focusVisibleRing: "outside" },
488
- outline: "none"
489
- },
490
- indicator: {
491
- borderRadius: "l1",
492
- zIndex: "1"
493
- },
494
- list: {
495
- _horizontal: { flexDirection: "row" },
496
- _vertical: { flexDirection: "column" },
497
- display: "flex",
498
- position: "relative"
499
- },
500
- root: {
501
- _horizontal: { flexDirection: "column" },
502
- _vertical: { flexDirection: "row" },
503
- display: "flex",
504
- width: "full"
505
- },
506
- trigger: {
507
- ...interactive(),
508
- _disabled: { cursor: "disabled" },
509
- alignItems: "center",
510
- display: "inline-flex",
511
- justifyContent: "center",
512
- position: "relative",
513
- whiteSpace: "nowrap"
514
- }
515
- },
516
- className: "tabs",
517
- defaultVariants: {
518
- size: "md",
519
- variant: "line"
520
- },
521
- jsx: [/^Tabs(\.\w+)?$/u],
522
- slots: [
523
- "root",
524
- "list",
525
- "trigger",
526
- "content",
527
- "indicator"
528
- ],
529
- variants: {
530
- /**
531
- * Whether the controls share the strip's width between them.
532
- */
533
- fitted: { true: { trigger: { flex: "1" } } },
534
- /**
535
- * Where the controls sit when they do not fill the strip.
536
- */
537
- justify: onSlot("list", justifyVariants()),
538
- /**
539
- * How much room the strip and the panel take.
540
- */
541
- size: onSlots({
542
- content: insetSizes(),
543
- trigger: controlSizes()
544
- }),
545
- /**
546
- * How the strip is drawn, and what marks the tab in force.
547
- */
548
- variant: {
549
- enclosed: {
550
- indicator: {
551
- background: "bg.panel",
552
- borderColor: "border",
553
- borderWidth: "sm"
554
- },
555
- list: {
556
- background: "bg.muted",
557
- borderRadius: "l2",
558
- padding: "inset.xs"
559
- },
560
- trigger: {
561
- _selected: { color: "fg" },
562
- borderRadius: "l1",
563
- color: "fg.muted"
564
- }
565
- },
566
- line: {
567
- indicator: {
568
- _horizontal: {
569
- bottom: "0",
570
- height: "{borderWidths.md}",
571
- width: "var(--width)"
572
- },
573
- _vertical: {
574
- height: "var(--height)",
575
- insetInlineStart: "0",
576
- width: "{borderWidths.md}"
577
- },
578
- background: "colorPalette.solid"
579
- },
580
- list: {
581
- _horizontal: {
582
- borderBlockEndColor: "border",
583
- borderBlockEndWidth: "sm"
584
- },
585
- _vertical: {
586
- borderInlineEndColor: "border",
587
- borderInlineEndWidth: "sm"
588
- }
589
- },
590
- trigger: {
591
- _hover: { color: "fg" },
592
- _selected: { color: "colorPalette.fg" },
593
- color: "fg.muted"
594
- }
595
- },
596
- plain: {
597
- indicator: { display: "none" },
598
- trigger: {
599
- _selected: { color: "fg" },
600
- color: "fg.muted"
601
- }
602
- },
603
- subtle: {
604
- indicator: {
605
- background: "colorPalette.subtle",
606
- borderRadius: "l1"
607
- },
608
- trigger: {
609
- _selected: { color: "colorPalette.fg" },
610
- color: "fg.muted"
611
- }
612
- }
613
- }
614
- }
615
- });
616
- //#endregion
617
- //#region src/tooltip/recipe.ts
618
- /**
619
- * States what a tooltip is: a short label that appears beside what a pointer rests on.
620
- *
621
- * @remarks
622
- * The machine names no root, because a tooltip is a control and a box that floats beside it
623
- * rather than a thing that frames the two. This recipe adds one anyway, drawn with
624
- * `display: contents` so it takes part in no layout, because the control and the box are
625
- * siblings and a slot recipe hands its variants down from an element above them both.
626
- * The positioner is placed by the machine, which measures the trigger and writes the box's
627
- * position as inline styles, so this recipe states nothing about where the box goes. It states
628
- * what the box looks like, and it grows from whichever corner the machine placed it against,
629
- * which is a custom property the machine sets.
630
- * Both looks name their surface once as a custom property, because the arrow has to be filled in
631
- * the same colour as the box and reads it from there rather than restating it.
632
- * A tooltip is quieter than a popover. It holds a few words, so it reads at a label rather than
633
- * at body text, and it is capped narrow so a long hint wraps rather than running the width of the
634
- * page.
635
- */
636
- /**
637
- * Draws an inverted tooltip at the middle size until a caller says otherwise.
638
- */
639
- const recipe = defineSlotRecipe({
640
- base: {
641
- arrow: {
642
- "--arrow-background": "var(--tooltip-surface)",
643
- "--arrow-size": "sizes.icon.xs"
644
- },
645
- arrowTip: {
646
- borderInlineStartWidth: "sm",
647
- borderTopWidth: "sm"
648
- },
649
- content: {
650
- ...motion("scale-fade.in", "scale-fade.out"),
651
- background: "var(--tooltip-surface)",
652
- borderRadius: "l2",
653
- boxShadow: "md",
654
- fontWeight: "medium",
655
- maxWidth: "xs",
656
- textWrap: "pretty",
657
- transformOrigin: "var(--transform-origin)"
658
- },
659
- positioner: { position: "relative" },
660
- root: { display: "contents" }
661
- },
662
- className: "tooltip",
663
- defaultVariants: {
664
- size: "md",
665
- variant: "inverted"
666
- },
667
- jsx: [/^Tooltip(\.\w+)?$/u],
668
- slots: [
669
- "root",
670
- "trigger",
671
- "positioner",
672
- "content",
673
- "arrow",
674
- "arrowTip"
675
- ],
676
- variants: {
677
- /**
678
- * How much room the box takes, and how loud its words are.
679
- */
680
- size: onSlot("content", sizeVariants((size) => ({
681
- padding: `inset.${size}`,
682
- textStyle: `label.${size}`
683
- }))),
684
- /**
685
- * How the box is set off from the page behind it.
686
- */
687
- variant: {
688
- inverted: {
689
- arrowTip: { borderColor: "var(--tooltip-surface)" },
690
- content: {
691
- "--tooltip-surface": "colors.bg.inverted",
692
- color: "fg.inverted"
693
- }
694
- },
695
- surface: {
696
- arrowTip: { borderColor: "border" },
697
- content: {
698
- "--tooltip-surface": "colors.bg.popover",
699
- borderColor: "border",
700
- borderWidth: "sm",
701
- color: "fg"
702
- }
703
- }
704
- }
705
- }
706
- });
707
- //#endregion
708
- export { recipe$4 as a, recipe$3 as i, recipe$1 as n, recipe$2 as r, recipe as t };