@seekalfred/primitives 0.2.0 → 0.4.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.
Files changed (157) hide show
  1. package/README.md +975 -13
  2. package/dist/accordion/accordion.d.ts +107 -0
  3. package/dist/accordion/accordion.js +84 -0
  4. package/dist/accordion/accordion.js.map +1 -0
  5. package/dist/accordion/index.d.ts +2 -0
  6. package/dist/accordion/index.js +2 -0
  7. package/dist/alert/alert.d.ts +68 -0
  8. package/dist/alert/alert.js +67 -0
  9. package/dist/alert/alert.js.map +1 -0
  10. package/dist/alert/index.d.ts +2 -0
  11. package/dist/alert/index.js +2 -0
  12. package/dist/alert-dialog/alert-dialog.d.ts +53 -0
  13. package/dist/alert-dialog/alert-dialog.js +16 -0
  14. package/dist/alert-dialog/alert-dialog.js.map +1 -0
  15. package/dist/alert-dialog/index.d.ts +2 -0
  16. package/dist/alert-dialog/index.js +2 -0
  17. package/dist/avatar/avatar.d.ts +50 -0
  18. package/dist/avatar/avatar.js +91 -0
  19. package/dist/avatar/avatar.js.map +1 -0
  20. package/dist/avatar/index.d.ts +2 -0
  21. package/dist/avatar/index.js +2 -0
  22. package/dist/badge/badge.d.ts +47 -0
  23. package/dist/badge/badge.js +189 -0
  24. package/dist/badge/badge.js.map +1 -0
  25. package/dist/badge/index.d.ts +2 -0
  26. package/dist/badge/index.js +2 -0
  27. package/dist/breadcrumb/breadcrumb.d.ts +98 -0
  28. package/dist/breadcrumb/breadcrumb.js +93 -0
  29. package/dist/breadcrumb/breadcrumb.js.map +1 -0
  30. package/dist/breadcrumb/index.d.ts +2 -0
  31. package/dist/breadcrumb/index.js +2 -0
  32. package/dist/button/button.d.ts +1 -1
  33. package/dist/button/button.js +7 -0
  34. package/dist/button/button.js.map +1 -1
  35. package/dist/calendar/calendar.d.ts +154 -0
  36. package/dist/calendar/calendar.js +282 -0
  37. package/dist/calendar/calendar.js.map +1 -0
  38. package/dist/calendar/index.d.ts +2 -0
  39. package/dist/calendar/index.js +2 -0
  40. package/dist/card/card.d.ts +93 -0
  41. package/dist/card/card.js +105 -0
  42. package/dist/card/card.js.map +1 -0
  43. package/dist/card/index.d.ts +2 -0
  44. package/dist/card/index.js +2 -0
  45. package/dist/checkbox/checkbox.d.ts +63 -0
  46. package/dist/checkbox/checkbox.js +94 -0
  47. package/dist/checkbox/checkbox.js.map +1 -0
  48. package/dist/checkbox/index.d.ts +2 -0
  49. package/dist/checkbox/index.js +2 -0
  50. package/dist/dialog/dialog.d.ts +140 -0
  51. package/dist/dialog/dialog.js +127 -0
  52. package/dist/dialog/dialog.js.map +1 -0
  53. package/dist/dialog/index.d.ts +2 -0
  54. package/dist/dialog/index.js +2 -0
  55. package/dist/index.d.ts +30 -1
  56. package/dist/index.js +30 -1
  57. package/dist/input/index.d.ts +2 -0
  58. package/dist/input/index.js +2 -0
  59. package/dist/input/input.d.ts +80 -0
  60. package/dist/input/input.js +195 -0
  61. package/dist/input/input.js.map +1 -0
  62. package/dist/label/index.d.ts +2 -0
  63. package/dist/label/index.js +2 -0
  64. package/dist/label/label.d.ts +41 -0
  65. package/dist/label/label.js +44 -0
  66. package/dist/label/label.js.map +1 -0
  67. package/dist/lib/field-surface.js +40 -0
  68. package/dist/lib/field-surface.js.map +1 -0
  69. package/dist/lib/selection-control.js +57 -0
  70. package/dist/lib/selection-control.js.map +1 -0
  71. package/dist/menu/index.d.ts +2 -0
  72. package/dist/menu/index.js +2 -0
  73. package/dist/menu/menu.d.ts +98 -0
  74. package/dist/menu/menu.js +99 -0
  75. package/dist/menu/menu.js.map +1 -0
  76. package/dist/popover/index.d.ts +2 -0
  77. package/dist/popover/index.js +2 -0
  78. package/dist/popover/popover.d.ts +50 -0
  79. package/dist/popover/popover.js +46 -0
  80. package/dist/popover/popover.js.map +1 -0
  81. package/dist/progress/index.d.ts +2 -0
  82. package/dist/progress/index.js +2 -0
  83. package/dist/progress/progress.d.ts +82 -0
  84. package/dist/progress/progress.js +82 -0
  85. package/dist/progress/progress.js.map +1 -0
  86. package/dist/radio-group/index.d.ts +2 -0
  87. package/dist/radio-group/index.js +2 -0
  88. package/dist/radio-group/radio-group.d.ts +53 -0
  89. package/dist/radio-group/radio-group.js +65 -0
  90. package/dist/radio-group/radio-group.js.map +1 -0
  91. package/dist/scroll-area/index.d.ts +2 -0
  92. package/dist/scroll-area/index.js +2 -0
  93. package/dist/scroll-area/scroll-area.d.ts +74 -0
  94. package/dist/scroll-area/scroll-area.js +63 -0
  95. package/dist/scroll-area/scroll-area.js.map +1 -0
  96. package/dist/select/index.d.ts +2 -0
  97. package/dist/select/index.js +2 -0
  98. package/dist/select/select.d.ts +105 -0
  99. package/dist/select/select.js +149 -0
  100. package/dist/select/select.js.map +1 -0
  101. package/dist/separator/index.d.ts +2 -0
  102. package/dist/separator/index.js +2 -0
  103. package/dist/separator/separator.d.ts +42 -0
  104. package/dist/separator/separator.js +49 -0
  105. package/dist/separator/separator.js.map +1 -0
  106. package/dist/sheet/index.d.ts +2 -0
  107. package/dist/sheet/index.js +2 -0
  108. package/dist/sheet/sheet.d.ts +96 -0
  109. package/dist/sheet/sheet.js +98 -0
  110. package/dist/sheet/sheet.js.map +1 -0
  111. package/dist/skeleton/index.d.ts +2 -0
  112. package/dist/skeleton/index.js +2 -0
  113. package/dist/skeleton/skeleton.d.ts +33 -0
  114. package/dist/skeleton/skeleton.js +42 -0
  115. package/dist/skeleton/skeleton.js.map +1 -0
  116. package/dist/slider/index.d.ts +2 -0
  117. package/dist/slider/index.js +2 -0
  118. package/dist/slider/slider.d.ts +70 -0
  119. package/dist/slider/slider.js +92 -0
  120. package/dist/slider/slider.js.map +1 -0
  121. package/dist/spinner/index.d.ts +2 -0
  122. package/dist/spinner/index.js +2 -0
  123. package/dist/spinner/spinner.d.ts +75 -0
  124. package/dist/spinner/spinner.js +68 -0
  125. package/dist/spinner/spinner.js.map +1 -0
  126. package/dist/styles.css +1 -1
  127. package/dist/switch/index.d.ts +2 -0
  128. package/dist/switch/index.js +2 -0
  129. package/dist/switch/switch.d.ts +50 -0
  130. package/dist/switch/switch.js +66 -0
  131. package/dist/switch/switch.js.map +1 -0
  132. package/dist/table/index.d.ts +2 -0
  133. package/dist/table/index.js +2 -0
  134. package/dist/table/table.d.ts +288 -0
  135. package/dist/table/table.js +513 -0
  136. package/dist/table/table.js.map +1 -0
  137. package/dist/tabs/index.d.ts +2 -0
  138. package/dist/tabs/index.js +2 -0
  139. package/dist/tabs/tabs.d.ts +79 -0
  140. package/dist/tabs/tabs.js +103 -0
  141. package/dist/tabs/tabs.js.map +1 -0
  142. package/dist/textarea/index.d.ts +2 -0
  143. package/dist/textarea/index.js +2 -0
  144. package/dist/textarea/textarea.d.ts +27 -0
  145. package/dist/textarea/textarea.js +37 -0
  146. package/dist/textarea/textarea.js.map +1 -0
  147. package/dist/toast/index.d.ts +2 -0
  148. package/dist/toast/index.js +2 -0
  149. package/dist/toast/toast.d.ts +93 -0
  150. package/dist/toast/toast.js +145 -0
  151. package/dist/toast/toast.js.map +1 -0
  152. package/dist/tooltip/index.d.ts +2 -0
  153. package/dist/tooltip/index.js +2 -0
  154. package/dist/tooltip/tooltip.d.ts +70 -0
  155. package/dist/tooltip/tooltip.js +65 -0
  156. package/dist/tooltip/tooltip.js.map +1 -0
  157. package/package.json +125 -7
