@misoto22/design 0.3.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.
Files changed (191) hide show
  1. package/LICENSE +21 -0
  2. package/dist/components/Accordion/Accordion.d.ts +34 -0
  3. package/dist/components/Accordion/Accordion.js +37 -0
  4. package/dist/components/Accordion/Accordion.js.map +1 -0
  5. package/dist/components/Alert/Alert.d.ts +32 -0
  6. package/dist/components/Alert/Alert.js +49 -0
  7. package/dist/components/Alert/Alert.js.map +1 -0
  8. package/dist/components/AppShell/AppShell.d.ts +38 -0
  9. package/dist/components/AppShell/AppShell.js +92 -0
  10. package/dist/components/AppShell/AppShell.js.map +1 -0
  11. package/dist/components/Avatar/Avatar.d.ts +29 -0
  12. package/dist/components/Avatar/Avatar.js +39 -0
  13. package/dist/components/Avatar/Avatar.js.map +1 -0
  14. package/dist/components/Badge/Badge.d.ts +30 -0
  15. package/dist/components/Badge/Badge.js +29 -0
  16. package/dist/components/Badge/Badge.js.map +1 -0
  17. package/dist/components/Breadcrumb/Breadcrumb.d.ts +31 -0
  18. package/dist/components/Breadcrumb/Breadcrumb.js +31 -0
  19. package/dist/components/Breadcrumb/Breadcrumb.js.map +1 -0
  20. package/dist/components/Button/Button.d.ts +70 -0
  21. package/dist/components/Button/Button.js +81 -0
  22. package/dist/components/Button/Button.js.map +1 -0
  23. package/dist/components/Calendar/Calendar.d.ts +28 -0
  24. package/dist/components/Calendar/Calendar.js +87 -0
  25. package/dist/components/Calendar/Calendar.js.map +1 -0
  26. package/dist/components/Card/Card.d.ts +49 -0
  27. package/dist/components/Card/Card.js +64 -0
  28. package/dist/components/Card/Card.js.map +1 -0
  29. package/dist/components/Checkbox/Checkbox.d.ts +23 -0
  30. package/dist/components/Checkbox/Checkbox.js +24 -0
  31. package/dist/components/Checkbox/Checkbox.js.map +1 -0
  32. package/dist/components/Collapsible/Collapsible.d.ts +31 -0
  33. package/dist/components/Collapsible/Collapsible.js +35 -0
  34. package/dist/components/Collapsible/Collapsible.js.map +1 -0
  35. package/dist/components/Combobox/Combobox.d.ts +63 -0
  36. package/dist/components/Combobox/Combobox.js +151 -0
  37. package/dist/components/Combobox/Combobox.js.map +1 -0
  38. package/dist/components/Command/Command.d.ts +98 -0
  39. package/dist/components/Command/Command.js +166 -0
  40. package/dist/components/Command/Command.js.map +1 -0
  41. package/dist/components/ContextMenu/ContextMenu.d.ts +38 -0
  42. package/dist/components/ContextMenu/ContextMenu.js +79 -0
  43. package/dist/components/ContextMenu/ContextMenu.js.map +1 -0
  44. package/dist/components/DatePicker/DatePicker.d.ts +98 -0
  45. package/dist/components/DatePicker/DatePicker.js +214 -0
  46. package/dist/components/DatePicker/DatePicker.js.map +1 -0
  47. package/dist/components/Dialog/Dialog.d.ts +48 -0
  48. package/dist/components/Dialog/Dialog.js +72 -0
  49. package/dist/components/Dialog/Dialog.js.map +1 -0
  50. package/dist/components/DropdownMenu/DropdownMenu.d.ts +49 -0
  51. package/dist/components/DropdownMenu/DropdownMenu.js +89 -0
  52. package/dist/components/DropdownMenu/DropdownMenu.js.map +1 -0
  53. package/dist/components/EmptyState/EmptyState.d.ts +31 -0
  54. package/dist/components/EmptyState/EmptyState.js +30 -0
  55. package/dist/components/EmptyState/EmptyState.js.map +1 -0
  56. package/dist/components/ErrorState/ErrorState.d.ts +34 -0
  57. package/dist/components/ErrorState/ErrorState.js +32 -0
  58. package/dist/components/ErrorState/ErrorState.js.map +1 -0
  59. package/dist/components/Field/Field.d.ts +44 -0
  60. package/dist/components/Field/Field.js +54 -0
  61. package/dist/components/Field/Field.js.map +1 -0
  62. package/dist/components/FigureBand/FigureBand.d.ts +51 -0
  63. package/dist/components/FigureBand/FigureBand.js +52 -0
  64. package/dist/components/FigureBand/FigureBand.js.map +1 -0
  65. package/dist/components/FloatingIconButton/FloatingIconButton.d.ts +29 -0
  66. package/dist/components/FloatingIconButton/FloatingIconButton.js +32 -0
  67. package/dist/components/FloatingIconButton/FloatingIconButton.js.map +1 -0
  68. package/dist/components/Input/Input.d.ts +22 -0
  69. package/dist/components/Input/Input.js +21 -0
  70. package/dist/components/Input/Input.js.map +1 -0
  71. package/dist/components/Kbd/Kbd.d.ts +20 -0
  72. package/dist/components/Kbd/Kbd.js +21 -0
  73. package/dist/components/Kbd/Kbd.js.map +1 -0
  74. package/dist/components/LinkArrow/LinkArrow.d.ts +24 -0
  75. package/dist/components/LinkArrow/LinkArrow.js +24 -0
  76. package/dist/components/LinkArrow/LinkArrow.js.map +1 -0
  77. package/dist/components/NativeSelect/NativeSelect.d.ts +27 -0
  78. package/dist/components/NativeSelect/NativeSelect.js +46 -0
  79. package/dist/components/NativeSelect/NativeSelect.js.map +1 -0
  80. package/dist/components/NavItem/NavItem.d.ts +39 -0
  81. package/dist/components/NavItem/NavItem.js +31 -0
  82. package/dist/components/NavItem/NavItem.js.map +1 -0
  83. package/dist/components/Pagination/Pagination.d.ts +44 -0
  84. package/dist/components/Pagination/Pagination.js +109 -0
  85. package/dist/components/Pagination/Pagination.js.map +1 -0
  86. package/dist/components/Popover/Popover.d.ts +39 -0
  87. package/dist/components/Popover/Popover.js +57 -0
  88. package/dist/components/Popover/Popover.js.map +1 -0
  89. package/dist/components/Progress/Progress.d.ts +31 -0
  90. package/dist/components/Progress/Progress.js +45 -0
  91. package/dist/components/Progress/Progress.js.map +1 -0
  92. package/dist/components/RadioGroup/RadioGroup.d.ts +43 -0
  93. package/dist/components/RadioGroup/RadioGroup.js +43 -0
  94. package/dist/components/RadioGroup/RadioGroup.js.map +1 -0
  95. package/dist/components/ScrollArea/ScrollArea.d.ts +31 -0
  96. package/dist/components/ScrollArea/ScrollArea.js +54 -0
  97. package/dist/components/ScrollArea/ScrollArea.js.map +1 -0
  98. package/dist/components/SearchableMenu/SearchableMenu.d.ts +59 -0
  99. package/dist/components/SearchableMenu/SearchableMenu.js +79 -0
  100. package/dist/components/SearchableMenu/SearchableMenu.js.map +1 -0
  101. package/dist/components/Select/Select.d.ts +71 -0
  102. package/dist/components/Select/Select.js +114 -0
  103. package/dist/components/Select/Select.js.map +1 -0
  104. package/dist/components/Separator/Separator.d.ts +30 -0
  105. package/dist/components/Separator/Separator.js +35 -0
  106. package/dist/components/Separator/Separator.js.map +1 -0
  107. package/dist/components/Sheet/Sheet.d.ts +38 -0
  108. package/dist/components/Sheet/Sheet.js +68 -0
  109. package/dist/components/Sheet/Sheet.js.map +1 -0
  110. package/dist/components/Skeleton/Skeleton.d.ts +63 -0
  111. package/dist/components/Skeleton/Skeleton.js +44 -0
  112. package/dist/components/Skeleton/Skeleton.js.map +1 -0
  113. package/dist/components/Slider/Slider.d.ts +35 -0
  114. package/dist/components/Slider/Slider.js +52 -0
  115. package/dist/components/Slider/Slider.js.map +1 -0
  116. package/dist/components/Spinner/Spinner.d.ts +50 -0
  117. package/dist/components/Spinner/Spinner.js +48 -0
  118. package/dist/components/Spinner/Spinner.js.map +1 -0
  119. package/dist/components/StatusDot/StatusDot.d.ts +29 -0
  120. package/dist/components/StatusDot/StatusDot.js +44 -0
  121. package/dist/components/StatusDot/StatusDot.js.map +1 -0
  122. package/dist/components/StatusPill/StatusPill.d.ts +25 -0
  123. package/dist/components/StatusPill/StatusPill.js +31 -0
  124. package/dist/components/StatusPill/StatusPill.js.map +1 -0
  125. package/dist/components/Switch/Switch.d.ts +23 -0
  126. package/dist/components/Switch/Switch.js +23 -0
  127. package/dist/components/Switch/Switch.js.map +1 -0
  128. package/dist/components/Table/Table.d.ts +96 -0
  129. package/dist/components/Table/Table.js +136 -0
  130. package/dist/components/Table/Table.js.map +1 -0
  131. package/dist/components/Tabs/Tabs.d.ts +35 -0
  132. package/dist/components/Tabs/Tabs.js +48 -0
  133. package/dist/components/Tabs/Tabs.js.map +1 -0
  134. package/dist/components/Tag/Tag.d.ts +28 -0
  135. package/dist/components/Tag/Tag.js +22 -0
  136. package/dist/components/Tag/Tag.js.map +1 -0
  137. package/dist/components/Textarea/Textarea.d.ts +20 -0
  138. package/dist/components/Textarea/Textarea.js +32 -0
  139. package/dist/components/Textarea/Textarea.js.map +1 -0
  140. package/dist/components/Toast/Toast.d.ts +24 -0
  141. package/dist/components/Toast/Toast.js +41 -0
  142. package/dist/components/Toast/Toast.js.map +1 -0
  143. package/dist/components/ToggleGroup/ToggleGroup.d.ts +41 -0
  144. package/dist/components/ToggleGroup/ToggleGroup.js +78 -0
  145. package/dist/components/ToggleGroup/ToggleGroup.js.map +1 -0
  146. package/dist/components/Tooltip/Tooltip.d.ts +43 -0
  147. package/dist/components/Tooltip/Tooltip.js +40 -0
  148. package/dist/components/Tooltip/Tooltip.js.map +1 -0
  149. package/dist/fonts/hanken-grotesk-300.woff2 +0 -0
  150. package/dist/fonts/hanken-grotesk-400.woff2 +0 -0
  151. package/dist/fonts/hanken-grotesk-500.woff2 +0 -0
  152. package/dist/fonts/hanken-grotesk-600.woff2 +0 -0
  153. package/dist/fonts/hanken-grotesk-700.woff2 +0 -0
  154. package/dist/fonts/ibm-plex-mono-400.woff2 +0 -0
  155. package/dist/fonts/ibm-plex-mono-500.woff2 +0 -0
  156. package/dist/fonts/ibm-plex-mono-600.woff2 +0 -0
  157. package/dist/fonts/newsreader-300.woff2 +0 -0
  158. package/dist/fonts/newsreader-400.woff2 +0 -0
  159. package/dist/fonts/newsreader-500.woff2 +0 -0
  160. package/dist/fonts/newsreader-600.woff2 +0 -0
  161. package/dist/fonts.css +96 -0
  162. package/dist/index.d.ts +77 -0
  163. package/dist/index.js +63 -0
  164. package/dist/index.js.map +1 -0
  165. package/dist/keyframes.css +212 -0
  166. package/dist/lib/cn.d.ts +21 -0
  167. package/dist/lib/cn.js +9 -0
  168. package/dist/lib/cn.js.map +1 -0
  169. package/dist/lib/control.d.ts +31 -0
  170. package/dist/lib/control.js +14 -0
  171. package/dist/lib/control.js.map +1 -0
  172. package/dist/lib/index.d.ts +4 -0
  173. package/dist/lib/index.js +11 -0
  174. package/dist/lib/index.js.map +1 -0
  175. package/dist/lib/overlay-container.d.ts +38 -0
  176. package/dist/lib/overlay-container.js +15 -0
  177. package/dist/lib/overlay-container.js.map +1 -0
  178. package/dist/lib/useSelectionIndicator.d.ts +29 -0
  179. package/dist/lib/useSelectionIndicator.js +45 -0
  180. package/dist/lib/useSelectionIndicator.js.map +1 -0
  181. package/dist/semantic.css +127 -0
  182. package/dist/styles.css +98 -0
  183. package/dist/themes.css +115 -0
  184. package/dist/tokens/brand.d.ts +47 -0
  185. package/dist/tokens/brand.js +38 -0
  186. package/dist/tokens/brand.js.map +1 -0
  187. package/dist/tokens.css +323 -0
  188. package/dist/tokens.d.ts +153 -0
  189. package/dist/tokens.js +766 -0
  190. package/dist/tokens.json +765 -0
  191. package/package.json +130 -0