@@ -0,0 +1,2 @@
1
+ import { Sheet, SheetProps, sheetOverlayVariants, sheetPanelVariants } from "./sheet.js";
2
+ export { Sheet, type SheetProps, sheetOverlayVariants, sheetPanelVariants };
@@ -0,0 +1,2 @@
1
+ import { Sheet, sheetOverlayVariants, sheetPanelVariants } from "./sheet.js";
2
+ export { Sheet, sheetOverlayVariants, sheetPanelVariants };
@@ -0,0 +1,96 @@
1
+ import { DialogProps } from "../dialog/dialog.js";
2
+ //#region src/sheet/sheet.d.ts
3
+ /**
4
+ * Sheet: a dialog that arrives from an edge.
5
+ *
6
+ * A PRESET over `Dialog`, like `AlertDialog`. The legacy `sheet.tsx` is
7
+ * `@radix-ui/react-dialog` again -- the same import, the same parts, the
8
+ * same close button -- with one prop changing where it sits. Keeping a
9
+ * second copy is how the legacy `dialog.tsx` and `alert-dialog.tsx`
10
+ * drifted, and this is the same file for the third time.
11
+ *
12
+ * Census: 2 files, 3 uses. `side="right"` twice, `side="left"` once, and
13
+ * `className` four times. `SheetHeader`, `SheetFooter`, `SheetTitle`,
14
+ * `SheetDescription` and `SheetClose` are exported by the legacy and used
15
+ * by NOBODY -- so all three sheets in the app render a `role="dialog"`
16
+ * with no title at all. Radix even warns about it in the console.
17
+ *
18
+ * `title` is required here, inherited from Dialog, so that is no longer
19
+ * expressible.
20
+ *
21
+ * `side` is `"left" | "right"` only. The legacy also offers `"top"` and
22
+ * `"bottom"`; both have zero call sites, and each is two lines to add the
23
+ * day something needs one.
24
+ *
25
+ * A REAL BUG THE PORT FIXES: the legacy panel is `w-3/4 sm:max-w-sm`, and
26
+ * `sm:max-w-sm` names `--container-sm`, which this repo does not define --
27
+ * so in Alfred it would compile to NOTHING and the sheet would be 75% of
28
+ * the viewport at every width, on a phone and on a 4K monitor alike. The
29
+ * width here is a spacing-scale step that actually resolves.
30
+ *
31
+ * NOT PORTED: the slide-in animation. It comes from tailwindcss-animate,
32
+ * which this repo does not ship. React Aria exposes `data-entering` and
33
+ * `data-exiting`, and `--animate-*` tokens now exist, so it is a small
34
+ * follow-up rather than a blocker.
35
+ */
36
+ export declare const sheetOverlayVariants: (props?: ({
37
+ side?: "left" | "right" | null | undefined;
38
+ } & import("class-variance-authority/types").ClassProp) | undefined) => string;
39
+ export declare const sheetPanelVariants: (props?: ({
40
+ side?: "left" | "right" | null | undefined;
41
+ } & import("class-variance-authority/types").ClassProp) | undefined) => string;
42
+ /**
43
+ * A panel that arrives from the side.
44
+ *
45
+ * @example
46
+ * <Sheet isOpen={open} onOpenChange={setOpen} title="Filters">
47
+ * ...
48
+ * </Sheet>
49
+ */
50
+ export interface SheetProps extends Omit<DialogProps, "overlayClassName"> {
51
+ /**
52
+ * Which edge it is attached to.
53
+ *
54
+ * `"top"` and `"bottom"` exist in the legacy and have zero call sites.
55
+ *
56
+ * @default "right"
57
+ */
58
+ side?: "left" | "right";
59
+ /**
60
+ * Show a back control, and what it does.
61
+ *
62
+ * A side panel that goes a level deeper REPLACES its own contents rather
63
+ * than stacking a second modal on top, so the browser's back affordance and
64
+ * the escape key both do the wrong thing -- they close the whole panel
65
+ * instead of returning one level. The product's own note calls this the
66
+ * thing that "makes items 119 and 120 one change rather than two".
67
+ */
68
+ onBack?: () => void;
69
+ /** Accessible name and label for the back control. @default "Back" */
70
+ backLabel?: string;
71
+ /**
72
+ * Change this when the panel's contents change, and the body scrolls back
73
+ * to the top.
74
+ *
75
+ * Going a level deeper and landing halfway down the new view is
76
+ * disorienting, and it is the kind of thing every call site forgets --
77
+ * which is why it belongs here rather than in each of them.
78
+ */
79
+ scrollResetKey?: string | number;
80
+ /** Extra classes for the scrolling body region. */
81
+ bodyClassName?: string;
82
+ /**
83
+ * Names the scrolling body region. Defaults to `title` when that is a
84
+ * string, which it is at every call site in the product.
85
+ *
86
+ * The body is a `ScrollArea`, so it is focusable -- a scrollable region that
87
+ * is not reachable by keyboard is WCAG 2.1.1, and axe caught exactly that
88
+ * when this was a plain div. The product's own `SidePanelBody` still is one.
89
+ * A focusable element needs a role and a role needs a name, so this is the
90
+ * only combination that is actually correct.
91
+ */
92
+ bodyLabel?: string;
93
+ }
94
+ export declare function Sheet({ side, className, onBack, backLabel, scrollResetKey, bodyClassName, bodyLabel, title, children, ...props }: SheetProps): import("react").JSX.Element;
95
+ //#endregion
96
+ //# sourceMappingURL=sheet.d.ts.map
@@ -0,0 +1,98 @@
1
+ "use client";
2
+ import { Dialog } from "../dialog/dialog.js";
3
+ import { Button } from "../button/button.js";
4
+ import { ScrollArea } from "../scroll-area/scroll-area.js";
5
+ import { cva } from "class-variance-authority";
6
+ import { jsx } from "react/jsx-runtime";
7
+ //#region src/sheet/sheet.tsx
8
+ /**
9
+ * Sheet: a dialog that arrives from an edge.
10
+ *
11
+ * A PRESET over `Dialog`, like `AlertDialog`. The legacy `sheet.tsx` is
12
+ * `@radix-ui/react-dialog` again -- the same import, the same parts, the
13
+ * same close button -- with one prop changing where it sits. Keeping a
14
+ * second copy is how the legacy `dialog.tsx` and `alert-dialog.tsx`
15
+ * drifted, and this is the same file for the third time.
16
+ *
17
+ * Census: 2 files, 3 uses. `side="right"` twice, `side="left"` once, and
18
+ * `className` four times. `SheetHeader`, `SheetFooter`, `SheetTitle`,
19
+ * `SheetDescription` and `SheetClose` are exported by the legacy and used
20
+ * by NOBODY -- so all three sheets in the app render a `role="dialog"`
21
+ * with no title at all. Radix even warns about it in the console.
22
+ *
23
+ * `title` is required here, inherited from Dialog, so that is no longer
24
+ * expressible.
25
+ *
26
+ * `side` is `"left" | "right"` only. The legacy also offers `"top"` and
27
+ * `"bottom"`; both have zero call sites, and each is two lines to add the
28
+ * day something needs one.
29
+ *
30
+ * A REAL BUG THE PORT FIXES: the legacy panel is `w-3/4 sm:max-w-sm`, and
31
+ * `sm:max-w-sm` names `--container-sm`, which this repo does not define --
32
+ * so in Alfred it would compile to NOTHING and the sheet would be 75% of
33
+ * the viewport at every width, on a phone and on a 4K monitor alike. The
34
+ * width here is a spacing-scale step that actually resolves.
35
+ *
36
+ * NOT PORTED: the slide-in animation. It comes from tailwindcss-animate,
37
+ * which this repo does not ship. React Aria exposes `data-entering` and
38
+ * `data-exiting`, and `--animate-*` tokens now exist, so it is a small
39
+ * follow-up rather than a blocker.
40
+ */
41
+ const sheetOverlayVariants = cva("p-0", {
42
+ variants: { side: {
43
+ left: "justify-start items-stretch",
44
+ right: "justify-end items-stretch"
45
+ } },
46
+ defaultVariants: { side: "right" }
47
+ });
48
+ const sheetPanelVariants = cva([
49
+ "h-full w-full max-w-96",
50
+ "rounded-none",
51
+ "max-h-full overflow-y-auto"
52
+ ], {
53
+ variants: { side: {
54
+ left: "border-y-0 border-s-0",
55
+ right: "border-y-0 border-e-0"
56
+ } },
57
+ defaultVariants: { side: "right" }
58
+ });
59
+ function BackArrowGlyph() {
60
+ return /* @__PURE__ */ jsx("svg", {
61
+ "aria-hidden": "true",
62
+ viewBox: "0 0 16 16",
63
+ fill: "none",
64
+ stroke: "currentColor",
65
+ strokeWidth: "1.5",
66
+ strokeLinecap: "round",
67
+ strokeLinejoin: "round",
68
+ className: "size-(--icon-sm) rtl:-scale-x-100",
69
+ children: /* @__PURE__ */ jsx("path", { d: "M10 3 5 8l5 5" })
70
+ });
71
+ }
72
+ function Sheet({ side = "right", className, onBack, backLabel = "Back", scrollResetKey, bodyClassName, bodyLabel, title, children, ...props }) {
73
+ return /* @__PURE__ */ jsx(Dialog, {
74
+ ...props,
75
+ title,
76
+ beforeTitle: onBack ? /* @__PURE__ */ jsx(Button, {
77
+ variant: "ghost",
78
+ size: "sm",
79
+ "data-slot": "sheet-back",
80
+ className: "-ms-2 w-fit",
81
+ startIcon: /* @__PURE__ */ jsx(BackArrowGlyph, {}),
82
+ onPress: onBack,
83
+ children: backLabel
84
+ }) : void 0,
85
+ overlayClassName: sheetOverlayVariants({ side }),
86
+ className: [sheetPanelVariants({ side }), className].filter(Boolean).join(" "),
87
+ children: /* @__PURE__ */ jsx(ScrollArea, {
88
+ scrollResetKey,
89
+ label: bodyLabel ?? (typeof title === "string" ? title : ""),
90
+ className: ["min-h-0 flex-1", bodyClassName].filter(Boolean).join(" "),
91
+ children
92
+ })
93
+ });
94
+ }
95
+ //#endregion
96
+ export { Sheet, sheetOverlayVariants, sheetPanelVariants };
97
+
98
+ //# sourceMappingURL=sheet.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sheet.js","names":[],"sources":["../../src/sheet/sheet.tsx"],"sourcesContent":["\"use client\";\n\nimport { cva } from \"class-variance-authority\";\n\nimport { Button } from \"../button/button.js\";\nimport { Dialog, type DialogProps } from \"../dialog/dialog.js\";\nimport { ScrollArea } from \"../scroll-area/scroll-area.js\";\n\n/**\n * Sheet: a dialog that arrives from an edge.\n *\n * A PRESET over `Dialog`, like `AlertDialog`. The legacy `sheet.tsx` is\n * `@radix-ui/react-dialog` again -- the same import, the same parts, the\n * same close button -- with one prop changing where it sits. Keeping a\n * second copy is how the legacy `dialog.tsx` and `alert-dialog.tsx`\n * drifted, and this is the same file for the third time.\n *\n * Census: 2 files, 3 uses. `side=\"right\"` twice, `side=\"left\"` once, and\n * `className` four times. `SheetHeader`, `SheetFooter`, `SheetTitle`,\n * `SheetDescription` and `SheetClose` are exported by the legacy and used\n * by NOBODY -- so all three sheets in the app render a `role=\"dialog\"`\n * with no title at all. Radix even warns about it in the console.\n *\n * `title` is required here, inherited from Dialog, so that is no longer\n * expressible.\n *\n * `side` is `\"left\" | \"right\"` only. The legacy also offers `\"top\"` and\n * `\"bottom\"`; both have zero call sites, and each is two lines to add the\n * day something needs one.\n *\n * A REAL BUG THE PORT FIXES: the legacy panel is `w-3/4 sm:max-w-sm`, and\n * `sm:max-w-sm` names `--container-sm`, which this repo does not define --\n * so in Alfred it would compile to NOTHING and the sheet would be 75% of\n * the viewport at every width, on a phone and on a 4K monitor alike. The\n * width here is a spacing-scale step that actually resolves.\n *\n * NOT PORTED: the slide-in animation. It comes from tailwindcss-animate,\n * which this repo does not ship. React Aria exposes `data-entering` and\n * `data-exiting`, and `--animate-*` tokens now exist, so it is a small\n * follow-up rather than a blocker.\n */\nexport const sheetOverlayVariants = cva(\"p-0\", {\n variants: {\n // The overlay is a flex box centring the panel. A sheet replaces that\n // centring with an edge alignment rather than positioning the panel\n // absolutely, so the scrim and the panel stay one layout.\n side: {\n left: \"justify-start items-stretch\",\n right: \"justify-end items-stretch\",\n },\n },\n defaultVariants: { side: \"right\" },\n});\n\nexport const sheetPanelVariants = cva(\n [\n // max-w-96 is 384px and RESOLVES. See the note above on sm:max-w-sm.\n \"h-full w-full max-w-96\",\n // Square against the edge it is attached to: a rounded corner flush\n // with the viewport edge reads as a rendering fault.\n \"rounded-none\",\n \"max-h-full overflow-y-auto\",\n ],\n {\n variants: {\n side: {\n left: \"border-y-0 border-s-0\",\n right: \"border-y-0 border-e-0\",\n },\n },\n defaultVariants: { side: \"right\" },\n },\n);\n\n/**\n * A panel that arrives from the side.\n *\n * @example\n * <Sheet isOpen={open} onOpenChange={setOpen} title=\"Filters\">\n * ...\n * </Sheet>\n */\nexport interface SheetProps extends Omit<DialogProps, \"overlayClassName\"> {\n /**\n * Which edge it is attached to.\n *\n * `\"top\"` and `\"bottom\"` exist in the legacy and have zero call sites.\n *\n * @default \"right\"\n */\n side?: \"left\" | \"right\";\n /**\n * Show a back control, and what it does.\n *\n * A side panel that goes a level deeper REPLACES its own contents rather\n * than stacking a second modal on top, so the browser's back affordance and\n * the escape key both do the wrong thing -- they close the whole panel\n * instead of returning one level. The product's own note calls this the\n * thing that \"makes items 119 and 120 one change rather than two\".\n */\n onBack?: () => void;\n /** Accessible name and label for the back control. @default \"Back\" */\n backLabel?: string;\n /**\n * Change this when the panel's contents change, and the body scrolls back\n * to the top.\n *\n * Going a level deeper and landing halfway down the new view is\n * disorienting, and it is the kind of thing every call site forgets --\n * which is why it belongs here rather than in each of them.\n */\n scrollResetKey?: string | number;\n /** Extra classes for the scrolling body region. */\n bodyClassName?: string;\n /**\n * Names the scrolling body region. Defaults to `title` when that is a\n * string, which it is at every call site in the product.\n *\n * The body is a `ScrollArea`, so it is focusable -- a scrollable region that\n * is not reachable by keyboard is WCAG 2.1.1, and axe caught exactly that\n * when this was a plain div. The product's own `SidePanelBody` still is one.\n * A focusable element needs a role and a role needs a name, so this is the\n * only combination that is actually correct.\n */\n bodyLabel?: string;\n}\n\nfunction BackArrowGlyph() {\n return (\n <svg\n aria-hidden=\"true\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.5\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n // Logical, so it points the right way once the panel mirrors in RTL.\n className=\"size-(--icon-sm) rtl:-scale-x-100\"\n >\n <path d=\"M10 3 5 8l5 5\" />\n </svg>\n );\n}\n\nexport function Sheet({\n side = \"right\",\n className,\n onBack,\n backLabel = \"Back\",\n scrollResetKey,\n bodyClassName,\n bodyLabel,\n title,\n children,\n ...props\n}: SheetProps) {\n return (\n /*\n No data-slot here. Dialog spreads its props onto the ModalOverlay and\n writes data-slot=\"dialog-overlay\" AFTER the spread, so a data-slot\n passed from here is overwritten before it reaches the DOM -- it read as\n working while rendering nothing. A sheet is identified by its side\n variant and its panel classes instead.\n */\n <Dialog\n {...props}\n title={title}\n beforeTitle={\n onBack ? (\n <Button\n variant=\"ghost\"\n size=\"sm\"\n data-slot=\"sheet-back\"\n className=\"-ms-2 w-fit\"\n startIcon={<BackArrowGlyph />}\n onPress={onBack}\n >\n {backLabel}\n </Button>\n ) : undefined\n }\n overlayClassName={sheetOverlayVariants({ side })}\n className={[sheetPanelVariants({ side }), className].filter(Boolean).join(\" \")}\n >\n {/*\n The scrolling region is its own element so the title and the back\n control stay put while the content moves. A panel that scrolled as one\n block would take its own heading off screen.\n */}\n {/*\n No data-slot=\"sheet-body\" here, for the same reason the overlay has\n none: ScrollArea spreads its props and writes data-slot=\"scroll-area\"\n AFTER the spread, so one passed from here is overwritten before it\n reaches the DOM. It read as wired while selecting nothing.\n */}\n <ScrollArea\n scrollResetKey={scrollResetKey}\n label={bodyLabel ?? (typeof title === \"string\" ? title : \"\")}\n className={[\"min-h-0 flex-1\", bodyClassName].filter(Boolean).join(\" \")}\n >\n {children}\n </ScrollArea>\n </Dialog>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyCA,MAAa,uBAAuB,IAAI,OAAO;CAC7C,UAAU,EAIR,MAAM;EACJ,MAAM;EACN,OAAO;CACT,EACF;CACA,iBAAiB,EAAE,MAAM,QAAQ;AACnC,CAAC;AAED,MAAa,qBAAqB,IAChC;CAEE;CAGA;CACA;AACF,GACA;CACE,UAAU,EACR,MAAM;EACJ,MAAM;EACN,OAAO;CACT,EACF;CACA,iBAAiB,EAAE,MAAM,QAAQ;AACnC,CACF;AAuDA,SAAS,iBAAiB;CACxB,OACE,oBAAC,OAAD;EACE,eAAY;EACZ,SAAQ;EACR,MAAK;EACL,QAAO;EACP,aAAY;EACZ,eAAc;EACd,gBAAe;EAEf,WAAU;EAEV,UAAA,oBAAC,QAAD,EAAM,GAAE,gBAAiB,CAAA;CACtB,CAAA;AAET;AAEA,SAAgB,MAAM,EACpB,OAAO,SACP,WACA,QACA,YAAY,QACZ,gBACA,eACA,WACA,OACA,UACA,GAAG,SACU;CACb,OAQE,oBAAC,QAAD;EACE,GAAI;EACG;EACP,aACE,SACE,oBAAC,QAAD;GACE,SAAQ;GACR,MAAK;GACL,aAAU;GACV,WAAU;GACV,WAAW,oBAAC,gBAAD,CAAiB,CAAA;GAC5B,SAAS;GAER,UAAA;EACK,CAAA,IACN,KAAA;EAEN,kBAAkB,qBAAqB,EAAE,KAAK,CAAC;EAC/C,WAAW,CAAC,mBAAmB,EAAE,KAAK,CAAC,GAAG,SAAS,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,GAAG;EAa7E,UAAA,oBAAC,YAAD;GACkB;GAChB,OAAO,cAAc,OAAO,UAAU,WAAW,QAAQ;GACzD,WAAW,CAAC,kBAAkB,aAAa,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,GAAG;GAEpE;EACS,CAAA;CACN,CAAA;AAEZ"}
@@ -0,0 +1,2 @@
1
+ import { Skeleton, SkeletonProps, skeletonVariants } from "./skeleton.js";
2
+ export { Skeleton, type SkeletonProps, skeletonVariants };
@@ -0,0 +1,2 @@
1
+ import { Skeleton, skeletonVariants } from "./skeleton.js";
2
+ export { Skeleton, skeletonVariants };
@@ -0,0 +1,33 @@
1
+ import { ComponentPropsWithoutRef } from "react";
2
+ //#region src/skeleton/skeleton.d.ts
3
+ /**
4
+ * Skeleton, ported from the legacy shadcn component.
5
+ *
6
+ * Census: 2 files, 33 uses, and every single one passes `className` and
7
+ * nothing else -- the size always comes from the call site. So there is no
8
+ * variant axis to invent here; the component is a surface and a pulse.
9
+ *
10
+ * THE PULSE DID NOT EXIST. `animate-pulse` names `--animate-pulse`, which
11
+ * this repo does not define: it imports only `tailwindcss/utilities.css`,
12
+ * never Tailwind's default theme, so both the custom property and the
13
+ * `@keyframes pulse` are absent and the utility emits NO RULE AT ALL. The
14
+ * class was present, tsc and ESLint were happy, and nothing moved. Same
15
+ * failure mode as the Dialog's `max-w-lg`. This port adds the token
16
+ * (`scale.json` -> `--animate-pulse`) and the keyframes
17
+ * (`@seekalfred/styles/motion.css`).
18
+ *
19
+ * `bg-accent` becomes `bg-muted`: in the legacy theme `--accent` is
20
+ * character-for-character `--muted` in both themes, so this is a rename to
21
+ * the role Alfred actually has, not a colour change.
22
+ */
23
+ export declare const skeletonVariants: (props?: import("class-variance-authority/types").ClassProp | undefined) => string;
24
+ /**
25
+ * A loading placeholder.
26
+ *
27
+ * @example
28
+ * <Skeleton className="h-4 w-32" />
29
+ */
30
+ export type SkeletonProps = ComponentPropsWithoutRef<"div">;
31
+ export declare function Skeleton({ className, ...props }: SkeletonProps): import("react").JSX.Element;
32
+ //#endregion
33
+ //# sourceMappingURL=skeleton.d.ts.map
@@ -0,0 +1,42 @@
1
+ "use client";
2
+ import { cn } from "../lib/cn.js";
3
+ import { cva } from "class-variance-authority";
4
+ import { jsx } from "react/jsx-runtime";
5
+ //#region src/skeleton/skeleton.tsx
6
+ /**
7
+ * Skeleton, ported from the legacy shadcn component.
8
+ *
9
+ * Census: 2 files, 33 uses, and every single one passes `className` and
10
+ * nothing else -- the size always comes from the call site. So there is no
11
+ * variant axis to invent here; the component is a surface and a pulse.
12
+ *
13
+ * THE PULSE DID NOT EXIST. `animate-pulse` names `--animate-pulse`, which
14
+ * this repo does not define: it imports only `tailwindcss/utilities.css`,
15
+ * never Tailwind's default theme, so both the custom property and the
16
+ * `@keyframes pulse` are absent and the utility emits NO RULE AT ALL. The
17
+ * class was present, tsc and ESLint were happy, and nothing moved. Same
18
+ * failure mode as the Dialog's `max-w-lg`. This port adds the token
19
+ * (`scale.json` -> `--animate-pulse`) and the keyframes
20
+ * (`@seekalfred/styles/motion.css`).
21
+ *
22
+ * `bg-accent` becomes `bg-muted`: in the legacy theme `--accent` is
23
+ * character-for-character `--muted` in both themes, so this is a rename to
24
+ * the role Alfred actually has, not a colour change.
25
+ */
26
+ const skeletonVariants = cva([
27
+ "block bg-muted rounded-md",
28
+ "motion-safe:animate-pulse",
29
+ "box-border"
30
+ ]);
31
+ function Skeleton({ className, ...props }) {
32
+ return /* @__PURE__ */ jsx("div", {
33
+ ...props,
34
+ "aria-hidden": "true",
35
+ "data-slot": "skeleton",
36
+ className: cn(skeletonVariants(), className)
37
+ });
38
+ }
39
+ //#endregion
40
+ export { Skeleton, skeletonVariants };
41
+
42
+ //# sourceMappingURL=skeleton.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skeleton.js","names":[],"sources":["../../src/skeleton/skeleton.tsx"],"sourcesContent":["\"use client\";\n\nimport { cva } from \"class-variance-authority\";\nimport type { ComponentPropsWithoutRef } from \"react\";\n\nimport { cn } from \"../lib/cn.js\";\n\n/**\n * Skeleton, ported from the legacy shadcn component.\n *\n * Census: 2 files, 33 uses, and every single one passes `className` and\n * nothing else -- the size always comes from the call site. So there is no\n * variant axis to invent here; the component is a surface and a pulse.\n *\n * THE PULSE DID NOT EXIST. `animate-pulse` names `--animate-pulse`, which\n * this repo does not define: it imports only `tailwindcss/utilities.css`,\n * never Tailwind's default theme, so both the custom property and the\n * `@keyframes pulse` are absent and the utility emits NO RULE AT ALL. The\n * class was present, tsc and ESLint were happy, and nothing moved. Same\n * failure mode as the Dialog's `max-w-lg`. This port adds the token\n * (`scale.json` -> `--animate-pulse`) and the keyframes\n * (`@seekalfred/styles/motion.css`).\n *\n * `bg-accent` becomes `bg-muted`: in the legacy theme `--accent` is\n * character-for-character `--muted` in both themes, so this is a rename to\n * the role Alfred actually has, not a colour change.\n */\nexport const skeletonVariants = cva([\n \"block bg-muted rounded-md\",\n // motion-safe: a full-screen page of these pulsing is exactly what\n // prefers-reduced-motion exists for. Without the animation it is still a\n // visible placeholder, which is the whole job.\n \"motion-safe:animate-pulse\",\n // The reset in @seekalfred/styles is OPT-IN, so a consumer may load none,\n // and every call site sizes this through className -- content-box would\n // add any padding they set on top of the height they asked for.\n \"box-border\",\n]);\n\n/**\n * A loading placeholder.\n *\n * @example\n * <Skeleton className=\"h-4 w-32\" />\n */\nexport type SkeletonProps = ComponentPropsWithoutRef<\"div\">;\n\nexport function Skeleton({ className, ...props }: SkeletonProps) {\n return (\n <div\n {...props}\n // aria-hidden: a placeholder is not content. Thirty-three empty boxes\n // announced while a page loads is noise, and the loading STATE belongs\n // on the region being replaced (aria-busy), which only the consumer\n // can place.\n aria-hidden=\"true\"\n data-slot=\"skeleton\"\n className={cn(skeletonVariants(), className)}\n />\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AA2BA,MAAa,mBAAmB,IAAI;CAClC;CAIA;CAIA;AACF,CAAC;AAUD,SAAgB,SAAS,EAAE,WAAW,GAAG,SAAwB;CAC/D,OACE,oBAAC,OAAD;EACE,GAAI;EAKJ,eAAY;EACZ,aAAU;EACV,WAAW,GAAG,iBAAiB,GAAG,SAAS;CAC5C,CAAA;AAEL"}
@@ -0,0 +1,2 @@
1
+ import { Slider, SliderProps, sliderBarVariants, sliderFillVariants, sliderThumbVariants, sliderTrackVariants, sliderVariants } from "./slider.js";
2
+ export { Slider, type SliderProps, sliderBarVariants, sliderFillVariants, sliderThumbVariants, sliderTrackVariants, sliderVariants };
@@ -0,0 +1,2 @@
1
+ import { Slider, sliderBarVariants, sliderFillVariants, sliderThumbVariants, sliderTrackVariants, sliderVariants } from "./slider.js";
2
+ export { Slider, sliderBarVariants, sliderFillVariants, sliderThumbVariants, sliderTrackVariants, sliderVariants };
@@ -0,0 +1,70 @@
1
+ import { SliderProps } from "react-aria-components";
2
+ import { ReactNode } from "react";
3
+ //#region src/slider/slider.d.ts
4
+ /**
5
+ * Slider, ported from the legacy shadcn/Radix component.
6
+ *
7
+ * Census: 4 call sites, all single-thumb, none of them named. Radix only
8
+ * generates thumb labels at two or more thumbs, so all four are announced
9
+ * as an unnamed slider -- `AudioSettings.tsx` even renders a
10
+ * `<Label>Input Volume</Label>` directly above one, with no `htmlFor` and
11
+ * no `id`, so it is associated with nothing at all.
12
+ *
13
+ * `label` IS REQUIRED here, the same answer Progress and Dialog give.
14
+ *
15
+ * THE PALETTE DELIBERATELY DOES NOT MATCH SWITCH, and the reason is worth
16
+ * stating because the two look like the same problem. A Switch has no
17
+ * fill: its track is one colour and the thumb tells you the state, so
18
+ * `border` unchecked and `primary-strong` checked works. A Slider has a
19
+ * filled portion whose LENGTH is the value -- and `primary-strong` on a
20
+ * `border` track is 1.01:1. The two halves would be the same grey and the
21
+ * value would be invisible to anyone not reading the thumb position.
22
+ *
23
+ * So the track is `muted` with a `border` boundary and the fill is
24
+ * `primary-strong`: fill against track 4.61:1, track against page 5.25:1,
25
+ * thumb against fill 5.20:1, thumb edge against track 4.65:1. All four
26
+ * pairs are in the build-failing contrast contract.
27
+ *
28
+ * Three legacy defects go with it: the focus ring was `ring-1`, a 1px
29
+ * box-shadow that forced-colors discards entirely; the thumb border was
30
+ * `border-primary/50`, an alpha edge with no measurable contrast; and the
31
+ * disabled styling used `disabled:`, a pseudo-class that only applies to
32
+ * form elements -- Radix renders a `<span role="slider">`, so a disabled
33
+ * slider looked exactly like an enabled one.
34
+ */
35
+ export declare const sliderVariants: (props?: import("class-variance-authority/types").ClassProp | undefined) => string;
36
+ export declare const sliderTrackVariants: (props?: import("class-variance-authority/types").ClassProp | undefined) => string;
37
+ export declare const sliderBarVariants: (props?: import("class-variance-authority/types").ClassProp | undefined) => string;
38
+ export declare const sliderFillVariants: (props?: import("class-variance-authority/types").ClassProp | undefined) => string;
39
+ export declare const sliderThumbVariants: (props?: import("class-variance-authority/types").ClassProp | undefined) => string;
40
+ /**
41
+ * A value along a range.
42
+ *
43
+ * @example
44
+ * <Slider label="Input volume" defaultValue={40} />
45
+ * <Slider label="Playback position" value={t} onChange={seek} formatOptions={{ style: "percent" }} />
46
+ */
47
+ interface SliderProps$1 extends Omit<SliderProps<number>, "className" | "style" | "children"> {
48
+ /**
49
+ * What the slider controls, and its accessible name.
50
+ *
51
+ * **Required.** All four legacy call sites are unnamed sliders.
52
+ */
53
+ label: ReactNode;
54
+ /** Render the label for screen readers only. */
55
+ labelHidden?: boolean;
56
+ /**
57
+ * Show the current value as text beside the label.
58
+ *
59
+ * On by default. It is the text equivalent of the fill length, which
60
+ * matters because in greyscale the filled and unfilled halves are
61
+ * distinguishable only by the thumb.
62
+ */
63
+ showValue?: boolean;
64
+ /** Extra classes. Merged last, so they win conflicts with ours. */
65
+ className?: string;
66
+ }
67
+ declare function Slider$1({ label, labelHidden, showValue, className, ...props }: SliderProps$1): import("react").JSX.Element;
68
+ //#endregion
69
+ export { Slider$1 as Slider, SliderProps$1 as SliderProps };
70
+ //# sourceMappingURL=slider.d.ts.map
@@ -0,0 +1,92 @@
1
+ "use client";
2
+ import { cn } from "../lib/cn.js";
3
+ import { cva } from "class-variance-authority";
4
+ import { Label, Slider, SliderOutput, SliderThumb, SliderTrack } from "react-aria-components";
5
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
6
+ //#region src/slider/slider.tsx
7
+ /**
8
+ * Slider, ported from the legacy shadcn/Radix component.
9
+ *
10
+ * Census: 4 call sites, all single-thumb, none of them named. Radix only
11
+ * generates thumb labels at two or more thumbs, so all four are announced
12
+ * as an unnamed slider -- `AudioSettings.tsx` even renders a
13
+ * `<Label>Input Volume</Label>` directly above one, with no `htmlFor` and
14
+ * no `id`, so it is associated with nothing at all.
15
+ *
16
+ * `label` IS REQUIRED here, the same answer Progress and Dialog give.
17
+ *
18
+ * THE PALETTE DELIBERATELY DOES NOT MATCH SWITCH, and the reason is worth
19
+ * stating because the two look like the same problem. A Switch has no
20
+ * fill: its track is one colour and the thumb tells you the state, so
21
+ * `border` unchecked and `primary-strong` checked works. A Slider has a
22
+ * filled portion whose LENGTH is the value -- and `primary-strong` on a
23
+ * `border` track is 1.01:1. The two halves would be the same grey and the
24
+ * value would be invisible to anyone not reading the thumb position.
25
+ *
26
+ * So the track is `muted` with a `border` boundary and the fill is
27
+ * `primary-strong`: fill against track 4.61:1, track against page 5.25:1,
28
+ * thumb against fill 5.20:1, thumb edge against track 4.65:1. All four
29
+ * pairs are in the build-failing contrast contract.
30
+ *
31
+ * Three legacy defects go with it: the focus ring was `ring-1`, a 1px
32
+ * box-shadow that forced-colors discards entirely; the thumb border was
33
+ * `border-primary/50`, an alpha edge with no measurable contrast; and the
34
+ * disabled styling used `disabled:`, a pseudo-class that only applies to
35
+ * form elements -- Radix renders a `<span role="slider">`, so a disabled
36
+ * slider looked exactly like an enabled one.
37
+ */
38
+ const sliderVariants = cva(["flex w-full flex-col gap-2 box-border font-sans"]);
39
+ const sliderTrackVariants = cva(["relative flex w-full items-center", "h-6"]);
40
+ const sliderBarVariants = cva(["absolute h-2 w-full rounded-pill box-border", "bg-muted border-(length:--border-width) border-border"]);
41
+ const sliderFillVariants = cva(["absolute h-2 rounded-pill bg-primary-strong"]);
42
+ const sliderThumbVariants = cva([
43
+ "size-6 rounded-full box-border",
44
+ "bg-background border-(length:--border-width) border-border shadow-surface",
45
+ "cursor-grab data-[dragging]:cursor-grabbing",
46
+ "outline-none",
47
+ "data-[focus-visible]:outline-solid",
48
+ "data-[focus-visible]:outline-(length:--focus-ring-width)",
49
+ "data-[focus-visible]:outline-offset-(--focus-ring-offset)",
50
+ "data-[focus-visible]:outline-(--color-ring)",
51
+ "data-[disabled]:cursor-not-allowed"
52
+ ]);
53
+ function Slider$1({ label, labelHidden, showValue = true, className, ...props }) {
54
+ return /* @__PURE__ */ jsxs(Slider, {
55
+ ...props,
56
+ "data-slot": "slider",
57
+ className: cn(sliderVariants(), className),
58
+ children: [/* @__PURE__ */ jsxs("div", {
59
+ className: cn("flex items-center justify-between gap-2", labelHidden && "sr-only"),
60
+ children: [/* @__PURE__ */ jsx(Label, {
61
+ "data-slot": "slider-label",
62
+ className: "text-sm leading-(--leading-normal) text-foreground",
63
+ children: label
64
+ }), showValue ? /* @__PURE__ */ jsx(SliderOutput, {
65
+ "data-slot": "slider-output",
66
+ className: "text-sm leading-(--leading-normal) text-muted-foreground"
67
+ }) : null]
68
+ }), /* @__PURE__ */ jsx(SliderTrack, {
69
+ "data-slot": "slider-track",
70
+ className: sliderTrackVariants(),
71
+ children: ({ state }) => /* @__PURE__ */ jsxs(Fragment, { children: [
72
+ /* @__PURE__ */ jsx("div", {
73
+ "data-slot": "slider-bar",
74
+ className: sliderBarVariants()
75
+ }),
76
+ /* @__PURE__ */ jsx("div", {
77
+ "data-slot": "slider-fill",
78
+ className: sliderFillVariants(),
79
+ style: { width: `${state.getThumbPercent(0) * 100}%` }
80
+ }),
81
+ /* @__PURE__ */ jsx(SliderThumb, {
82
+ "data-slot": "slider-thumb",
83
+ className: sliderThumbVariants()
84
+ })
85
+ ] })
86
+ })]
87
+ });
88
+ }
89
+ //#endregion
90
+ export { Slider$1 as Slider, sliderBarVariants, sliderFillVariants, sliderThumbVariants, sliderTrackVariants, sliderVariants };
91
+
92
+ //# sourceMappingURL=slider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slider.js","names":["Slider","AriaSlider","AriaLabel","AriaSliderOutput","AriaSliderTrack","AriaSliderThumb"],"sources":["../../src/slider/slider.tsx"],"sourcesContent":["\"use client\";\n\nimport { cva } from \"class-variance-authority\";\nimport type { ReactNode } from \"react\";\nimport {\n Label as AriaLabel,\n Slider as AriaSlider,\n SliderOutput as AriaSliderOutput,\n SliderThumb as AriaSliderThumb,\n SliderTrack as AriaSliderTrack,\n type SliderProps as AriaSliderProps,\n} from \"react-aria-components\";\n\nimport { cn } from \"../lib/cn.js\";\n\n/**\n * Slider, ported from the legacy shadcn/Radix component.\n *\n * Census: 4 call sites, all single-thumb, none of them named. Radix only\n * generates thumb labels at two or more thumbs, so all four are announced\n * as an unnamed slider -- `AudioSettings.tsx` even renders a\n * `<Label>Input Volume</Label>` directly above one, with no `htmlFor` and\n * no `id`, so it is associated with nothing at all.\n *\n * `label` IS REQUIRED here, the same answer Progress and Dialog give.\n *\n * THE PALETTE DELIBERATELY DOES NOT MATCH SWITCH, and the reason is worth\n * stating because the two look like the same problem. A Switch has no\n * fill: its track is one colour and the thumb tells you the state, so\n * `border` unchecked and `primary-strong` checked works. A Slider has a\n * filled portion whose LENGTH is the value -- and `primary-strong` on a\n * `border` track is 1.01:1. The two halves would be the same grey and the\n * value would be invisible to anyone not reading the thumb position.\n *\n * So the track is `muted` with a `border` boundary and the fill is\n * `primary-strong`: fill against track 4.61:1, track against page 5.25:1,\n * thumb against fill 5.20:1, thumb edge against track 4.65:1. All four\n * pairs are in the build-failing contrast contract.\n *\n * Three legacy defects go with it: the focus ring was `ring-1`, a 1px\n * box-shadow that forced-colors discards entirely; the thumb border was\n * `border-primary/50`, an alpha edge with no measurable contrast; and the\n * disabled styling used `disabled:`, a pseudo-class that only applies to\n * form elements -- Radix renders a `<span role=\"slider\">`, so a disabled\n * slider looked exactly like an enabled one.\n */\nexport const sliderVariants = cva([\"flex w-full flex-col gap-2 box-border font-sans\"]);\n\nexport const sliderTrackVariants = cva([\n \"relative flex w-full items-center\",\n // h-6 is the TRACK ROW, not the visible bar -- it gives the 24px\n // pointer target WCAG 2.5.8 wants. The bar itself is drawn inside.\n \"h-6\",\n]);\n\nexport const sliderBarVariants = cva([\n \"absolute h-2 w-full rounded-pill box-border\",\n \"bg-muted border-(length:--border-width) border-border\",\n]);\n\nexport const sliderFillVariants = cva([\"absolute h-2 rounded-pill bg-primary-strong\"]);\n\nexport const sliderThumbVariants = cva([\n // size-6 is 24px: WCAG 2.5.8 target size. The legacy thumb was 16px at\n // all four call sites, and one of them crams the whole control into 80px.\n \"size-6 rounded-full box-border\",\n \"bg-background border-(length:--border-width) border-border shadow-surface\",\n \"cursor-grab data-[dragging]:cursor-grabbing\",\n // A real outline, not the legacy ring-1 box-shadow.\n \"outline-none\",\n \"data-[focus-visible]:outline-solid\",\n \"data-[focus-visible]:outline-(length:--focus-ring-width)\",\n \"data-[focus-visible]:outline-offset-(--focus-ring-offset)\",\n \"data-[focus-visible]:outline-(--color-ring)\",\n // data-[disabled], NOT disabled:. The legacy used the CSS :disabled\n // pseudo-class, which applies only to form elements -- React Aria and\n // Radix both render a span, so that styling never fired.\n \"data-[disabled]:cursor-not-allowed\",\n]);\n\n/**\n * A value along a range.\n *\n * @example\n * <Slider label=\"Input volume\" defaultValue={40} />\n * <Slider label=\"Playback position\" value={t} onChange={seek} formatOptions={{ style: \"percent\" }} />\n */\nexport interface SliderProps\n extends Omit<AriaSliderProps<number>, \"className\" | \"style\" | \"children\"> {\n /**\n * What the slider controls, and its accessible name.\n *\n * **Required.** All four legacy call sites are unnamed sliders.\n */\n label: ReactNode;\n /** Render the label for screen readers only. */\n labelHidden?: boolean;\n /**\n * Show the current value as text beside the label.\n *\n * On by default. It is the text equivalent of the fill length, which\n * matters because in greyscale the filled and unfilled halves are\n * distinguishable only by the thumb.\n */\n showValue?: boolean;\n /** Extra classes. Merged last, so they win conflicts with ours. */\n className?: string;\n}\n\nexport function Slider({\n label,\n labelHidden,\n showValue = true,\n className,\n ...props\n}: SliderProps) {\n return (\n <AriaSlider {...props} data-slot=\"slider\" className={cn(sliderVariants(), className)}>\n <div className={cn(\"flex items-center justify-between gap-2\", labelHidden && \"sr-only\")}>\n <AriaLabel\n data-slot=\"slider-label\"\n className=\"text-sm leading-(--leading-normal) text-foreground\"\n >\n {label}\n </AriaLabel>\n {showValue ? (\n <AriaSliderOutput\n data-slot=\"slider-output\"\n className=\"text-sm leading-(--leading-normal) text-muted-foreground\"\n />\n ) : null}\n </div>\n\n <AriaSliderTrack data-slot=\"slider-track\" className={sliderTrackVariants()}>\n {({ state }) => (\n <>\n <div data-slot=\"slider-bar\" className={sliderBarVariants()} />\n <div\n data-slot=\"slider-fill\"\n className={sliderFillVariants()}\n style={{ width: `${state.getThumbPercent(0) * 100}%` }}\n />\n <AriaSliderThumb data-slot=\"slider-thumb\" className={sliderThumbVariants()} />\n </>\n )}\n </AriaSliderTrack>\n </AriaSlider>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8CA,MAAa,iBAAiB,IAAI,CAAC,iDAAiD,CAAC;AAErF,MAAa,sBAAsB,IAAI,CACrC,qCAGA,KACF,CAAC;AAED,MAAa,oBAAoB,IAAI,CACnC,+CACA,uDACF,CAAC;AAED,MAAa,qBAAqB,IAAI,CAAC,6CAA6C,CAAC;AAErF,MAAa,sBAAsB,IAAI;CAGrC;CACA;CACA;CAEA;CACA;CACA;CACA;CACA;CAIA;AACF,CAAC;AA+BD,SAAgBA,SAAO,EACrB,OACA,aACA,YAAY,MACZ,WACA,GAAG,SACW;CACd,OACE,qBAACC,QAAD;EAAY,GAAI;EAAO,aAAU;EAAS,WAAW,GAAG,eAAe,GAAG,SAAS;EAAnF,UAAA,CACE,qBAAC,OAAD;GAAK,WAAW,GAAG,2CAA2C,eAAe,SAAS;GAAtF,UAAA,CACE,oBAACC,OAAD;IACE,aAAU;IACV,WAAU;IAET,UAAA;GACQ,CAAA,GACV,YACC,oBAACC,cAAD;IACE,aAAU;IACV,WAAU;GACX,CAAA,IACC,IACD;EAEL,CAAA,GAAA,oBAACC,aAAD;GAAiB,aAAU;GAAe,WAAW,oBAAoB;GACrE,WAAA,EAAE,YACF,qBAAA,UAAA,EAAA,UAAA;IACE,oBAAC,OAAD;KAAK,aAAU;KAAa,WAAW,kBAAkB;IAAI,CAAA;IAC7D,oBAAC,OAAD;KACE,aAAU;KACV,WAAW,mBAAmB;KAC9B,OAAO,EAAE,OAAO,GAAG,MAAM,gBAAgB,CAAC,IAAI,IAAI,GAAG;IACtD,CAAA;IACD,oBAACC,aAAD;KAAiB,aAAU;KAAe,WAAW,oBAAoB;IAAI,CAAA;GAC7E,EAAA,CAAA;EAEW,CAAA,CACP;;AAEhB"}
@@ -0,0 +1,2 @@
1
+ import { Spinner, SpinnerProps, spinnerVariants } from "./spinner.js";
2
+ export { Spinner, type SpinnerProps, spinnerVariants };
@@ -0,0 +1,2 @@
1
+ import { Spinner, spinnerVariants } from "./spinner.js";
2
+ export { Spinner, spinnerVariants };
@@ -0,0 +1,75 @@
1
+ import { VariantProps } from "class-variance-authority";
2
+ import { ComponentPropsWithoutRef } from "react";
3
+ //#region src/spinner/spinner.d.ts
4
+ /**
5
+ * Spinner.
6
+ *
7
+ * There is no React Aria base: RAC ships no busy indicator, and a rotating
8
+ * ring has no interaction model to inherit. Because it is not RAC-backed its
9
+ * props follow ARCHITECTURE section 7 rule 1 -- VariantProps &
10
+ * ComponentPropsWithoutRef -- rather than the Button's Omit-from-React-Aria
11
+ * departure, which exists only to stop RAC-ignored props being advertised.
12
+ *
13
+ * Every string below is a STATIC literal. Tailwind scans source text, so a
14
+ * class assembled at runtime is invisible to it and would never be emitted.
15
+ *
16
+ * THE LEGACY WOULD HAVE SHIPPED INVISIBLE. Its ring is
17
+ * `border-gray-300 border-t-blue-600` and its rotation is `animate-spin`.
18
+ * All three of those classes emit NO RULE in this repo: `--color-*: initial`
19
+ * deleted Tailwind's default palette, and this package imports only
20
+ * tailwindcss/utilities.css, never the default theme, so `--animate-spin` and
21
+ * `@keyframes spin` did not exist either. A literal port would have compiled,
22
+ * linted and type-checked into a static, colourless box.
23
+ */
24
+ export declare const spinnerVariants: (props?: ({
25
+ size?: "sm" | "md" | "lg" | null | undefined;
26
+ } & import("class-variance-authority/types").ClassProp) | undefined) => string;
27
+ /**
28
+ * A busy indicator.
29
+ *
30
+ * Announces itself. Use it for an operation the user is waiting on; for
31
+ * content-shaped placeholders during a page load, reach for `Skeleton`
32
+ * instead, which is deliberately silent because thirty-three empty boxes
33
+ * announcing themselves is noise.
34
+ *
35
+ * @example
36
+ * <Spinner label="Loading campaigns" />
37
+ * <Spinner label="Saving" size="sm" />
38
+ */
39
+ export interface SpinnerProps extends Omit<ComponentPropsWithoutRef<"span">, "children" | "role" | "aria-label">, VariantProps<typeof spinnerVariants> {
40
+ /**
41
+ * What is loading. Becomes the spinner's accessible name.
42
+ *
43
+ * REQUIRED, so a nameless spinner is a type error rather than an omission.
44
+ * The legacy component has no role, no name and no announced text, and it is
45
+ * only about 1% of the spinning in that app -- there are also 63 lucide
46
+ * `Loader2` spins and 8 hand-rolled divs, none of which announce either.
47
+ * A required name is what makes that unrepeatable here.
48
+ *
49
+ * A `string` and not a `ReactNode` because it becomes an `aria-label`, which
50
+ * is a DOM string attribute. `Progress.label` is a ReactNode only because
51
+ * React Aria renders it; the difference is forced by the mechanism rather
52
+ * than chosen.
53
+ *
54
+ * There is no `labelHidden` counterpart: not one legacy call site renders
55
+ * text through the spinner, and the one component that shows a message
56
+ * composes its own heading beside one. That is a layout, not a prop.
57
+ */
58
+ label: string;
59
+ /**
60
+ * Diameter, from the icon scale.
61
+ *
62
+ * All three steps are used by the legacy. The values come from
63
+ * `--icon-sm/md/lg` rather than being restated, because that app already has
64
+ * two spinners sharing this exact union and disagreeing on every pixel --
65
+ * `h-4/6/8` against `h-8/12/16`.
66
+ *
67
+ * @default "md"
68
+ */
69
+ size?: VariantProps<typeof spinnerVariants>["size"];
70
+ /** Extra classes. Merged last, so they win conflicts with ours. */
71
+ className?: string;
72
+ }
73
+ export declare function Spinner({ className, size, label, ...props }: SpinnerProps): import("react").JSX.Element;
74
+ //#endregion
75
+ //# sourceMappingURL=spinner.d.ts.map