@@ -0,0 +1,54 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area";
4
+ import { cn } from "../../lib/cn.js";
5
+ function ScrollArea({
6
+ label,
7
+ orientation = "vertical",
8
+ className,
9
+ children,
10
+ ...props
11
+ }) {
12
+ return /* @__PURE__ */ jsxs(
13
+ ScrollAreaPrimitive.Root,
14
+ {
15
+ type: "hover",
16
+ className: cn("relative overflow-hidden", className),
17
+ ...props,
18
+ children: [
19
+ /* @__PURE__ */ jsx(
20
+ ScrollAreaPrimitive.Viewport,
21
+ {
22
+ tabIndex: 0,
23
+ role: "region",
24
+ "aria-label": label,
25
+ className: "size-full rounded-[inherit]",
26
+ children
27
+ }
28
+ ),
29
+ (orientation === "vertical" || orientation === "both") && /* @__PURE__ */ jsx(Bar, { orientation: "vertical" }),
30
+ (orientation === "horizontal" || orientation === "both") && /* @__PURE__ */ jsx(Bar, { orientation: "horizontal" }),
31
+ /* @__PURE__ */ jsx(ScrollAreaPrimitive.Corner, {})
32
+ ]
33
+ }
34
+ );
35
+ }
36
+ function Bar({ orientation }) {
37
+ return /* @__PURE__ */ jsx(
38
+ ScrollAreaPrimitive.Scrollbar,
39
+ {
40
+ orientation,
41
+ className: cn(
42
+ "flex touch-none select-none p-0.5 transition-colors duration-(--duration-fast)",
43
+ orientation === "vertical" ? "h-full w-2" : "h-2 flex-col"
44
+ ),
45
+ children: /* @__PURE__ */ jsx(ScrollAreaPrimitive.Thumb, { className: "relative flex-1 rounded-(--radius-pill) bg-(--rule-2)" })
46
+ }
47
+ );
48
+ }
49
+ var ScrollArea_default = ScrollArea;
50
+ export {
51
+ ScrollArea,
52
+ ScrollArea_default as default
53
+ };
54
+ //# sourceMappingURL=ScrollArea.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/ScrollArea/ScrollArea.tsx"],"sourcesContent":["'use client'\n\nimport * as ScrollAreaPrimitive from '@radix-ui/react-scroll-area'\nimport type { ComponentProps } from 'react'\nimport { cn } from '../../lib/cn'\n\nexport interface ScrollAreaProps extends ComponentProps<typeof ScrollAreaPrimitive.Root> {\n /**\n * Names the region. Required, and not decoration: a scroll container is a\n * keyboard stop, and an unnamed stop announces \"group\" and nothing else.\n */\n label: string\n orientation?: 'vertical' | 'horizontal' | 'both'\n}\n\n/**\n * A box that scrolls, with a scrollbar that looks the same on every platform.\n *\n * The reason to reach for this over `overflow-auto` is not the scrollbar — it\n * is that Radix keeps the viewport focusable and the bar operable, which a bare\n * overflow container does not. A scrollable region whose contents are not\n * themselves focusable is unreachable by keyboard: there is nothing to Tab to,\n * so everything past the fold does not exist without a mouse.\n *\n * For a page-level or prose scroll, the `scroll-slim` utility is lighter and\n * needs no component. This is for a bounded panel: a long option list, a log,\n * a sidebar that outgrows its column.\n *\n * @example\n * <ScrollArea label=\"Deploy log\" className=\"h-48\">…</ScrollArea>\n */\nexport function ScrollArea({\n label,\n orientation = 'vertical',\n className,\n children,\n ...props\n}: ScrollAreaProps) {\n return (\n <ScrollAreaPrimitive.Root\n type=\"hover\"\n className={cn('relative overflow-hidden', className)}\n {...props}\n >\n <ScrollAreaPrimitive.Viewport\n tabIndex={0}\n role=\"region\"\n aria-label={label}\n className=\"size-full rounded-[inherit]\"\n >\n {children}\n </ScrollAreaPrimitive.Viewport>\n {(orientation === 'vertical' || orientation === 'both') && <Bar orientation=\"vertical\" />}\n {(orientation === 'horizontal' || orientation === 'both') && <Bar orientation=\"horizontal\" />}\n <ScrollAreaPrimitive.Corner />\n </ScrollAreaPrimitive.Root>\n )\n}\n\nfunction Bar({ orientation }: { orientation: 'vertical' | 'horizontal' }) {\n return (\n <ScrollAreaPrimitive.Scrollbar\n orientation={orientation}\n className={cn(\n 'flex touch-none select-none p-0.5 transition-colors duration-(--duration-fast)',\n orientation === 'vertical' ? 'h-full w-2' : 'h-2 flex-col',\n )}\n >\n <ScrollAreaPrimitive.Thumb className=\"relative flex-1 rounded-(--radius-pill) bg-(--rule-2)\" />\n </ScrollAreaPrimitive.Scrollbar>\n )\n}\n\nexport default ScrollArea\n"],"mappings":";AAuCI,SAKE,KALF;AArCJ,YAAY,yBAAyB;AAErC,SAAS,UAAU;AA2BZ,SAAS,WAAW;AAAA,EACzB;AAAA,EACA,cAAc;AAAA,EACd;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAAoB;AAClB,SACE;AAAA,IAAC,oBAAoB;AAAA,IAApB;AAAA,MACC,MAAK;AAAA,MACL,WAAW,GAAG,4BAA4B,SAAS;AAAA,MAClD,GAAG;AAAA,MAEJ;AAAA;AAAA,UAAC,oBAAoB;AAAA,UAApB;AAAA,YACC,UAAU;AAAA,YACV,MAAK;AAAA,YACL,cAAY;AAAA,YACZ,WAAU;AAAA,YAET;AAAA;AAAA,QACH;AAAA,SACE,gBAAgB,cAAc,gBAAgB,WAAW,oBAAC,OAAI,aAAY,YAAW;AAAA,SACrF,gBAAgB,gBAAgB,gBAAgB,WAAW,oBAAC,OAAI,aAAY,cAAa;AAAA,QAC3F,oBAAC,oBAAoB,QAApB,EAA2B;AAAA;AAAA;AAAA,EAC9B;AAEJ;AAEA,SAAS,IAAI,EAAE,YAAY,GAA+C;AACxE,SACE;AAAA,IAAC,oBAAoB;AAAA,IAApB;AAAA,MACC;AAAA,MACA,WAAW;AAAA,QACT;AAAA,QACA,gBAAgB,aAAa,eAAe;AAAA,MAC9C;AAAA,MAEA,8BAAC,oBAAoB,OAApB,EAA0B,WAAU,yDAAwD;AAAA;AAAA,EAC/F;AAEJ;AAEA,IAAO,qBAAQ;","names":[]}
@@ -0,0 +1,59 @@
1
+ import * as react from 'react';
2
+ import { ReactNode } from 'react';
3
+
4
+ interface MenuAction {
5
+ /** Stable id, and what the filter matches on. */
6
+ id: string;
7
+ label: ReactNode;
8
+ /** Plain text the filter should match beyond the label. */
9
+ keywords?: string[];
10
+ /** Printed at the end of the row. */
11
+ shortcut?: string;
12
+ /** Optional group heading. Rows with the same value are shown together. */
13
+ group?: string;
14
+ disabled?: boolean;
15
+ destructive?: boolean;
16
+ onSelect: () => void;
17
+ }
18
+ interface SearchableMenuProps {
19
+ actions: MenuAction[];
20
+ /** The trigger's text. */
21
+ children: ReactNode;
22
+ /** Names the menu for assistive tech. */
23
+ label: string;
24
+ searchPlaceholder?: string;
25
+ emptyMessage?: string;
26
+ align?: 'start' | 'center' | 'end';
27
+ className?: string;
28
+ }
29
+ /**
30
+ * A menu of actions you can type into.
31
+ *
32
+ * The fourth corner of a square the system otherwise had three of, and the
33
+ * distinction is worth stating because reaching for the wrong one is easy:
34
+ *
35
+ * | | few options | many options |
36
+ * |---|---|---|
37
+ * | **sets a value** | `Select` | `Combobox` |
38
+ * | **runs an action** | `DropdownMenu` | `SearchableMenu` |
39
+ *
40
+ * A `DropdownMenu` past about a dozen rows stops being scannable, and the
41
+ * usual response — nesting submenus — makes it worse. This is the same list
42
+ * with a filter over it.
43
+ *
44
+ * Not the same thing as `Command`: that is a page-level palette, usually modal
45
+ * and usually bound to ⌘K. This is anchored to a control, like the menu it
46
+ * replaces.
47
+ *
48
+ * The rows are `option`s inside a listbox rather than `menuitem`s, because that
49
+ * is what the filtering pattern requires — the highlight moves through
50
+ * `aria-activedescendant` while focus stays in the input, and a menu cannot do
51
+ * that. The trade is deliberate: a menu that cannot be filtered is worse for
52
+ * the reader than a listbox that runs actions.
53
+ *
54
+ * @example
55
+ * <SearchableMenu label="Actions" actions={ACTIONS}>Actions</SearchableMenu>
56
+ */
57
+ declare function SearchableMenu({ actions, children, label, searchPlaceholder, emptyMessage, align, className, }: SearchableMenuProps): react.JSX.Element;
58
+
59
+ export { type MenuAction, SearchableMenu, type SearchableMenuProps, SearchableMenu as default };
@@ -0,0 +1,79 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { ChevronDown } from "lucide-react";
4
+ import { useState } from "react";
5
+ import { cn } from "../../lib/cn.js";
6
+ import {
7
+ Command,
8
+ CommandEmpty,
9
+ CommandGroup,
10
+ CommandInput,
11
+ CommandItem,
12
+ CommandList,
13
+ CommandSeparator
14
+ } from "../Command/Command.js";
15
+ import { Popover, PopoverContent, PopoverTrigger } from "../Popover/Popover.js";
16
+ function SearchableMenu({
17
+ actions,
18
+ children,
19
+ label,
20
+ searchPlaceholder = "Filter\u2026",
21
+ emptyMessage = "Nothing matches.",
22
+ align = "start",
23
+ className
24
+ }) {
25
+ const [open, setOpen] = useState(false);
26
+ const groups = [];
27
+ for (const action of actions) {
28
+ const last = groups.at(-1);
29
+ if (last && last.name === action.group) last.items.push(action);
30
+ else groups.push({ name: action.group, items: [action] });
31
+ }
32
+ return /* @__PURE__ */ jsxs(Popover, { open, onOpenChange: setOpen, children: [
33
+ /* @__PURE__ */ jsxs(
34
+ PopoverTrigger,
35
+ {
36
+ "aria-label": label,
37
+ className: cn(
38
+ "inline-flex min-h-(--control-h-sm) items-center gap-2 rounded-(--radius-pill) border border-(--rule-2) px-4 text-sm text-(--ink) transition-colors duration-(--duration-fast) hover:border-(--ink)",
39
+ className
40
+ ),
41
+ children: [
42
+ children,
43
+ /* @__PURE__ */ jsx(ChevronDown, { size: 14, strokeWidth: 1.5, "aria-hidden": true, className: "shrink-0 text-(--ink-3-aa)" })
44
+ ]
45
+ }
46
+ ),
47
+ /* @__PURE__ */ jsx(PopoverContent, { label, align, className: "w-64 overflow-hidden p-0", children: /* @__PURE__ */ jsxs(Command, { label: `${label}: ${searchPlaceholder}`, className: "rounded-none border-0", children: [
48
+ /* @__PURE__ */ jsx(CommandInput, { placeholder: searchPlaceholder }),
49
+ /* @__PURE__ */ jsxs(CommandList, { children: [
50
+ /* @__PURE__ */ jsx(CommandEmpty, { children: emptyMessage }),
51
+ groups.map((group, index) => /* @__PURE__ */ jsxs("div", { children: [
52
+ index > 0 && /* @__PURE__ */ jsx(CommandSeparator, {}),
53
+ /* @__PURE__ */ jsx(CommandGroup, { heading: group.name, children: group.items.map((action) => /* @__PURE__ */ jsx(
54
+ CommandItem,
55
+ {
56
+ value: action.id,
57
+ keywords: action.keywords,
58
+ disabled: action.disabled,
59
+ shortcut: action.shortcut,
60
+ onSelect: () => {
61
+ setOpen(false);
62
+ action.onSelect();
63
+ },
64
+ className: action.destructive ? "text-(--danger)" : void 0,
65
+ children: action.label
66
+ },
67
+ action.id
68
+ )) })
69
+ ] }, group.name ?? index))
70
+ ] })
71
+ ] }) })
72
+ ] });
73
+ }
74
+ var SearchableMenu_default = SearchableMenu;
75
+ export {
76
+ SearchableMenu,
77
+ SearchableMenu_default as default
78
+ };
79
+ //# sourceMappingURL=SearchableMenu.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/SearchableMenu/SearchableMenu.tsx"],"sourcesContent":["'use client'\n\nimport { ChevronDown } from 'lucide-react'\nimport { useState, type ReactNode } from 'react'\nimport { cn } from '../../lib/cn'\nimport {\n Command,\n CommandEmpty,\n CommandGroup,\n CommandInput,\n CommandItem,\n CommandList,\n CommandSeparator,\n} from '../Command/Command'\nimport { Popover, PopoverContent, PopoverTrigger } from '../Popover/Popover'\n\nexport interface MenuAction {\n /** Stable id, and what the filter matches on. */\n id: string\n label: ReactNode\n /** Plain text the filter should match beyond the label. */\n keywords?: string[]\n /** Printed at the end of the row. */\n shortcut?: string\n /** Optional group heading. Rows with the same value are shown together. */\n group?: string\n disabled?: boolean\n destructive?: boolean\n onSelect: () => void\n}\n\nexport interface SearchableMenuProps {\n actions: MenuAction[]\n /** The trigger's text. */\n children: ReactNode\n /** Names the menu for assistive tech. */\n label: string\n searchPlaceholder?: string\n emptyMessage?: string\n align?: 'start' | 'center' | 'end'\n className?: string\n}\n\n/**\n * A menu of actions you can type into.\n *\n * The fourth corner of a square the system otherwise had three of, and the\n * distinction is worth stating because reaching for the wrong one is easy:\n *\n * | | few options | many options |\n * |---|---|---|\n * | **sets a value** | `Select` | `Combobox` |\n * | **runs an action** | `DropdownMenu` | `SearchableMenu` |\n *\n * A `DropdownMenu` past about a dozen rows stops being scannable, and the\n * usual response — nesting submenus — makes it worse. This is the same list\n * with a filter over it.\n *\n * Not the same thing as `Command`: that is a page-level palette, usually modal\n * and usually bound to ⌘K. This is anchored to a control, like the menu it\n * replaces.\n *\n * The rows are `option`s inside a listbox rather than `menuitem`s, because that\n * is what the filtering pattern requires — the highlight moves through\n * `aria-activedescendant` while focus stays in the input, and a menu cannot do\n * that. The trade is deliberate: a menu that cannot be filtered is worse for\n * the reader than a listbox that runs actions.\n *\n * @example\n * <SearchableMenu label=\"Actions\" actions={ACTIONS}>Actions</SearchableMenu>\n */\nexport function SearchableMenu({\n actions,\n children,\n label,\n searchPlaceholder = 'Filter…',\n emptyMessage = 'Nothing matches.',\n align = 'start',\n className,\n}: SearchableMenuProps) {\n const [open, setOpen] = useState(false)\n\n // Grouped in declaration order, so a caller controls the arrangement by the\n // order they list actions rather than by an extra prop.\n const groups: { name: string | undefined; items: MenuAction[] }[] = []\n for (const action of actions) {\n const last = groups.at(-1)\n if (last && last.name === action.group) last.items.push(action)\n else groups.push({ name: action.group, items: [action] })\n }\n\n return (\n <Popover open={open} onOpenChange={setOpen}>\n <PopoverTrigger\n aria-label={label}\n className={cn(\n 'inline-flex min-h-(--control-h-sm) items-center gap-2 rounded-(--radius-pill) border border-(--rule-2) px-4 text-sm text-(--ink) transition-colors duration-(--duration-fast) hover:border-(--ink)',\n className,\n )}\n >\n {children}\n <ChevronDown size={14} strokeWidth={1.5} aria-hidden className=\"shrink-0 text-(--ink-3-aa)\" />\n </PopoverTrigger>\n <PopoverContent label={label} align={align} className=\"w-64 overflow-hidden p-0\">\n <Command label={`${label}: ${searchPlaceholder}`} className=\"rounded-none border-0\">\n <CommandInput placeholder={searchPlaceholder} />\n <CommandList>\n <CommandEmpty>{emptyMessage}</CommandEmpty>\n {groups.map((group, index) => (\n <div key={group.name ?? index}>\n {index > 0 && <CommandSeparator />}\n <CommandGroup heading={group.name}>\n {group.items.map((action) => (\n <CommandItem\n key={action.id}\n value={action.id}\n keywords={action.keywords}\n disabled={action.disabled}\n shortcut={action.shortcut}\n onSelect={() => {\n setOpen(false)\n action.onSelect()\n }}\n className={action.destructive ? 'text-(--danger)' : undefined}\n >\n {action.label}\n </CommandItem>\n ))}\n </CommandGroup>\n </div>\n ))}\n </CommandList>\n </Command>\n </PopoverContent>\n </Popover>\n )\n}\n\nexport default SearchableMenu\n"],"mappings":";AA6FM,SAQE,KARF;AA3FN,SAAS,mBAAmB;AAC5B,SAAS,gBAAgC;AACzC,SAAS,UAAU;AACnB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,SAAS,gBAAgB,sBAAsB;AAyDjD,SAAS,eAAe;AAAA,EAC7B;AAAA,EACA;AAAA,EACA;AAAA,EACA,oBAAoB;AAAA,EACpB,eAAe;AAAA,EACf,QAAQ;AAAA,EACR;AACF,GAAwB;AACtB,QAAM,CAAC,MAAM,OAAO,IAAI,SAAS,KAAK;AAItC,QAAM,SAA8D,CAAC;AACrE,aAAW,UAAU,SAAS;AAC5B,UAAM,OAAO,OAAO,GAAG,EAAE;AACzB,QAAI,QAAQ,KAAK,SAAS,OAAO,MAAO,MAAK,MAAM,KAAK,MAAM;AAAA,QACzD,QAAO,KAAK,EAAE,MAAM,OAAO,OAAO,OAAO,CAAC,MAAM,EAAE,CAAC;AAAA,EAC1D;AAEA,SACE,qBAAC,WAAQ,MAAY,cAAc,SACjC;AAAA;AAAA,MAAC;AAAA;AAAA,QACC,cAAY;AAAA,QACZ,WAAW;AAAA,UACT;AAAA,UACA;AAAA,QACF;AAAA,QAEC;AAAA;AAAA,UACD,oBAAC,eAAY,MAAM,IAAI,aAAa,KAAK,eAAW,MAAC,WAAU,8BAA6B;AAAA;AAAA;AAAA,IAC9F;AAAA,IACA,oBAAC,kBAAe,OAAc,OAAc,WAAU,4BACpD,+BAAC,WAAQ,OAAO,GAAG,KAAK,KAAK,iBAAiB,IAAI,WAAU,yBAC1D;AAAA,0BAAC,gBAAa,aAAa,mBAAmB;AAAA,MAC9C,qBAAC,eACC;AAAA,4BAAC,gBAAc,wBAAa;AAAA,QAC3B,OAAO,IAAI,CAAC,OAAO,UAClB,qBAAC,SACE;AAAA,kBAAQ,KAAK,oBAAC,oBAAiB;AAAA,UAChC,oBAAC,gBAAa,SAAS,MAAM,MAC1B,gBAAM,MAAM,IAAI,CAAC,WAChB;AAAA,YAAC;AAAA;AAAA,cAEC,OAAO,OAAO;AAAA,cACd,UAAU,OAAO;AAAA,cACjB,UAAU,OAAO;AAAA,cACjB,UAAU,OAAO;AAAA,cACjB,UAAU,MAAM;AACd,wBAAQ,KAAK;AACb,uBAAO,SAAS;AAAA,cAClB;AAAA,cACA,WAAW,OAAO,cAAc,oBAAoB;AAAA,cAEnD,iBAAO;AAAA;AAAA,YAXH,OAAO;AAAA,UAYd,CACD,GACH;AAAA,aAnBQ,MAAM,QAAQ,KAoBxB,CACD;AAAA,SACH;AAAA,OACF,GACF;AAAA,KACF;AAEJ;AAEA,IAAO,yBAAQ;","names":[]}
@@ -0,0 +1,71 @@
1
+ import * as react from 'react';
2
+ import { ComponentProps, ReactNode } from 'react';
3
+ import * as SelectPrimitive from '@radix-ui/react-select';
4
+
5
+ /** Radix Select root, group and label, as typed passthroughs. */
6
+ declare const SelectRoot: react.FC<SelectPrimitive.SelectProps>;
7
+ declare const SelectGroup: react.ForwardRefExoticComponent<SelectPrimitive.SelectGroupProps & react.RefAttributes<HTMLDivElement>>;
8
+ interface SelectProps extends ComponentProps<typeof SelectPrimitive.Root> {
9
+ /**
10
+ * Classes for the option PANEL, not the trigger.
11
+ *
12
+ * `className` styles the trigger, which is the common case. This exists for
13
+ * the uncommon one: a select inside a bounded surface, where the default
14
+ * 18rem of list would cover the thing the reader is choosing for — a year
15
+ * picker over its own calendar, say.
16
+ */
17
+ contentClassName?: string;
18
+ /** Names the control. Required — the trigger shows a value, and a value is not a name. */
19
+ label: string;
20
+ placeholder?: string;
21
+ /** Paints the resting border with `--danger` and reflects `aria-invalid`. */
22
+ invalid?: boolean;
23
+ disabled?: boolean;
24
+ className?: string;
25
+ /** `SelectItem`s, optionally wrapped in `SelectGroup` with a `SelectLabel`. */
26
+ children: ReactNode;
27
+ }
28
+ /**
29
+ * A choice from a list, styled the whole way down.
30
+ *
31
+ * The option list is ours — drawn from the same tokens as everything else, so
32
+ * it does not change typeface, spacing and selection colour the moment it
33
+ * opens. That is the whole reason this replaced the native control as the
34
+ * default: a design system whose most common form control stops being part of
35
+ * the system on click is not a design system, it is a stylesheet for closed
36
+ * states.
37
+ *
38
+ * The keyboard contract is Radix's, which means it is the platform's: typeahead
39
+ * works, the arrows move, Home and End reach the ends, and Escape closes
40
+ * without choosing. That was the one genuine argument for staying native, and
41
+ * it is answered.
42
+ *
43
+ * Past roughly a dozen options, reach for `Combobox` — a list nobody can filter
44
+ * is worse than one they can type into. Where the platform picker is genuinely
45
+ * better — a phone, or a form that must survive without JavaScript — reach for
46
+ * `NativeSelect`.
47
+ *
48
+ * @example
49
+ * <Field label="Region">
50
+ * <Select label="Region" defaultValue="au">
51
+ * <SelectItem value="au">Australia</SelectItem>
52
+ * <SelectItem value="nz">New Zealand</SelectItem>
53
+ * </Select>
54
+ * </Field>
55
+ */
56
+ declare function Select({ label, placeholder, invalid, disabled, className, contentClassName, children, ...props }: SelectProps): react.JSX.Element;
57
+ type SelectItemProps = ComponentProps<typeof SelectPrimitive.Item>;
58
+ /** One option. The tick marks the chosen one; the fill marks the highlighted one. */
59
+ declare function SelectItem({ className, children, ...props }: SelectItemProps): react.JSX.Element;
60
+ /**
61
+ * Mono eyebrow heading for a group of options.
62
+ *
63
+ * Must sit inside a `SelectGroup` — Radix throws otherwise, because a heading
64
+ * with no group is a heading for nothing, and assistive tech would announce it
65
+ * as an option.
66
+ */
67
+ declare function SelectLabel({ className, ...props }: ComponentProps<typeof SelectPrimitive.Label>): react.JSX.Element;
68
+ /** Hairline divider between groups. */
69
+ declare function SelectSeparator({ className, ...props }: ComponentProps<typeof SelectPrimitive.Separator>): react.JSX.Element;
70
+
71
+ export { Select, SelectGroup, SelectItem, type SelectItemProps, SelectLabel, type SelectProps, SelectRoot, SelectSeparator, Select as default };
@@ -0,0 +1,114 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import * as SelectPrimitive from "@radix-ui/react-select";
4
+ import { Check, ChevronDown, ChevronUp } from "lucide-react";
5
+ import { cn } from "../../lib/cn.js";
6
+ import { useOverlayContainer } from "../../lib/overlay-container.js";
7
+ import { CONTROL_BASE, CONTROL_BORDER, isInvalid } from "../../lib/control.js";
8
+ const SelectRoot = SelectPrimitive.Root;
9
+ const SelectGroup = SelectPrimitive.Group;
10
+ function Select({
11
+ label,
12
+ placeholder = "Select\u2026",
13
+ invalid,
14
+ disabled,
15
+ className,
16
+ contentClassName,
17
+ children,
18
+ ...props
19
+ }) {
20
+ const bad = isInvalid(invalid);
21
+ const container = useOverlayContainer();
22
+ return /* @__PURE__ */ jsxs(SelectPrimitive.Root, { disabled, ...props, children: [
23
+ /* @__PURE__ */ jsxs(
24
+ SelectPrimitive.Trigger,
25
+ {
26
+ "aria-label": label,
27
+ "aria-invalid": bad || void 0,
28
+ className: cn(
29
+ CONTROL_BASE,
30
+ "group flex cursor-pointer items-center justify-between gap-2 text-start data-[placeholder]:text-(--ink-3-aa)",
31
+ bad ? CONTROL_BORDER.invalid : CONTROL_BORDER.resting,
32
+ className
33
+ ),
34
+ children: [
35
+ /* @__PURE__ */ jsx(SelectPrimitive.Value, { placeholder }),
36
+ /* @__PURE__ */ jsx(SelectPrimitive.Icon, { asChild: true, children: /* @__PURE__ */ jsx(
37
+ ChevronDown,
38
+ {
39
+ size: 16,
40
+ strokeWidth: 1.5,
41
+ "aria-hidden": true,
42
+ className: "shrink-0 text-(--ink-3-aa) transition-transform duration-(--duration-fast) group-data-[state=open]:rotate-180"
43
+ }
44
+ ) })
45
+ ]
46
+ }
47
+ ),
48
+ /* @__PURE__ */ jsx(SelectPrimitive.Portal, { container: container ?? void 0, children: /* @__PURE__ */ jsxs(
49
+ SelectPrimitive.Content,
50
+ {
51
+ collisionBoundary: container ?? void 0,
52
+ collisionPadding: 8,
53
+ position: "popper",
54
+ sideOffset: 6,
55
+ "data-m22-animated": true,
56
+ className: cn(
57
+ "z-(--z-dropdown) max-h-72 min-w-(--radix-select-trigger-width) overflow-hidden rounded-(--radius) border border-(--rule-2) bg-(--paper)",
58
+ contentClassName,
59
+ "data-[state=open]:animate-[m22-pop-in_var(--duration-fast)_var(--ease-out-expo)] data-[state=closed]:animate-[m22-pop-out_var(--duration-fast)_var(--ease)] origin-(--radix-popper-transform-origin)"
60
+ ),
61
+ children: [
62
+ /* @__PURE__ */ jsx(SelectPrimitive.ScrollUpButton, { className: "flex h-6 items-center justify-center text-(--ink-3-aa)", children: /* @__PURE__ */ jsx(ChevronUp, { size: 14, strokeWidth: 1.5, "aria-hidden": true }) }),
63
+ /* @__PURE__ */ jsx(SelectPrimitive.Viewport, { className: "p-1.5", children }),
64
+ /* @__PURE__ */ jsx(SelectPrimitive.ScrollDownButton, { className: "flex h-6 items-center justify-center text-(--ink-3-aa)", children: /* @__PURE__ */ jsx(ChevronDown, { size: 14, strokeWidth: 1.5, "aria-hidden": true }) })
65
+ ]
66
+ }
67
+ ) })
68
+ ] });
69
+ }
70
+ function SelectItem({ className, children, ...props }) {
71
+ return /* @__PURE__ */ jsxs(
72
+ SelectPrimitive.Item,
73
+ {
74
+ className: cn(
75
+ "flex cursor-pointer select-none items-center gap-2.5 rounded-(--radius-sm) px-2.5 py-2 text-sm text-(--ink-2) outline-none transition-colors duration-(--duration-fast) data-[highlighted]:bg-(--stone) data-[highlighted]:text-(--ink) data-[disabled]:pointer-events-none data-[disabled]:opacity-(--disabled-opacity)",
76
+ className
77
+ ),
78
+ ...props,
79
+ children: [
80
+ /* @__PURE__ */ jsx("span", { className: "grid size-3.5 shrink-0 place-items-center", children: /* @__PURE__ */ jsx(SelectPrimitive.ItemIndicator, { asChild: true, children: /* @__PURE__ */ jsx(Check, { size: 14, strokeWidth: 2, "aria-hidden": true }) }) }),
81
+ /* @__PURE__ */ jsx(SelectPrimitive.ItemText, { children })
82
+ ]
83
+ }
84
+ );
85
+ }
86
+ function SelectLabel({
87
+ className,
88
+ ...props
89
+ }) {
90
+ return /* @__PURE__ */ jsx(
91
+ SelectPrimitive.Label,
92
+ {
93
+ className: cn("px-2.5 py-1.5 eyebrow text-(--ink-3-aa)", className),
94
+ ...props
95
+ }
96
+ );
97
+ }
98
+ function SelectSeparator({
99
+ className,
100
+ ...props
101
+ }) {
102
+ return /* @__PURE__ */ jsx(SelectPrimitive.Separator, { className: cn("my-1 h-px bg-(--rule)", className), ...props });
103
+ }
104
+ var Select_default = Select;
105
+ export {
106
+ Select,
107
+ SelectGroup,
108
+ SelectItem,
109
+ SelectLabel,
110
+ SelectRoot,
111
+ SelectSeparator,
112
+ Select_default as default
113
+ };
114
+ //# sourceMappingURL=Select.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/Select/Select.tsx"],"sourcesContent":["'use client'\n\nimport * as SelectPrimitive from '@radix-ui/react-select'\nimport { Check, ChevronDown, ChevronUp } from 'lucide-react'\nimport type { ComponentProps, ReactNode } from 'react'\nimport { cn } from '../../lib/cn'\nimport { useOverlayContainer } from '../../lib/overlay-container'\nimport { CONTROL_BASE, CONTROL_BORDER, isInvalid } from '../../lib/control'\n\n/** Radix Select root, group and label, as typed passthroughs. */\nexport const SelectRoot = SelectPrimitive.Root\nexport const SelectGroup = SelectPrimitive.Group\n\nexport interface SelectProps extends ComponentProps<typeof SelectPrimitive.Root> {\n /**\n * Classes for the option PANEL, not the trigger.\n *\n * `className` styles the trigger, which is the common case. This exists for\n * the uncommon one: a select inside a bounded surface, where the default\n * 18rem of list would cover the thing the reader is choosing for — a year\n * picker over its own calendar, say.\n */\n contentClassName?: string\n /** Names the control. Required — the trigger shows a value, and a value is not a name. */\n label: string\n placeholder?: string\n /** Paints the resting border with `--danger` and reflects `aria-invalid`. */\n invalid?: boolean\n disabled?: boolean\n className?: string\n /** `SelectItem`s, optionally wrapped in `SelectGroup` with a `SelectLabel`. */\n children: ReactNode\n}\n\n/**\n * A choice from a list, styled the whole way down.\n *\n * The option list is ours — drawn from the same tokens as everything else, so\n * it does not change typeface, spacing and selection colour the moment it\n * opens. That is the whole reason this replaced the native control as the\n * default: a design system whose most common form control stops being part of\n * the system on click is not a design system, it is a stylesheet for closed\n * states.\n *\n * The keyboard contract is Radix's, which means it is the platform's: typeahead\n * works, the arrows move, Home and End reach the ends, and Escape closes\n * without choosing. That was the one genuine argument for staying native, and\n * it is answered.\n *\n * Past roughly a dozen options, reach for `Combobox` — a list nobody can filter\n * is worse than one they can type into. Where the platform picker is genuinely\n * better — a phone, or a form that must survive without JavaScript — reach for\n * `NativeSelect`.\n *\n * @example\n * <Field label=\"Region\">\n * <Select label=\"Region\" defaultValue=\"au\">\n * <SelectItem value=\"au\">Australia</SelectItem>\n * <SelectItem value=\"nz\">New Zealand</SelectItem>\n * </Select>\n * </Field>\n */\nexport function Select({\n label,\n placeholder = 'Select…',\n invalid,\n disabled,\n className,\n contentClassName,\n children,\n ...props\n}: SelectProps) {\n const bad = isInvalid(invalid)\n const container = useOverlayContainer()\n\n return (\n <SelectPrimitive.Root disabled={disabled} {...props}>\n <SelectPrimitive.Trigger\n aria-label={label}\n aria-invalid={bad || undefined}\n className={cn(\n CONTROL_BASE,\n 'group flex cursor-pointer items-center justify-between gap-2 text-start data-[placeholder]:text-(--ink-3-aa)',\n bad ? CONTROL_BORDER.invalid : CONTROL_BORDER.resting,\n className,\n )}\n >\n <SelectPrimitive.Value placeholder={placeholder} />\n <SelectPrimitive.Icon asChild>\n <ChevronDown\n size={16}\n strokeWidth={1.5}\n aria-hidden\n className=\"shrink-0 text-(--ink-3-aa) transition-transform duration-(--duration-fast) group-data-[state=open]:rotate-180\"\n />\n </SelectPrimitive.Icon>\n </SelectPrimitive.Trigger>\n\n <SelectPrimitive.Portal container={container ?? undefined}>\n <SelectPrimitive.Content\n collisionBoundary={container ?? undefined}\n collisionPadding={8}\n position=\"popper\"\n sideOffset={6}\n data-m22-animated\n className={cn(\n 'z-(--z-dropdown) max-h-72 min-w-(--radix-select-trigger-width) overflow-hidden rounded-(--radius) border border-(--rule-2) bg-(--paper)',\n contentClassName,\n 'data-[state=open]:animate-[m22-pop-in_var(--duration-fast)_var(--ease-out-expo)] data-[state=closed]:animate-[m22-pop-out_var(--duration-fast)_var(--ease)] origin-(--radix-popper-transform-origin)',\n )}\n >\n <SelectPrimitive.ScrollUpButton className=\"flex h-6 items-center justify-center text-(--ink-3-aa)\">\n <ChevronUp size={14} strokeWidth={1.5} aria-hidden />\n </SelectPrimitive.ScrollUpButton>\n <SelectPrimitive.Viewport className=\"p-1.5\">{children}</SelectPrimitive.Viewport>\n <SelectPrimitive.ScrollDownButton className=\"flex h-6 items-center justify-center text-(--ink-3-aa)\">\n <ChevronDown size={14} strokeWidth={1.5} aria-hidden />\n </SelectPrimitive.ScrollDownButton>\n </SelectPrimitive.Content>\n </SelectPrimitive.Portal>\n </SelectPrimitive.Root>\n )\n}\n\nexport type SelectItemProps = ComponentProps<typeof SelectPrimitive.Item>\n\n/** One option. The tick marks the chosen one; the fill marks the highlighted one. */\nexport function SelectItem({ className, children, ...props }: SelectItemProps) {\n return (\n <SelectPrimitive.Item\n className={cn(\n 'flex cursor-pointer select-none items-center gap-2.5 rounded-(--radius-sm) px-2.5 py-2 text-sm text-(--ink-2) outline-none transition-colors duration-(--duration-fast) data-[highlighted]:bg-(--stone) data-[highlighted]:text-(--ink) data-[disabled]:pointer-events-none data-[disabled]:opacity-(--disabled-opacity)',\n className,\n )}\n {...props}\n >\n {/* The box is always there and only the tick inside it appears, so\n choosing an option does not shunt every label sideways. */}\n <span className=\"grid size-3.5 shrink-0 place-items-center\">\n <SelectPrimitive.ItemIndicator asChild>\n <Check size={14} strokeWidth={2} aria-hidden />\n </SelectPrimitive.ItemIndicator>\n </span>\n <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>\n </SelectPrimitive.Item>\n )\n}\n\n/**\n * Mono eyebrow heading for a group of options.\n *\n * Must sit inside a `SelectGroup` — Radix throws otherwise, because a heading\n * with no group is a heading for nothing, and assistive tech would announce it\n * as an option.\n */\nexport function SelectLabel({\n className,\n ...props\n}: ComponentProps<typeof SelectPrimitive.Label>) {\n return (\n <SelectPrimitive.Label\n className={cn('px-2.5 py-1.5 eyebrow text-(--ink-3-aa)', className)}\n {...props}\n />\n )\n}\n\n/** Hairline divider between groups. */\nexport function SelectSeparator({\n className,\n ...props\n}: ComponentProps<typeof SelectPrimitive.Separator>) {\n return (\n <SelectPrimitive.Separator className={cn('my-1 h-px bg-(--rule)', className)} {...props} />\n )\n}\n\nexport default Select\n"],"mappings":";AA6EM,SAUE,KAVF;AA3EN,YAAY,qBAAqB;AACjC,SAAS,OAAO,aAAa,iBAAiB;AAE9C,SAAS,UAAU;AACnB,SAAS,2BAA2B;AACpC,SAAS,cAAc,gBAAgB,iBAAiB;AAGjD,MAAM,aAAa,gBAAgB;AACnC,MAAM,cAAc,gBAAgB;AAmDpC,SAAS,OAAO;AAAA,EACrB;AAAA,EACA,cAAc;AAAA,EACd;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAAgB;AACd,QAAM,MAAM,UAAU,OAAO;AAC7B,QAAM,YAAY,oBAAoB;AAEtC,SACE,qBAAC,gBAAgB,MAAhB,EAAqB,UAAqB,GAAG,OAC5C;AAAA;AAAA,MAAC,gBAAgB;AAAA,MAAhB;AAAA,QACC,cAAY;AAAA,QACZ,gBAAc,OAAO;AAAA,QACrB,WAAW;AAAA,UACT;AAAA,UACA;AAAA,UACA,MAAM,eAAe,UAAU,eAAe;AAAA,UAC9C;AAAA,QACF;AAAA,QAEA;AAAA,8BAAC,gBAAgB,OAAhB,EAAsB,aAA0B;AAAA,UACjD,oBAAC,gBAAgB,MAAhB,EAAqB,SAAO,MAC3B;AAAA,YAAC;AAAA;AAAA,cACC,MAAM;AAAA,cACN,aAAa;AAAA,cACb,eAAW;AAAA,cACX,WAAU;AAAA;AAAA,UACZ,GACF;AAAA;AAAA;AAAA,IACF;AAAA,IAEA,oBAAC,gBAAgB,QAAhB,EAAuB,WAAW,aAAa,QAC9C;AAAA,MAAC,gBAAgB;AAAA,MAAhB;AAAA,QACD,mBAAmB,aAAa;AAAA,QAChC,kBAAkB;AAAA,QAChB,UAAS;AAAA,QACT,YAAY;AAAA,QACZ,qBAAiB;AAAA,QACjB,WAAW;AAAA,UACT;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAAA,QAEA;AAAA,8BAAC,gBAAgB,gBAAhB,EAA+B,WAAU,0DACxC,8BAAC,aAAU,MAAM,IAAI,aAAa,KAAK,eAAW,MAAC,GACrD;AAAA,UACA,oBAAC,gBAAgB,UAAhB,EAAyB,WAAU,SAAS,UAAS;AAAA,UACtD,oBAAC,gBAAgB,kBAAhB,EAAiC,WAAU,0DAC1C,8BAAC,eAAY,MAAM,IAAI,aAAa,KAAK,eAAW,MAAC,GACvD;AAAA;AAAA;AAAA,IACF,GACF;AAAA,KACF;AAEJ;AAKO,SAAS,WAAW,EAAE,WAAW,UAAU,GAAG,MAAM,GAAoB;AAC7E,SACE;AAAA,IAAC,gBAAgB;AAAA,IAAhB;AAAA,MACC,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA,MAIJ;AAAA,4BAAC,UAAK,WAAU,6CACd,8BAAC,gBAAgB,eAAhB,EAA8B,SAAO,MACpC,8BAAC,SAAM,MAAM,IAAI,aAAa,GAAG,eAAW,MAAC,GAC/C,GACF;AAAA,QACA,oBAAC,gBAAgB,UAAhB,EAA0B,UAAS;AAAA;AAAA;AAAA,EACtC;AAEJ;AASO,SAAS,YAAY;AAAA,EAC1B;AAAA,EACA,GAAG;AACL,GAAiD;AAC/C,SACE;AAAA,IAAC,gBAAgB;AAAA,IAAhB;AAAA,MACC,WAAW,GAAG,2CAA2C,SAAS;AAAA,MACjE,GAAG;AAAA;AAAA,EACN;AAEJ;AAGO,SAAS,gBAAgB;AAAA,EAC9B;AAAA,EACA,GAAG;AACL,GAAqD;AACnD,SACE,oBAAC,gBAAgB,WAAhB,EAA0B,WAAW,GAAG,yBAAyB,SAAS,GAAI,GAAG,OAAO;AAE7F;AAEA,IAAO,iBAAQ;","names":[]}
@@ -0,0 +1,30 @@
1
+ import * as react from 'react';
2
+ import { HTMLAttributes } from 'react';
3
+
4
+ interface SeparatorProps extends HTMLAttributes<HTMLDivElement> {
5
+ orientation?: 'horizontal' | 'vertical';
6
+ /** `hairline` divides rows inside one block; `edge` divides one block from the next. */
7
+ weight?: 'hairline' | 'edge' | 'hard';
8
+ /**
9
+ * A rule that only groups things visually is decoration and must not be
10
+ * announced. Set `false` when the rule genuinely separates two sections a
11
+ * screen reader should hear as distinct.
12
+ */
13
+ decorative?: boolean;
14
+ }
15
+ /**
16
+ * A rule.
17
+ *
18
+ * In a monochrome system the rule does the work colour would otherwise do, so
19
+ * it has three weights rather than one: hairline between rows, edge between
20
+ * blocks, hard under a masthead. Picking by name keeps a page from drifting
21
+ * into five hand-tuned greys.
22
+ *
23
+ * @example
24
+ * <Separator weight="hard" />
25
+ * @example
26
+ * <Separator orientation="vertical" className="h-4" />
27
+ */
28
+ declare function Separator({ orientation, weight, decorative, className, ...rest }: SeparatorProps): react.JSX.Element;
29
+
30
+ export { Separator, type SeparatorProps, Separator as default };
@@ -0,0 +1,35 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { cn } from "../../lib/cn.js";
3
+ const WEIGHT = {
4
+ hairline: "bg-(--rule)",
5
+ edge: "bg-(--rule-2)",
6
+ hard: "bg-(--rule-hard)"
7
+ };
8
+ function Separator({
9
+ orientation = "horizontal",
10
+ weight = "hairline",
11
+ decorative = true,
12
+ className,
13
+ ...rest
14
+ }) {
15
+ return /* @__PURE__ */ jsx(
16
+ "div",
17
+ {
18
+ role: decorative ? "none" : "separator",
19
+ "aria-orientation": decorative ? void 0 : orientation,
20
+ className: cn(
21
+ "shrink-0",
22
+ orientation === "horizontal" ? "h-px w-full" : "h-full w-px",
23
+ WEIGHT[weight],
24
+ className
25
+ ),
26
+ ...rest
27
+ }
28
+ );
29
+ }
30
+ var Separator_default = Separator;
31
+ export {
32
+ Separator,
33
+ Separator_default as default
34
+ };
35
+ //# sourceMappingURL=Separator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/Separator/Separator.tsx"],"sourcesContent":["import type { HTMLAttributes } from 'react'\nimport { cn } from '../../lib/cn'\n\nexport interface SeparatorProps extends HTMLAttributes<HTMLDivElement> {\n orientation?: 'horizontal' | 'vertical'\n /** `hairline` divides rows inside one block; `edge` divides one block from the next. */\n weight?: 'hairline' | 'edge' | 'hard'\n /**\n * A rule that only groups things visually is decoration and must not be\n * announced. Set `false` when the rule genuinely separates two sections a\n * screen reader should hear as distinct.\n */\n decorative?: boolean\n}\n\nconst WEIGHT = {\n hairline: 'bg-(--rule)',\n edge: 'bg-(--rule-2)',\n hard: 'bg-(--rule-hard)',\n} as const\n\n/**\n * A rule.\n *\n * In a monochrome system the rule does the work colour would otherwise do, so\n * it has three weights rather than one: hairline between rows, edge between\n * blocks, hard under a masthead. Picking by name keeps a page from drifting\n * into five hand-tuned greys.\n *\n * @example\n * <Separator weight=\"hard\" />\n * @example\n * <Separator orientation=\"vertical\" className=\"h-4\" />\n */\nexport function Separator({\n orientation = 'horizontal',\n weight = 'hairline',\n decorative = true,\n className,\n ...rest\n}: SeparatorProps) {\n return (\n <div\n role={decorative ? 'none' : 'separator'}\n aria-orientation={decorative ? undefined : orientation}\n className={cn(\n 'shrink-0',\n orientation === 'horizontal' ? 'h-px w-full' : 'h-full w-px',\n WEIGHT[weight],\n className,\n )}\n {...rest}\n />\n )\n}\n\nexport default Separator\n"],"mappings":"AA0CI;AAzCJ,SAAS,UAAU;AAcnB,MAAM,SAAS;AAAA,EACb,UAAU;AAAA,EACV,MAAM;AAAA,EACN,MAAM;AACR;AAeO,SAAS,UAAU;AAAA,EACxB,cAAc;AAAA,EACd,SAAS;AAAA,EACT,aAAa;AAAA,EACb;AAAA,EACA,GAAG;AACL,GAAmB;AACjB,SACE;AAAA,IAAC;AAAA;AAAA,MACC,MAAM,aAAa,SAAS;AAAA,MAC5B,oBAAkB,aAAa,SAAY;AAAA,MAC3C,WAAW;AAAA,QACT;AAAA,QACA,gBAAgB,eAAe,gBAAgB;AAAA,QAC/C,OAAO,MAAM;AAAA,QACb;AAAA,MACF;AAAA,MACC,GAAG;AAAA;AAAA,EACN;AAEJ;AAEA,IAAO,oBAAQ;","names":[]}
@@ -0,0 +1,38 @@
1
+ import * as react from 'react';
2
+ import { ComponentProps, ReactNode } from 'react';
3
+ import * as DialogPrimitive from '@radix-ui/react-dialog';
4
+
5
+ /** Radix Dialog root, trigger and close — a sheet IS a dialog, docked. */
6
+ declare const Sheet: react.FC<DialogPrimitive.DialogProps>;
7
+ declare const SheetTrigger: react.ForwardRefExoticComponent<DialogPrimitive.DialogTriggerProps & react.RefAttributes<HTMLButtonElement>>;
8
+ declare const SheetClose: react.ForwardRefExoticComponent<DialogPrimitive.DialogCloseProps & react.RefAttributes<HTMLButtonElement>>;
9
+ type SheetSide = 'start' | 'end' | 'top' | 'bottom';
10
+ interface SheetContentProps extends Omit<ComponentProps<typeof DialogPrimitive.Content>, 'title'> {
11
+ /** Which edge it is docked to. `end` by default. */
12
+ side?: SheetSide;
13
+ title: ReactNode;
14
+ description?: ReactNode;
15
+ /** Hide the title visually while keeping it for assistive tech. */
16
+ hideTitle?: boolean;
17
+ }
18
+ /**
19
+ * A panel docked to an edge of the viewport.
20
+ *
21
+ * It is a modal dialog — Radix's, so the focus trap, the escape key, the scroll
22
+ * lock and the `aria-modal` wiring are the same ones `Dialog` gets. The only
23
+ * differences are where it sits and which way it arrives, which is why this
24
+ * shares that implementation rather than reproducing it: a second focus trap is
25
+ * a second focus trap to get wrong.
26
+ *
27
+ * The title is required, visible or not. A modal with no accessible name drops
28
+ * a screen reader into an unnamed region with no way back out.
29
+ *
30
+ * @example
31
+ * <Sheet>
32
+ * <SheetTrigger asChild><Button variant="secondary">Filters</Button></SheetTrigger>
33
+ * <SheetContent title="Filters" description="Narrow the list.">…</SheetContent>
34
+ * </Sheet>
35
+ */
36
+ declare function SheetContent({ side, title, description, hideTitle, className, children, ...rest }: SheetContentProps): react.JSX.Element;
37
+
38
+ export { Sheet, SheetClose, SheetContent, type SheetContentProps, type SheetSide, SheetTrigger, Sheet as default };
@@ -0,0 +1,68 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import * as DialogPrimitive from "@radix-ui/react-dialog";
4
+ import { X } from "lucide-react";
5
+ import { cn } from "../../lib/cn.js";
6
+ const Sheet = DialogPrimitive.Root;
7
+ const SheetTrigger = DialogPrimitive.Trigger;
8
+ const SheetClose = DialogPrimitive.Close;
9
+ const SIDE = {
10
+ start: "inset-y-0 start-0 h-full w-[min(24rem,92vw)] border-e data-[state=closed]:-translate-x-full rtl:data-[state=closed]:translate-x-full",
11
+ end: "inset-y-0 end-0 h-full w-[min(24rem,92vw)] border-s data-[state=closed]:translate-x-full rtl:data-[state=closed]:-translate-x-full",
12
+ top: "inset-x-0 top-0 w-full max-h-[85vh] border-b data-[state=closed]:-translate-y-full",
13
+ bottom: "inset-x-0 bottom-0 w-full max-h-[85vh] border-t data-[state=closed]:translate-y-full"
14
+ };
15
+ function SheetContent({
16
+ side = "end",
17
+ title,
18
+ description,
19
+ hideTitle = false,
20
+ className,
21
+ children,
22
+ ...rest
23
+ }) {
24
+ return /* @__PURE__ */ jsxs(DialogPrimitive.Portal, { children: [
25
+ /* @__PURE__ */ jsx(
26
+ DialogPrimitive.Overlay,
27
+ {
28
+ "data-m22-animated": true,
29
+ className: "fixed inset-0 z-(--z-overlay) bg-(--scrim) data-[state=open]:animate-[m22-fade-in_var(--duration-fast)_var(--ease)]"
30
+ }
31
+ ),
32
+ /* @__PURE__ */ jsxs(
33
+ DialogPrimitive.Content,
34
+ {
35
+ className: cn(
36
+ "fixed z-(--z-modal) flex flex-col overflow-y-auto border-(--rule-2) bg-(--paper) p-6 transition-transform duration-(--duration-slow) ease-(--ease-out-expo) scroll-slim",
37
+ SIDE[side],
38
+ className
39
+ ),
40
+ ...rest,
41
+ children: [
42
+ /* @__PURE__ */ jsxs("div", { className: cn("mb-4 flex flex-col gap-1 pe-8", hideTitle && "sr-only"), children: [
43
+ /* @__PURE__ */ jsx(DialogPrimitive.Title, { className: "m-0 font-heading text-[length:var(--fs-item)] font-normal text-(--ink)", children: title }),
44
+ description !== void 0 && /* @__PURE__ */ jsx(DialogPrimitive.Description, { className: "m-0 text-sm leading-relaxed text-(--ink-3-aa)", children: description })
45
+ ] }),
46
+ /* @__PURE__ */ jsx(
47
+ DialogPrimitive.Close,
48
+ {
49
+ "aria-label": "Close",
50
+ className: "absolute end-3 top-3 grid size-9 place-items-center rounded-(--radius-pill) text-(--ink-3-aa) transition-colors duration-(--duration-fast) hover:bg-(--stone) hover:text-(--ink)",
51
+ children: /* @__PURE__ */ jsx(X, { size: 16, strokeWidth: 1.5, "aria-hidden": true })
52
+ }
53
+ ),
54
+ children
55
+ ]
56
+ }
57
+ )
58
+ ] });
59
+ }
60
+ var Sheet_default = Sheet;
61
+ export {
62
+ Sheet,
63
+ SheetClose,
64
+ SheetContent,
65
+ SheetTrigger,
66
+ Sheet_default as default
67
+ };
68
+ //# sourceMappingURL=Sheet.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/Sheet/Sheet.tsx"],"sourcesContent":["'use client'\n\nimport * as DialogPrimitive from '@radix-ui/react-dialog'\nimport { X } from 'lucide-react'\nimport type { ComponentProps, ReactNode } from 'react'\nimport { cn } from '../../lib/cn'\n\n/** Radix Dialog root, trigger and close — a sheet IS a dialog, docked. */\nexport const Sheet = DialogPrimitive.Root\nexport const SheetTrigger = DialogPrimitive.Trigger\nexport const SheetClose = DialogPrimitive.Close\n\nexport type SheetSide = 'start' | 'end' | 'top' | 'bottom'\n\n/**\n * Full literal class strings per edge, because Tailwind only generates what it\n * can see verbatim. The inline sides are named in READING order, so a sheet\n * docked to `end` comes from the right in English and the left in Arabic.\n */\nconst SIDE: Record<SheetSide, string> = {\n start:\n 'inset-y-0 start-0 h-full w-[min(24rem,92vw)] border-e data-[state=closed]:-translate-x-full rtl:data-[state=closed]:translate-x-full',\n end: 'inset-y-0 end-0 h-full w-[min(24rem,92vw)] border-s data-[state=closed]:translate-x-full rtl:data-[state=closed]:-translate-x-full',\n top: 'inset-x-0 top-0 w-full max-h-[85vh] border-b data-[state=closed]:-translate-y-full',\n bottom:\n 'inset-x-0 bottom-0 w-full max-h-[85vh] border-t data-[state=closed]:translate-y-full',\n}\n\nexport interface SheetContentProps\n extends Omit<ComponentProps<typeof DialogPrimitive.Content>, 'title'> {\n /** Which edge it is docked to. `end` by default. */\n side?: SheetSide\n title: ReactNode\n description?: ReactNode\n /** Hide the title visually while keeping it for assistive tech. */\n hideTitle?: boolean\n}\n\n/**\n * A panel docked to an edge of the viewport.\n *\n * It is a modal dialog — Radix's, so the focus trap, the escape key, the scroll\n * lock and the `aria-modal` wiring are the same ones `Dialog` gets. The only\n * differences are where it sits and which way it arrives, which is why this\n * shares that implementation rather than reproducing it: a second focus trap is\n * a second focus trap to get wrong.\n *\n * The title is required, visible or not. A modal with no accessible name drops\n * a screen reader into an unnamed region with no way back out.\n *\n * @example\n * <Sheet>\n * <SheetTrigger asChild><Button variant=\"secondary\">Filters</Button></SheetTrigger>\n * <SheetContent title=\"Filters\" description=\"Narrow the list.\">…</SheetContent>\n * </Sheet>\n */\nexport function SheetContent({\n side = 'end',\n title,\n description,\n hideTitle = false,\n className,\n children,\n ...rest\n}: SheetContentProps) {\n return (\n <DialogPrimitive.Portal>\n <DialogPrimitive.Overlay\n data-m22-animated\n className=\"fixed inset-0 z-(--z-overlay) bg-(--scrim) data-[state=open]:animate-[m22-fade-in_var(--duration-fast)_var(--ease)]\"\n />\n <DialogPrimitive.Content\n className={cn(\n 'fixed z-(--z-modal) flex flex-col overflow-y-auto border-(--rule-2) bg-(--paper) p-6 transition-transform duration-(--duration-slow) ease-(--ease-out-expo) scroll-slim',\n SIDE[side],\n className,\n )}\n {...rest}\n >\n <div className={cn('mb-4 flex flex-col gap-1 pe-8', hideTitle && 'sr-only')}>\n <DialogPrimitive.Title className=\"m-0 font-heading text-[length:var(--fs-item)] font-normal text-(--ink)\">\n {title}\n </DialogPrimitive.Title>\n {description !== undefined && (\n <DialogPrimitive.Description className=\"m-0 text-sm leading-relaxed text-(--ink-3-aa)\">\n {description}\n </DialogPrimitive.Description>\n )}\n </div>\n\n <DialogPrimitive.Close\n aria-label=\"Close\"\n className=\"absolute end-3 top-3 grid size-9 place-items-center rounded-(--radius-pill) text-(--ink-3-aa) transition-colors duration-(--duration-fast) hover:bg-(--stone) hover:text-(--ink)\"\n >\n <X size={16} strokeWidth={1.5} aria-hidden />\n </DialogPrimitive.Close>\n\n {children}\n </DialogPrimitive.Content>\n </DialogPrimitive.Portal>\n )\n}\n\nexport default Sheet\n"],"mappings":";AAmEM,cAYE,YAZF;AAjEN,YAAY,qBAAqB;AACjC,SAAS,SAAS;AAElB,SAAS,UAAU;AAGZ,MAAM,QAAQ,gBAAgB;AAC9B,MAAM,eAAe,gBAAgB;AACrC,MAAM,aAAa,gBAAgB;AAS1C,MAAM,OAAkC;AAAA,EACtC,OACE;AAAA,EACF,KAAK;AAAA,EACL,KAAK;AAAA,EACL,QACE;AACJ;AA8BO,SAAS,aAAa;AAAA,EAC3B,OAAO;AAAA,EACP;AAAA,EACA;AAAA,EACA,YAAY;AAAA,EACZ;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAAsB;AACpB,SACE,qBAAC,gBAAgB,QAAhB,EACC;AAAA;AAAA,MAAC,gBAAgB;AAAA,MAAhB;AAAA,QACC,qBAAiB;AAAA,QACjB,WAAU;AAAA;AAAA,IACZ;AAAA,IACA;AAAA,MAAC,gBAAgB;AAAA,MAAhB;AAAA,QACC,WAAW;AAAA,UACT;AAAA,UACA,KAAK,IAAI;AAAA,UACT;AAAA,QACF;AAAA,QACC,GAAG;AAAA,QAEJ;AAAA,+BAAC,SAAI,WAAW,GAAG,iCAAiC,aAAa,SAAS,GACxE;AAAA,gCAAC,gBAAgB,OAAhB,EAAsB,WAAU,0EAC9B,iBACH;AAAA,YACC,gBAAgB,UACf,oBAAC,gBAAgB,aAAhB,EAA4B,WAAU,iDACpC,uBACH;AAAA,aAEJ;AAAA,UAEA;AAAA,YAAC,gBAAgB;AAAA,YAAhB;AAAA,cACC,cAAW;AAAA,cACX,WAAU;AAAA,cAEV,8BAAC,KAAE,MAAM,IAAI,aAAa,KAAK,eAAW,MAAC;AAAA;AAAA,UAC7C;AAAA,UAEC;AAAA;AAAA;AAAA,IACH;AAAA,KACF;AAEJ;AAEA,IAAO,gBAAQ;","names":[]}