@seekalfred/primitives 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +950 -13
- package/dist/accordion/accordion.d.ts +107 -0
- package/dist/accordion/accordion.js +84 -0
- package/dist/accordion/accordion.js.map +1 -0
- package/dist/accordion/index.d.ts +2 -0
- package/dist/accordion/index.js +2 -0
- package/dist/alert/alert.d.ts +68 -0
- package/dist/alert/alert.js +67 -0
- package/dist/alert/alert.js.map +1 -0
- package/dist/alert/index.d.ts +2 -0
- package/dist/alert/index.js +2 -0
- package/dist/alert-dialog/alert-dialog.d.ts +53 -0
- package/dist/alert-dialog/alert-dialog.js +16 -0
- package/dist/alert-dialog/alert-dialog.js.map +1 -0
- package/dist/alert-dialog/index.d.ts +2 -0
- package/dist/alert-dialog/index.js +2 -0
- package/dist/avatar/avatar.d.ts +50 -0
- package/dist/avatar/avatar.js +91 -0
- package/dist/avatar/avatar.js.map +1 -0
- package/dist/avatar/index.d.ts +2 -0
- package/dist/avatar/index.js +2 -0
- package/dist/badge/badge.d.ts +47 -0
- package/dist/badge/badge.js +189 -0
- package/dist/badge/badge.js.map +1 -0
- package/dist/badge/index.d.ts +2 -0
- package/dist/badge/index.js +2 -0
- package/dist/breadcrumb/breadcrumb.d.ts +98 -0
- package/dist/breadcrumb/breadcrumb.js +93 -0
- package/dist/breadcrumb/breadcrumb.js.map +1 -0
- package/dist/breadcrumb/index.d.ts +2 -0
- package/dist/breadcrumb/index.js +2 -0
- package/dist/button/button.d.ts +1 -1
- package/dist/button/button.js +7 -0
- package/dist/button/button.js.map +1 -1
- package/dist/calendar/calendar.d.ts +154 -0
- package/dist/calendar/calendar.js +282 -0
- package/dist/calendar/calendar.js.map +1 -0
- package/dist/calendar/index.d.ts +2 -0
- package/dist/calendar/index.js +2 -0
- package/dist/card/card.d.ts +93 -0
- package/dist/card/card.js +105 -0
- package/dist/card/card.js.map +1 -0
- package/dist/card/index.d.ts +2 -0
- package/dist/card/index.js +2 -0
- package/dist/checkbox/checkbox.d.ts +63 -0
- package/dist/checkbox/checkbox.js +94 -0
- package/dist/checkbox/checkbox.js.map +1 -0
- package/dist/checkbox/index.d.ts +2 -0
- package/dist/checkbox/index.js +2 -0
- package/dist/dialog/dialog.d.ts +140 -0
- package/dist/dialog/dialog.js +127 -0
- package/dist/dialog/dialog.js.map +1 -0
- package/dist/dialog/index.d.ts +2 -0
- package/dist/dialog/index.js +2 -0
- package/dist/index.d.ts +30 -1
- package/dist/index.js +30 -1
- package/dist/input/index.d.ts +2 -0
- package/dist/input/index.js +2 -0
- package/dist/input/input.d.ts +64 -0
- package/dist/input/input.js +94 -0
- package/dist/input/input.js.map +1 -0
- package/dist/label/index.d.ts +2 -0
- package/dist/label/index.js +2 -0
- package/dist/label/label.d.ts +41 -0
- package/dist/label/label.js +44 -0
- package/dist/label/label.js.map +1 -0
- package/dist/lib/field-surface.js +40 -0
- package/dist/lib/field-surface.js.map +1 -0
- package/dist/lib/selection-control.js +57 -0
- package/dist/lib/selection-control.js.map +1 -0
- package/dist/menu/index.d.ts +2 -0
- package/dist/menu/index.js +2 -0
- package/dist/menu/menu.d.ts +98 -0
- package/dist/menu/menu.js +99 -0
- package/dist/menu/menu.js.map +1 -0
- package/dist/popover/index.d.ts +2 -0
- package/dist/popover/index.js +2 -0
- package/dist/popover/popover.d.ts +50 -0
- package/dist/popover/popover.js +46 -0
- package/dist/popover/popover.js.map +1 -0
- package/dist/progress/index.d.ts +2 -0
- package/dist/progress/index.js +2 -0
- package/dist/progress/progress.d.ts +82 -0
- package/dist/progress/progress.js +82 -0
- package/dist/progress/progress.js.map +1 -0
- package/dist/radio-group/index.d.ts +2 -0
- package/dist/radio-group/index.js +2 -0
- package/dist/radio-group/radio-group.d.ts +53 -0
- package/dist/radio-group/radio-group.js +65 -0
- package/dist/radio-group/radio-group.js.map +1 -0
- package/dist/scroll-area/index.d.ts +2 -0
- package/dist/scroll-area/index.js +2 -0
- package/dist/scroll-area/scroll-area.d.ts +74 -0
- package/dist/scroll-area/scroll-area.js +63 -0
- package/dist/scroll-area/scroll-area.js.map +1 -0
- package/dist/select/index.d.ts +2 -0
- package/dist/select/index.js +2 -0
- package/dist/select/select.d.ts +93 -0
- package/dist/select/select.js +132 -0
- package/dist/select/select.js.map +1 -0
- package/dist/separator/index.d.ts +2 -0
- package/dist/separator/index.js +2 -0
- package/dist/separator/separator.d.ts +42 -0
- package/dist/separator/separator.js +49 -0
- package/dist/separator/separator.js.map +1 -0
- package/dist/sheet/index.d.ts +2 -0
- package/dist/sheet/index.js +2 -0
- package/dist/sheet/sheet.d.ts +96 -0
- package/dist/sheet/sheet.js +98 -0
- package/dist/sheet/sheet.js.map +1 -0
- package/dist/skeleton/index.d.ts +2 -0
- package/dist/skeleton/index.js +2 -0
- package/dist/skeleton/skeleton.d.ts +33 -0
- package/dist/skeleton/skeleton.js +42 -0
- package/dist/skeleton/skeleton.js.map +1 -0
- package/dist/slider/index.d.ts +2 -0
- package/dist/slider/index.js +2 -0
- package/dist/slider/slider.d.ts +70 -0
- package/dist/slider/slider.js +92 -0
- package/dist/slider/slider.js.map +1 -0
- package/dist/spinner/index.d.ts +2 -0
- package/dist/spinner/index.js +2 -0
- package/dist/spinner/spinner.d.ts +75 -0
- package/dist/spinner/spinner.js +68 -0
- package/dist/spinner/spinner.js.map +1 -0
- package/dist/styles.css +1 -1
- package/dist/switch/index.d.ts +2 -0
- package/dist/switch/index.js +2 -0
- package/dist/switch/switch.d.ts +50 -0
- package/dist/switch/switch.js +66 -0
- package/dist/switch/switch.js.map +1 -0
- package/dist/table/index.d.ts +2 -0
- package/dist/table/index.js +2 -0
- package/dist/table/table.d.ts +288 -0
- package/dist/table/table.js +513 -0
- package/dist/table/table.js.map +1 -0
- package/dist/tabs/index.d.ts +2 -0
- package/dist/tabs/index.js +2 -0
- package/dist/tabs/tabs.d.ts +79 -0
- package/dist/tabs/tabs.js +103 -0
- package/dist/tabs/tabs.js.map +1 -0
- package/dist/textarea/index.d.ts +2 -0
- package/dist/textarea/index.js +2 -0
- package/dist/textarea/textarea.d.ts +27 -0
- package/dist/textarea/textarea.js +37 -0
- package/dist/textarea/textarea.js.map +1 -0
- package/dist/toast/index.d.ts +2 -0
- package/dist/toast/index.js +2 -0
- package/dist/toast/toast.d.ts +93 -0
- package/dist/toast/toast.js +145 -0
- package/dist/toast/toast.js.map +1 -0
- package/dist/tooltip/index.d.ts +2 -0
- package/dist/tooltip/index.js +2 -0
- package/dist/tooltip/tooltip.d.ts +70 -0
- package/dist/tooltip/tooltip.js +65 -0
- package/dist/tooltip/tooltip.js.map +1 -0
- package/package.json +125 -7
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { ComponentPropsWithoutRef, ReactNode } from "react";
|
|
2
|
+
//#region src/scroll-area/scroll-area.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* ScrollArea. React Aria has no equivalent, so this is a plain scrolling
|
|
5
|
+
* div -- and that is the honest answer rather than a shortcut.
|
|
6
|
+
*
|
|
7
|
+
* Census: fourteen paths match, thirteen are counted, ELEVEN render
|
|
8
|
+
* anything, and across them 17 uses. Every single one passes `className`
|
|
9
|
+
* and nothing else, and 13 of the 17 are a height or flex constraint plus
|
|
10
|
+
* padding. `ScrollBar` is exported by the legacy and imported by nobody.
|
|
11
|
+
* `orientation="horizontal"` has zero call sites.
|
|
12
|
+
*
|
|
13
|
+
* So there is no styled-scrollbar requirement to port. The legacy pays for
|
|
14
|
+
* one anyway: Radix injects a `<style>` tag that hides the native
|
|
15
|
+
* scrollbar, then re-draws it in JavaScript with a ResizeObserver, a
|
|
16
|
+
* portal-free overlay and a `display: table` content wrapper. That is a
|
|
17
|
+
* lot of machinery to reproduce a scrollbar, and it is why the legacy
|
|
18
|
+
* `ScrollArea` cannot be scrolled by keyboard at all.
|
|
19
|
+
*
|
|
20
|
+
* Tailwind 4.3.3 ships `scrollbar-width` and `scrollbar-color` utilities
|
|
21
|
+
* (verified in the installed dist, not assumed), which gets the same look
|
|
22
|
+
* in three classes and no JavaScript.
|
|
23
|
+
*
|
|
24
|
+
* WHAT CANNOT BE STYLED, stated rather than glossed: `scrollbar-color`
|
|
25
|
+
* takes a thumb and a track colour and nothing else -- no width in pixels,
|
|
26
|
+
* no radius, no hover state, no margins. `::-webkit-scrollbar` can do
|
|
27
|
+
* those but is non-standard and Firefox has never supported it. This
|
|
28
|
+
* component uses the standard properties only.
|
|
29
|
+
*/
|
|
30
|
+
export declare const scrollAreaVariants: (props?: import("class-variance-authority/types").ClassProp | undefined) => string;
|
|
31
|
+
/**
|
|
32
|
+
* A scrolling region.
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* <ScrollArea label="Chat messages" className="h-64">...</ScrollArea>
|
|
36
|
+
*/
|
|
37
|
+
export interface ScrollAreaProps extends ComponentPropsWithoutRef<"div"> {
|
|
38
|
+
children?: ReactNode;
|
|
39
|
+
/**
|
|
40
|
+
* Names the scrolling region.
|
|
41
|
+
*
|
|
42
|
+
* **Required**, and it took a failing test to get here. A focusable
|
|
43
|
+
* element with no role fails axe `focus-order-semantics`, so the
|
|
44
|
+
* keyboard fix below cannot ship without a role -- and a role with no
|
|
45
|
+
* accessible name is a violation of its own. Naming it is the only
|
|
46
|
+
* combination that is actually correct.
|
|
47
|
+
*
|
|
48
|
+
* `role="region"` specifically. `group` was the first attempt -- it is
|
|
49
|
+
* lighter, since region is a landmark and seventeen landmarks in one app
|
|
50
|
+
* is noise -- but axe does not accept `group` as an appropriate role for
|
|
51
|
+
* a focusable element, and it is right: a named region is the role the
|
|
52
|
+
* ARIA practices name for a scrollable content area, and being able to
|
|
53
|
+
* jump to it is the point.
|
|
54
|
+
*/
|
|
55
|
+
label: string;
|
|
56
|
+
/**
|
|
57
|
+
* Change this when the contents change, and the region scrolls back to the
|
|
58
|
+
* top.
|
|
59
|
+
*
|
|
60
|
+
* It lives here rather than on the caller because the caller cannot reach
|
|
61
|
+
* this node: no component in this package exposes a `ref`, and it must not
|
|
62
|
+
* -- React 18 is a supported peer, where a `ref` passed as a plain prop to a
|
|
63
|
+
* function component is silently dropped. The scroller owns its own
|
|
64
|
+
* scrollTop, so the reset belongs with it.
|
|
65
|
+
*
|
|
66
|
+
* Sheet forwards its own `scrollResetKey` straight through: going a level
|
|
67
|
+
* deeper in a panel and landing halfway down the new view is disorienting,
|
|
68
|
+
* and it is exactly the thing every call site forgets.
|
|
69
|
+
*/
|
|
70
|
+
scrollResetKey?: string | number;
|
|
71
|
+
}
|
|
72
|
+
export declare function ScrollArea({ className, children, label, scrollResetKey, ...props }: ScrollAreaProps): import("react").JSX.Element;
|
|
73
|
+
//#endregion
|
|
74
|
+
//# sourceMappingURL=scroll-area.d.ts.map
|
|
@@ -0,0 +1,63 @@
|
|
|
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
|
+
import { useEffect, useRef } from "react";
|
|
6
|
+
//#region src/scroll-area/scroll-area.tsx
|
|
7
|
+
/**
|
|
8
|
+
* ScrollArea. React Aria has no equivalent, so this is a plain scrolling
|
|
9
|
+
* div -- and that is the honest answer rather than a shortcut.
|
|
10
|
+
*
|
|
11
|
+
* Census: fourteen paths match, thirteen are counted, ELEVEN render
|
|
12
|
+
* anything, and across them 17 uses. Every single one passes `className`
|
|
13
|
+
* and nothing else, and 13 of the 17 are a height or flex constraint plus
|
|
14
|
+
* padding. `ScrollBar` is exported by the legacy and imported by nobody.
|
|
15
|
+
* `orientation="horizontal"` has zero call sites.
|
|
16
|
+
*
|
|
17
|
+
* So there is no styled-scrollbar requirement to port. The legacy pays for
|
|
18
|
+
* one anyway: Radix injects a `<style>` tag that hides the native
|
|
19
|
+
* scrollbar, then re-draws it in JavaScript with a ResizeObserver, a
|
|
20
|
+
* portal-free overlay and a `display: table` content wrapper. That is a
|
|
21
|
+
* lot of machinery to reproduce a scrollbar, and it is why the legacy
|
|
22
|
+
* `ScrollArea` cannot be scrolled by keyboard at all.
|
|
23
|
+
*
|
|
24
|
+
* Tailwind 4.3.3 ships `scrollbar-width` and `scrollbar-color` utilities
|
|
25
|
+
* (verified in the installed dist, not assumed), which gets the same look
|
|
26
|
+
* in three classes and no JavaScript.
|
|
27
|
+
*
|
|
28
|
+
* WHAT CANNOT BE STYLED, stated rather than glossed: `scrollbar-color`
|
|
29
|
+
* takes a thumb and a track colour and nothing else -- no width in pixels,
|
|
30
|
+
* no radius, no hover state, no margins. `::-webkit-scrollbar` can do
|
|
31
|
+
* those but is non-standard and Firefox has never supported it. This
|
|
32
|
+
* component uses the standard properties only.
|
|
33
|
+
*/
|
|
34
|
+
const scrollAreaVariants = cva([
|
|
35
|
+
"overflow-y-auto overscroll-contain",
|
|
36
|
+
"box-border",
|
|
37
|
+
"scrollbar-thin scrollbar-thumb-border scrollbar-track-transparent",
|
|
38
|
+
"outline-none",
|
|
39
|
+
"focus-visible:outline-solid",
|
|
40
|
+
"focus-visible:outline-(length:--focus-ring-width)",
|
|
41
|
+
"focus-visible:outline-offset-(--focus-ring-offset)",
|
|
42
|
+
"focus-visible:outline-(--color-ring)"
|
|
43
|
+
]);
|
|
44
|
+
function ScrollArea({ className, children, label, scrollResetKey, ...props }) {
|
|
45
|
+
const region = useRef(null);
|
|
46
|
+
useEffect(() => {
|
|
47
|
+
if (region.current) region.current.scrollTop = 0;
|
|
48
|
+
}, [scrollResetKey]);
|
|
49
|
+
return /* @__PURE__ */ jsx("div", {
|
|
50
|
+
...props,
|
|
51
|
+
ref: region,
|
|
52
|
+
tabIndex: 0,
|
|
53
|
+
role: "region",
|
|
54
|
+
"aria-label": label,
|
|
55
|
+
"data-slot": "scroll-area",
|
|
56
|
+
className: cn(scrollAreaVariants(), className),
|
|
57
|
+
children
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
//#endregion
|
|
61
|
+
export { ScrollArea, scrollAreaVariants };
|
|
62
|
+
|
|
63
|
+
//# sourceMappingURL=scroll-area.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scroll-area.js","names":[],"sources":["../../src/scroll-area/scroll-area.tsx"],"sourcesContent":["\"use client\";\n\nimport { cva } from \"class-variance-authority\";\nimport { useEffect, useRef, type ComponentPropsWithoutRef, type ReactNode } from \"react\";\n\nimport { cn } from \"../lib/cn.js\";\n\n/**\n * ScrollArea. React Aria has no equivalent, so this is a plain scrolling\n * div -- and that is the honest answer rather than a shortcut.\n *\n * Census: fourteen paths match, thirteen are counted, ELEVEN render\n * anything, and across them 17 uses. Every single one passes `className`\n * and nothing else, and 13 of the 17 are a height or flex constraint plus\n * padding. `ScrollBar` is exported by the legacy and imported by nobody.\n * `orientation=\"horizontal\"` has zero call sites.\n *\n * So there is no styled-scrollbar requirement to port. The legacy pays for\n * one anyway: Radix injects a `<style>` tag that hides the native\n * scrollbar, then re-draws it in JavaScript with a ResizeObserver, a\n * portal-free overlay and a `display: table` content wrapper. That is a\n * lot of machinery to reproduce a scrollbar, and it is why the legacy\n * `ScrollArea` cannot be scrolled by keyboard at all.\n *\n * Tailwind 4.3.3 ships `scrollbar-width` and `scrollbar-color` utilities\n * (verified in the installed dist, not assumed), which gets the same look\n * in three classes and no JavaScript.\n *\n * WHAT CANNOT BE STYLED, stated rather than glossed: `scrollbar-color`\n * takes a thumb and a track colour and nothing else -- no width in pixels,\n * no radius, no hover state, no margins. `::-webkit-scrollbar` can do\n * those but is non-standard and Firefox has never supported it. This\n * component uses the standard properties only.\n */\nexport const scrollAreaVariants = cva([\n \"overflow-y-auto overscroll-contain\",\n \"box-border\",\n // Standard scrollbar properties. thumb = --color-border (5.25:1 on the\n // page), track transparent so it does not draw a channel.\n \"scrollbar-thin scrollbar-thumb-border scrollbar-track-transparent\",\n // A scrollable region must be reachable by keyboard, or its content is\n // unreachable without a pointer -- WCAG 2.1.1, and axe's\n // scrollable-region-focusable rule. The legacy has no tabIndex at all,\n // so all 17 of its scroll areas are keyboard traps in the other\n // direction: you can see the content but never scroll to it.\n \"outline-none\",\n \"focus-visible:outline-solid\",\n \"focus-visible:outline-(length:--focus-ring-width)\",\n \"focus-visible:outline-offset-(--focus-ring-offset)\",\n \"focus-visible:outline-(--color-ring)\",\n]);\n\n/**\n * A scrolling region.\n *\n * @example\n * <ScrollArea label=\"Chat messages\" className=\"h-64\">...</ScrollArea>\n */\nexport interface ScrollAreaProps extends ComponentPropsWithoutRef<\"div\"> {\n children?: ReactNode;\n /**\n * Names the scrolling region.\n *\n * **Required**, and it took a failing test to get here. A focusable\n * element with no role fails axe `focus-order-semantics`, so the\n * keyboard fix below cannot ship without a role -- and a role with no\n * accessible name is a violation of its own. Naming it is the only\n * combination that is actually correct.\n *\n * `role=\"region\"` specifically. `group` was the first attempt -- it is\n * lighter, since region is a landmark and seventeen landmarks in one app\n * is noise -- but axe does not accept `group` as an appropriate role for\n * a focusable element, and it is right: a named region is the role the\n * ARIA practices name for a scrollable content area, and being able to\n * jump to it is the point.\n */\n label: string;\n /**\n * Change this when the contents change, and the region scrolls back to the\n * top.\n *\n * It lives here rather than on the caller because the caller cannot reach\n * this node: no component in this package exposes a `ref`, and it must not\n * -- React 18 is a supported peer, where a `ref` passed as a plain prop to a\n * function component is silently dropped. The scroller owns its own\n * scrollTop, so the reset belongs with it.\n *\n * Sheet forwards its own `scrollResetKey` straight through: going a level\n * deeper in a panel and landing halfway down the new view is disorienting,\n * and it is exactly the thing every call site forgets.\n */\n scrollResetKey?: string | number;\n}\n\nexport function ScrollArea({\n className,\n children,\n label,\n scrollResetKey,\n ...props\n}: ScrollAreaProps) {\n const region = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n // Unconditional rather than guarded on scrollResetKey being set: with no\n // key this runs once on mount, where scrollTop is already 0.\n if (region.current) region.current.scrollTop = 0;\n }, [scrollResetKey]);\n\n return (\n <div\n {...props}\n ref={region}\n // tabIndex 0 unconditionally: whether the content overflows is a\n // runtime fact that changes as the content does, and a region that\n // becomes scrollable after a fetch would otherwise never gain focus.\n tabIndex={0}\n role=\"region\"\n aria-label={label}\n data-slot=\"scroll-area\"\n className={cn(scrollAreaVariants(), className)}\n >\n {children}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkCA,MAAa,qBAAqB,IAAI;CACpC;CACA;CAGA;CAMA;CACA;CACA;CACA;CACA;AACF,CAAC;AA4CD,SAAgB,WAAW,EACzB,WACA,UACA,OACA,gBACA,GAAG,SACe;CAClB,MAAM,SAAS,OAAuB,IAAI;CAE1C,gBAAgB;EAGd,IAAI,OAAO,SAAS,OAAO,QAAQ,YAAY;CACjD,GAAG,CAAC,cAAc,CAAC;CAEnB,OACE,oBAAC,OAAD;EACE,GAAI;EACJ,KAAK;EAIL,UAAU;EACV,MAAK;EACL,cAAY;EACZ,aAAU;EACV,WAAW,GAAG,mBAAmB,GAAG,SAAS;EAE5C;CACE,CAAA;AAET"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { Select, SelectItem, SelectItemProps, SelectProps, selectItemVariants, selectPopoverVariants, selectTriggerVariants, selectVariants } from "./select.js";
|
|
2
|
+
export { Select, SelectItem, type SelectItemProps, type SelectProps, selectItemVariants, selectPopoverVariants, selectTriggerVariants, selectVariants };
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { ListBoxItemProps, SelectProps } from "react-aria-components";
|
|
2
|
+
import { ReactNode } from "react";
|
|
3
|
+
//#region src/select/select.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Select, ported from the legacy shadcn/Radix component.
|
|
6
|
+
*
|
|
7
|
+
* FOUR PARTS COLLAPSE INTO ONE. The census reads `SelectItem` 35 and
|
|
8
|
+
* `Select`, `SelectTrigger`, `SelectValue`, `SelectContent` 15 each --
|
|
9
|
+
* those four always appear together in the same shape, exactly like
|
|
10
|
+
* Dialog's header trio, so they are one component and a prop.
|
|
11
|
+
* `SelectGroup`, `SelectLabel`, `SelectSeparator`, `SelectScrollUpButton`
|
|
12
|
+
* and `SelectScrollDownButton` have ZERO call sites and are not ported.
|
|
13
|
+
*
|
|
14
|
+
* `label` IS REQUIRED. The legacy exports a `SelectLabel` that nobody
|
|
15
|
+
* renders and passes no `aria-label` anywhere, so all 15 selects are
|
|
16
|
+
* unnamed comboboxes -- the same finding as Progress, Slider and
|
|
17
|
+
* ScrollArea. Pass `labelHidden` when the surrounding UI already says it.
|
|
18
|
+
*
|
|
19
|
+
* The trigger reuses `fieldSurfaceVariants`, shared with Input and
|
|
20
|
+
* Textarea. A select and a text field sit in the same form row and must
|
|
21
|
+
* be the same box; the legacy styled them separately and drifted --
|
|
22
|
+
* `h-10` here against Input's `h-9`, for no stated reason.
|
|
23
|
+
*
|
|
24
|
+
* The popover is `card`, not a `popover` role: `--popover` is
|
|
25
|
+
* byte-identical to `--card` in the legacy theme. Item hover is `muted`,
|
|
26
|
+
* not `accent`, for the same reason.
|
|
27
|
+
*/
|
|
28
|
+
export declare const selectVariants: (props?: import("class-variance-authority/types").ClassProp | undefined) => string;
|
|
29
|
+
export declare const selectTriggerVariants: (props?: import("class-variance-authority/types").ClassProp | undefined) => string;
|
|
30
|
+
export declare const selectPopoverVariants: (props?: import("class-variance-authority/types").ClassProp | undefined) => string;
|
|
31
|
+
export declare const selectItemVariants: (props?: import("class-variance-authority/types").ClassProp | undefined) => string;
|
|
32
|
+
/**
|
|
33
|
+
* A single-choice picker.
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* <Select label="Country" placeholder="Pick one"
|
|
37
|
+
* selectedKey={code} onSelectionChange={setCode}>
|
|
38
|
+
* <SelectItem id="gb">United Kingdom</SelectItem>
|
|
39
|
+
* <SelectItem id="us">United States</SelectItem>
|
|
40
|
+
* </Select>
|
|
41
|
+
*/
|
|
42
|
+
interface SelectProps$1 extends Omit<SelectProps<object>, "className" | "style" | "children" | "items"> {
|
|
43
|
+
/** `SelectItem` children. */
|
|
44
|
+
children?: ReactNode;
|
|
45
|
+
/**
|
|
46
|
+
* What is being chosen, and the control's accessible name.
|
|
47
|
+
*
|
|
48
|
+
* **Required.** All 15 legacy selects are unnamed: the legacy exports a
|
|
49
|
+
* `SelectLabel` that nobody renders and passes no `aria-label`.
|
|
50
|
+
*/
|
|
51
|
+
label: ReactNode;
|
|
52
|
+
/** Render the label for screen readers only. */
|
|
53
|
+
labelHidden?: boolean;
|
|
54
|
+
/** Shown while nothing is selected. */
|
|
55
|
+
placeholder?: string;
|
|
56
|
+
/**
|
|
57
|
+
* Why the selection is wrong. Rendered under the control, and it is what
|
|
58
|
+
* makes `isInvalid` reach a screen reader.
|
|
59
|
+
*
|
|
60
|
+
* React Aria marks the ROOT `data-invalid` and leaves the button untouched,
|
|
61
|
+
* and an `aria-invalid` handed to that button is dropped by
|
|
62
|
+
* `filterDOMProps`, whose allowlist stops at the labelling attributes -- the
|
|
63
|
+
* same silent loss as `aria-current` on a pagination button. So without a
|
|
64
|
+
* message, an invalid select is invalid only in colour, which is WCAG 1.4.1
|
|
65
|
+
* on its own. React Aria wires this into `aria-describedby` for us.
|
|
66
|
+
*/
|
|
67
|
+
errorMessage?: ReactNode;
|
|
68
|
+
/** Extra classes for the wrapper. Merged last. */
|
|
69
|
+
className?: string;
|
|
70
|
+
}
|
|
71
|
+
declare function Select$1({ children, label, labelHidden, placeholder, errorMessage, className, ...props }: SelectProps$1): import("react").JSX.Element;
|
|
72
|
+
/**
|
|
73
|
+
* One option. Its `id` is the value.
|
|
74
|
+
*
|
|
75
|
+
* @example
|
|
76
|
+
* <SelectItem id="gb">United Kingdom</SelectItem>
|
|
77
|
+
*/
|
|
78
|
+
export interface SelectItemProps extends Omit<ListBoxItemProps, "className" | "style" | "children"> {
|
|
79
|
+
children?: ReactNode;
|
|
80
|
+
/**
|
|
81
|
+
* The option's text, for the trigger and for typeahead.
|
|
82
|
+
*
|
|
83
|
+
* Derived from `children` when they are a string. Pass it explicitly
|
|
84
|
+
* when the option renders anything richer than text.
|
|
85
|
+
*/
|
|
86
|
+
textValue?: string;
|
|
87
|
+
/** Extra classes. Merged last, so they win conflicts with ours. */
|
|
88
|
+
className?: string;
|
|
89
|
+
}
|
|
90
|
+
export declare function SelectItem({ className, children, textValue, ...props }: SelectItemProps): import("react").JSX.Element;
|
|
91
|
+
//#endregion
|
|
92
|
+
export { Select$1 as Select, SelectProps$1 as SelectProps };
|
|
93
|
+
//# sourceMappingURL=select.d.ts.map
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cn } from "../lib/cn.js";
|
|
3
|
+
import { fieldSurfaceVariants } from "../lib/field-surface.js";
|
|
4
|
+
import { cva } from "class-variance-authority";
|
|
5
|
+
import { Button, FieldError, Label, ListBox, ListBoxItem, Popover, Select, SelectValue } from "react-aria-components";
|
|
6
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
7
|
+
//#region src/select/select.tsx
|
|
8
|
+
/**
|
|
9
|
+
* Select, ported from the legacy shadcn/Radix component.
|
|
10
|
+
*
|
|
11
|
+
* FOUR PARTS COLLAPSE INTO ONE. The census reads `SelectItem` 35 and
|
|
12
|
+
* `Select`, `SelectTrigger`, `SelectValue`, `SelectContent` 15 each --
|
|
13
|
+
* those four always appear together in the same shape, exactly like
|
|
14
|
+
* Dialog's header trio, so they are one component and a prop.
|
|
15
|
+
* `SelectGroup`, `SelectLabel`, `SelectSeparator`, `SelectScrollUpButton`
|
|
16
|
+
* and `SelectScrollDownButton` have ZERO call sites and are not ported.
|
|
17
|
+
*
|
|
18
|
+
* `label` IS REQUIRED. The legacy exports a `SelectLabel` that nobody
|
|
19
|
+
* renders and passes no `aria-label` anywhere, so all 15 selects are
|
|
20
|
+
* unnamed comboboxes -- the same finding as Progress, Slider and
|
|
21
|
+
* ScrollArea. Pass `labelHidden` when the surrounding UI already says it.
|
|
22
|
+
*
|
|
23
|
+
* The trigger reuses `fieldSurfaceVariants`, shared with Input and
|
|
24
|
+
* Textarea. A select and a text field sit in the same form row and must
|
|
25
|
+
* be the same box; the legacy styled them separately and drifted --
|
|
26
|
+
* `h-10` here against Input's `h-9`, for no stated reason.
|
|
27
|
+
*
|
|
28
|
+
* The popover is `card`, not a `popover` role: `--popover` is
|
|
29
|
+
* byte-identical to `--card` in the legacy theme. Item hover is `muted`,
|
|
30
|
+
* not `accent`, for the same reason.
|
|
31
|
+
*/
|
|
32
|
+
const selectVariants = cva(["group flex w-full flex-col gap-1.5 box-border font-sans"]);
|
|
33
|
+
const selectTriggerVariants = cva([
|
|
34
|
+
"group-data-[invalid]:border-destructive-strong",
|
|
35
|
+
fieldSurfaceVariants(),
|
|
36
|
+
"flex items-center justify-between gap-2 text-start",
|
|
37
|
+
"h-(--density-control-h-md) px-(--density-control-pad-md)",
|
|
38
|
+
"text-base leading-(--leading-normal) cursor-pointer",
|
|
39
|
+
"[&_svg]:pointer-events-none [&_svg]:shrink-0",
|
|
40
|
+
"[&_svg:not([class*='size-'])]:size-(--icon-sm)"
|
|
41
|
+
]);
|
|
42
|
+
const selectPopoverVariants = cva([
|
|
43
|
+
"w-(--trigger-width) min-w-32 overflow-y-auto",
|
|
44
|
+
"rounded-md border-(length:--border-width) border-border p-1",
|
|
45
|
+
"bg-card text-card-foreground shadow-floating",
|
|
46
|
+
"box-border font-sans text-sm leading-(--leading-normal)"
|
|
47
|
+
]);
|
|
48
|
+
const selectItemVariants = cva([
|
|
49
|
+
"relative flex cursor-pointer select-none items-center gap-2",
|
|
50
|
+
"rounded-2xs py-1.5 pe-2 ps-8",
|
|
51
|
+
"box-border text-sm leading-(--leading-normal) text-card-foreground",
|
|
52
|
+
"outline-none data-[focused]:bg-muted",
|
|
53
|
+
"data-[focus-visible]:outline-solid",
|
|
54
|
+
"data-[focus-visible]:outline-(length:--focus-ring-width)",
|
|
55
|
+
"data-[focus-visible]:outline-offset-(--focus-ring-offset)",
|
|
56
|
+
"data-[focus-visible]:outline-(--color-ring)",
|
|
57
|
+
"data-[disabled]:cursor-not-allowed data-[disabled]:opacity-(--opacity-disabled)"
|
|
58
|
+
]);
|
|
59
|
+
function Select$1({ children, label, labelHidden, placeholder, errorMessage, className, ...props }) {
|
|
60
|
+
return /* @__PURE__ */ jsxs(Select, {
|
|
61
|
+
...props,
|
|
62
|
+
"data-slot": "select",
|
|
63
|
+
className: cn(selectVariants(), className),
|
|
64
|
+
children: [
|
|
65
|
+
/* @__PURE__ */ jsx(Label, {
|
|
66
|
+
"data-slot": "select-label",
|
|
67
|
+
className: cn("text-sm leading-(--leading-normal) text-foreground", labelHidden && "sr-only"),
|
|
68
|
+
children: label
|
|
69
|
+
}),
|
|
70
|
+
/* @__PURE__ */ jsxs(Button, {
|
|
71
|
+
"data-slot": "select-trigger",
|
|
72
|
+
className: selectTriggerVariants(),
|
|
73
|
+
children: [/* @__PURE__ */ jsx(SelectValue, {
|
|
74
|
+
"data-slot": "select-value",
|
|
75
|
+
children: ({ isPlaceholder, selectedText }) => isPlaceholder ? /* @__PURE__ */ jsx("span", {
|
|
76
|
+
"data-slot": "select-placeholder",
|
|
77
|
+
className: "text-muted-foreground",
|
|
78
|
+
children: placeholder
|
|
79
|
+
}) : selectedText
|
|
80
|
+
}), /* @__PURE__ */ jsx("svg", {
|
|
81
|
+
"aria-hidden": "true",
|
|
82
|
+
viewBox: "0 0 16 16",
|
|
83
|
+
fill: "none",
|
|
84
|
+
stroke: "currentColor",
|
|
85
|
+
strokeWidth: "1.75",
|
|
86
|
+
strokeLinecap: "round",
|
|
87
|
+
strokeLinejoin: "round",
|
|
88
|
+
className: "text-muted-foreground",
|
|
89
|
+
children: /* @__PURE__ */ jsx("path", { d: "m4 6 4 4 4-4" })
|
|
90
|
+
})]
|
|
91
|
+
}),
|
|
92
|
+
errorMessage == null ? null : /* @__PURE__ */ jsx(FieldError, {
|
|
93
|
+
"data-slot": "select-error",
|
|
94
|
+
className: "text-sm leading-(--leading-normal) text-destructive-strong",
|
|
95
|
+
children: errorMessage
|
|
96
|
+
}),
|
|
97
|
+
/* @__PURE__ */ jsx(Popover, {
|
|
98
|
+
"data-slot": "select-popover",
|
|
99
|
+
className: selectPopoverVariants(),
|
|
100
|
+
children: /* @__PURE__ */ jsx(ListBox, {
|
|
101
|
+
"data-slot": "select-list",
|
|
102
|
+
className: "outline-none",
|
|
103
|
+
children
|
|
104
|
+
})
|
|
105
|
+
})
|
|
106
|
+
]
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
function SelectItem({ className, children, textValue, ...props }) {
|
|
110
|
+
return /* @__PURE__ */ jsx(ListBoxItem, {
|
|
111
|
+
...props,
|
|
112
|
+
textValue: textValue ?? (typeof children === "string" ? children : void 0),
|
|
113
|
+
"data-slot": "select-item",
|
|
114
|
+
className: cn(selectItemVariants(), className),
|
|
115
|
+
children: ({ isSelected }) => /* @__PURE__ */ jsxs(Fragment, { children: [isSelected ? /* @__PURE__ */ jsx("svg", {
|
|
116
|
+
"data-slot": "select-item-check",
|
|
117
|
+
"aria-hidden": "true",
|
|
118
|
+
viewBox: "0 0 16 16",
|
|
119
|
+
fill: "none",
|
|
120
|
+
stroke: "currentColor",
|
|
121
|
+
strokeWidth: "2",
|
|
122
|
+
strokeLinecap: "round",
|
|
123
|
+
strokeLinejoin: "round",
|
|
124
|
+
className: "absolute start-2 size-3.5",
|
|
125
|
+
children: /* @__PURE__ */ jsx("path", { d: "m3.5 8.5 3 3 6-6" })
|
|
126
|
+
}) : null, children] })
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
//#endregion
|
|
130
|
+
export { Select$1 as Select, SelectItem, selectItemVariants, selectPopoverVariants, selectTriggerVariants, selectVariants };
|
|
131
|
+
|
|
132
|
+
//# sourceMappingURL=select.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"select.js","names":["Select","AriaSelect","AriaLabel","AriaButton","AriaSelectValue","AriaFieldError","AriaPopover","AriaListBox","AriaListBoxItem"],"sources":["../../src/select/select.tsx"],"sourcesContent":["\"use client\";\n\nimport { cva } from \"class-variance-authority\";\nimport type { ReactNode } from \"react\";\nimport {\n Button as AriaButton,\n FieldError as AriaFieldError,\n Label as AriaLabel,\n ListBox as AriaListBox,\n ListBoxItem as AriaListBoxItem,\n Popover as AriaPopover,\n Select as AriaSelect,\n SelectValue as AriaSelectValue,\n type ListBoxItemProps as AriaListBoxItemProps,\n type SelectProps as AriaSelectProps,\n} from \"react-aria-components\";\n\nimport { cn } from \"../lib/cn.js\";\nimport { fieldSurfaceVariants } from \"../lib/field-surface.js\";\n\n/**\n * Select, ported from the legacy shadcn/Radix component.\n *\n * FOUR PARTS COLLAPSE INTO ONE. The census reads `SelectItem` 35 and\n * `Select`, `SelectTrigger`, `SelectValue`, `SelectContent` 15 each --\n * those four always appear together in the same shape, exactly like\n * Dialog's header trio, so they are one component and a prop.\n * `SelectGroup`, `SelectLabel`, `SelectSeparator`, `SelectScrollUpButton`\n * and `SelectScrollDownButton` have ZERO call sites and are not ported.\n *\n * `label` IS REQUIRED. The legacy exports a `SelectLabel` that nobody\n * renders and passes no `aria-label` anywhere, so all 15 selects are\n * unnamed comboboxes -- the same finding as Progress, Slider and\n * ScrollArea. Pass `labelHidden` when the surrounding UI already says it.\n *\n * The trigger reuses `fieldSurfaceVariants`, shared with Input and\n * Textarea. A select and a text field sit in the same form row and must\n * be the same box; the legacy styled them separately and drifted --\n * `h-10` here against Input's `h-9`, for no stated reason.\n *\n * The popover is `card`, not a `popover` role: `--popover` is\n * byte-identical to `--card` in the legacy theme. Item hover is `muted`,\n * not `accent`, for the same reason.\n */\n/*\n * `group` is load-bearing, not decoration. React Aria puts `data-invalid` on\n * THIS element, never on the trigger -- so the trigger's own\n * `data-[invalid]:` rule, inherited from the shared field surface, can never\n * match and `isInvalid` painted nothing at all. The trigger reads the state\n * from here instead.\n */\nexport const selectVariants = cva([\"group flex w-full flex-col gap-1.5 box-border font-sans\"]);\n\nexport const selectTriggerVariants = cva([\n // See selectVariants: the invalid state lives on the ROOT, so this reads it\n // from the group rather than from itself.\n \"group-data-[invalid]:border-destructive-strong\",\n fieldSurfaceVariants(),\n \"flex items-center justify-between gap-2 text-start\",\n \"h-(--density-control-h-md) px-(--density-control-pad-md)\",\n \"text-base leading-(--leading-normal) cursor-pointer\",\n \"[&_svg]:pointer-events-none [&_svg]:shrink-0\",\n \"[&_svg:not([class*='size-'])]:size-(--icon-sm)\",\n]);\n\nexport const selectPopoverVariants = cva([\n // --trigger-width is set inline by React Aria, so the list matches the\n // control it came from rather than guessing a width.\n \"w-(--trigger-width) min-w-32 overflow-y-auto\",\n \"rounded-md border-(length:--border-width) border-border p-1\",\n \"bg-card text-card-foreground shadow-floating\",\n \"box-border font-sans text-sm leading-(--leading-normal)\",\n]);\n\nexport const selectItemVariants = cva([\n \"relative flex cursor-pointer select-none items-center gap-2\",\n \"rounded-2xs py-1.5 pe-2 ps-8\",\n \"box-border text-sm leading-(--leading-normal) text-card-foreground\",\n // data-focused, not :hover. React Aria drives one highlight from both\n // pointer and keyboard, so the row under the mouse and the row the\n // arrows reached are the same row.\n \"outline-none data-[focused]:bg-muted\",\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]:cursor-not-allowed data-[disabled]:opacity-(--opacity-disabled)\",\n]);\n\n/**\n * A single-choice picker.\n *\n * @example\n * <Select label=\"Country\" placeholder=\"Pick one\"\n * selectedKey={code} onSelectionChange={setCode}>\n * <SelectItem id=\"gb\">United Kingdom</SelectItem>\n * <SelectItem id=\"us\">United States</SelectItem>\n * </Select>\n */\nexport interface SelectProps\n extends Omit<AriaSelectProps<object>, \"className\" | \"style\" | \"children\" | \"items\"> {\n /** `SelectItem` children. */\n children?: ReactNode;\n /**\n * What is being chosen, and the control's accessible name.\n *\n * **Required.** All 15 legacy selects are unnamed: the legacy exports a\n * `SelectLabel` that nobody renders and passes no `aria-label`.\n */\n label: ReactNode;\n /** Render the label for screen readers only. */\n labelHidden?: boolean;\n /** Shown while nothing is selected. */\n placeholder?: string;\n /**\n * Why the selection is wrong. Rendered under the control, and it is what\n * makes `isInvalid` reach a screen reader.\n *\n * React Aria marks the ROOT `data-invalid` and leaves the button untouched,\n * and an `aria-invalid` handed to that button is dropped by\n * `filterDOMProps`, whose allowlist stops at the labelling attributes -- the\n * same silent loss as `aria-current` on a pagination button. So without a\n * message, an invalid select is invalid only in colour, which is WCAG 1.4.1\n * on its own. React Aria wires this into `aria-describedby` for us.\n */\n errorMessage?: ReactNode;\n /** Extra classes for the wrapper. Merged last. */\n className?: string;\n}\n\nexport function Select({\n children,\n label,\n labelHidden,\n placeholder,\n errorMessage,\n className,\n ...props\n}: SelectProps) {\n return (\n <AriaSelect {...props} data-slot=\"select\" className={cn(selectVariants(), className)}>\n <AriaLabel\n data-slot=\"select-label\"\n className={cn(\n \"text-sm leading-(--leading-normal) text-foreground\",\n labelHidden && \"sr-only\",\n )}\n >\n {label}\n </AriaLabel>\n\n <AriaButton data-slot=\"select-trigger\" className={selectTriggerVariants()}>\n <AriaSelectValue data-slot=\"select-value\">\n {({ isPlaceholder, selectedText }) =>\n isPlaceholder ? (\n <span data-slot=\"select-placeholder\" className=\"text-muted-foreground\">\n {placeholder}\n </span>\n ) : (\n selectedText\n )\n }\n </AriaSelectValue>\n {/*\n aria-hidden: the chevron is decoration. The button already\n announces itself as a combobox, and a second name source only\n makes it announce twice.\n */}\n <svg\n aria-hidden=\"true\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.75\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className=\"text-muted-foreground\"\n >\n <path d=\"m4 6 4 4 4-4\" />\n </svg>\n </AriaButton>\n\n {errorMessage == null ? null : (\n // Not conditional on isInvalid: React Aria renders FieldError only\n // while the field IS invalid, so gating it here as well would just be\n // two sources of truth for one state.\n <AriaFieldError\n data-slot=\"select-error\"\n className=\"text-sm leading-(--leading-normal) text-destructive-strong\"\n >\n {errorMessage}\n </AriaFieldError>\n )}\n\n <AriaPopover data-slot=\"select-popover\" className={selectPopoverVariants()}>\n <AriaListBox data-slot=\"select-list\" className=\"outline-none\">\n {children}\n </AriaListBox>\n </AriaPopover>\n </AriaSelect>\n );\n}\n\n/**\n * One option. Its `id` is the value.\n *\n * @example\n * <SelectItem id=\"gb\">United Kingdom</SelectItem>\n */\nexport interface SelectItemProps\n extends Omit<AriaListBoxItemProps, \"className\" | \"style\" | \"children\"> {\n children?: ReactNode;\n /**\n * The option's text, for the trigger and for typeahead.\n *\n * Derived from `children` when they are a string. Pass it explicitly\n * when the option renders anything richer than text.\n */\n textValue?: string;\n /** Extra classes. Merged last, so they win conflicts with ours. */\n className?: string;\n}\n\nexport function SelectItem({ className, children, textValue, ...props }: SelectItemProps) {\n return (\n <AriaListBoxItem\n {...props}\n /*\n textValue, derived from string children.\n React Aria infers an item's text from its children -- but ONLY\n when they are a string. This item uses FUNCTION children so the\n tick can be rendered in JS, which defeats that inference: the\n trigger showed an empty value after selecting, and typeahead had\n nothing to match on. Neither is visible in the type system.\n */\n textValue={textValue ?? (typeof children === \"string\" ? children : undefined)}\n data-slot=\"select-item\"\n className={cn(selectItemVariants(), className)}\n >\n {({ isSelected }) => (\n <>\n {/*\n The tick is rendered in JS rather than toggled with CSS, so\n the precedence is explicit and a test can read it from the\n DOM. ps-8 above reserves the gutter, so selecting an option\n does not shift its label.\n */}\n {isSelected ? (\n <svg\n data-slot=\"select-item-check\"\n aria-hidden=\"true\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className=\"absolute start-2 size-3.5\"\n >\n <path d=\"m3.5 8.5 3 3 6-6\" />\n </svg>\n ) : null}\n {children}\n </>\n )}\n </AriaListBoxItem>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmDA,MAAa,iBAAiB,IAAI,CAAC,yDAAyD,CAAC;AAE7F,MAAa,wBAAwB,IAAI;CAGvC;CACA,qBAAqB;CACrB;CACA;CACA;CACA;CACA;AACF,CAAC;AAED,MAAa,wBAAwB,IAAI;CAGvC;CACA;CACA;CACA;AACF,CAAC;AAED,MAAa,qBAAqB,IAAI;CACpC;CACA;CACA;CAIA;CACA;CACA;CACA;CACA;CACA;AACF,CAAC;AA2CD,SAAgBA,SAAO,EACrB,UACA,OACA,aACA,aACA,cACA,WACA,GAAG,SACW;CACd,OACE,qBAACC,QAAD;EAAY,GAAI;EAAO,aAAU;EAAS,WAAW,GAAG,eAAe,GAAG,SAAS;EAAnF,UAAA;GACE,oBAACC,OAAD;IACE,aAAU;IACV,WAAW,GACT,sDACA,eAAe,SACjB;IAEC,UAAA;GACQ,CAAA;GAEX,qBAACC,QAAD;IAAY,aAAU;IAAiB,WAAW,sBAAsB;IAAxE,UAAA,CACE,oBAACC,aAAD;KAAiB,aAAU;KACvB,WAAA,EAAE,eAAe,mBACjB,gBACE,oBAAC,QAAD;MAAM,aAAU;MAAqB,WAAU;MAC5C,UAAA;KACG,CAAA,IAEN;IAGW,CAAA,GAMjB,oBAAC,OAAD;KACE,eAAY;KACZ,SAAQ;KACR,MAAK;KACL,QAAO;KACP,aAAY;KACZ,eAAc;KACd,gBAAe;KACf,WAAU;KAEV,UAAA,oBAAC,QAAD,EAAM,GAAE,eAAgB,CAAA;IACrB,CAAA,CACK;;GAEX,gBAAgB,OAAO,OAItB,oBAACC,YAAD;IACE,aAAU;IACV,WAAU;IAET,UAAA;GACa,CAAA;GAGlB,oBAACC,SAAD;IAAa,aAAU;IAAiB,WAAW,sBAAsB;IACvE,UAAA,oBAACC,SAAD;KAAa,aAAU;KAAc,WAAU;KAC5C;IACU,CAAA;GACF,CAAA;EACH;;AAEhB;AAsBA,SAAgB,WAAW,EAAE,WAAW,UAAU,WAAW,GAAG,SAA0B;CACxF,OACE,oBAACC,aAAD;EACE,GAAI;EASJ,WAAW,cAAc,OAAO,aAAa,WAAW,WAAW,KAAA;EACnE,aAAU;EACV,WAAW,GAAG,mBAAmB,GAAG,SAAS;EAE3C,WAAA,EAAE,iBACF,qBAAA,UAAA,EAAA,UAAA,CAOG,aACC,oBAAC,OAAD;GACE,aAAU;GACV,eAAY;GACZ,SAAQ;GACR,MAAK;GACL,QAAO;GACP,aAAY;GACZ,eAAc;GACd,gBAAe;GACf,WAAU;GAEV,UAAA,oBAAC,QAAD,EAAM,GAAE,mBAAoB,CAAA;EACzB,CAAA,IACH,MACH,QACD,EAAA,CAAA;CAEW,CAAA;AAErB"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { SeparatorProps } from "react-aria-components";
|
|
2
|
+
//#region src/separator/separator.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* Separator, ported from the legacy component onto React Aria's Separator.
|
|
5
|
+
*
|
|
6
|
+
* Census: 7 files, 6 uses, one of them `orientation="vertical"`. No other
|
|
7
|
+
* prop is ever passed.
|
|
8
|
+
*
|
|
9
|
+
* `decorative` is NOT ported -- zero call sites pass it, and React Aria has
|
|
10
|
+
* no equivalent. That does change the semantics of all six existing uses,
|
|
11
|
+
* which is worth saying out loud rather than burying: the legacy defaulted
|
|
12
|
+
* to `decorative: true` and therefore rendered `role="none"`, so every
|
|
13
|
+
* divider in the app is currently invisible to assistive technology. React
|
|
14
|
+
* Aria renders an `<hr>` for a horizontal separator, whose implicit role IS
|
|
15
|
+
* separator. A visible line between two groups of content is a separator;
|
|
16
|
+
* announcing it is the correct behaviour, and it is what the element the
|
|
17
|
+
* legacy chose (a `div`) was pretending not to be.
|
|
18
|
+
*
|
|
19
|
+
* Every string in the cva blocks below is a STATIC literal. Tailwind scans
|
|
20
|
+
* source text, so a class assembled at runtime is invisible to it and would
|
|
21
|
+
* simply never be emitted.
|
|
22
|
+
*/
|
|
23
|
+
export declare const separatorVariants: (props?: ({
|
|
24
|
+
orientation?: "horizontal" | "vertical" | null | undefined;
|
|
25
|
+
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
26
|
+
/**
|
|
27
|
+
* A visual divider between two groups of content.
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* <Separator />
|
|
31
|
+
* <Separator orientation="vertical" />
|
|
32
|
+
*/
|
|
33
|
+
interface SeparatorProps$1 extends Omit<SeparatorProps, "className" | "style"> {
|
|
34
|
+
/** @default "horizontal" */
|
|
35
|
+
orientation?: "horizontal" | "vertical";
|
|
36
|
+
/** Extra classes. Merged last, so they win conflicts with ours. */
|
|
37
|
+
className?: string;
|
|
38
|
+
}
|
|
39
|
+
declare function Separator$1({ className, orientation, ...props }: SeparatorProps$1): import("react").JSX.Element;
|
|
40
|
+
//#endregion
|
|
41
|
+
export { Separator$1 as Separator, SeparatorProps$1 as SeparatorProps };
|
|
42
|
+
//# sourceMappingURL=separator.d.ts.map
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cn } from "../lib/cn.js";
|
|
3
|
+
import { cva } from "class-variance-authority";
|
|
4
|
+
import { Separator } from "react-aria-components";
|
|
5
|
+
import { jsx } from "react/jsx-runtime";
|
|
6
|
+
//#region src/separator/separator.tsx
|
|
7
|
+
/**
|
|
8
|
+
* Separator, ported from the legacy component onto React Aria's Separator.
|
|
9
|
+
*
|
|
10
|
+
* Census: 7 files, 6 uses, one of them `orientation="vertical"`. No other
|
|
11
|
+
* prop is ever passed.
|
|
12
|
+
*
|
|
13
|
+
* `decorative` is NOT ported -- zero call sites pass it, and React Aria has
|
|
14
|
+
* no equivalent. That does change the semantics of all six existing uses,
|
|
15
|
+
* which is worth saying out loud rather than burying: the legacy defaulted
|
|
16
|
+
* to `decorative: true` and therefore rendered `role="none"`, so every
|
|
17
|
+
* divider in the app is currently invisible to assistive technology. React
|
|
18
|
+
* Aria renders an `<hr>` for a horizontal separator, whose implicit role IS
|
|
19
|
+
* separator. A visible line between two groups of content is a separator;
|
|
20
|
+
* announcing it is the correct behaviour, and it is what the element the
|
|
21
|
+
* legacy chose (a `div`) was pretending not to be.
|
|
22
|
+
*
|
|
23
|
+
* Every string in the cva blocks below is a STATIC literal. Tailwind scans
|
|
24
|
+
* source text, so a class assembled at runtime is invisible to it and would
|
|
25
|
+
* simply never be emitted.
|
|
26
|
+
*/
|
|
27
|
+
const separatorVariants = cva([
|
|
28
|
+
"shrink-0 bg-border border-none",
|
|
29
|
+
"m-0",
|
|
30
|
+
"box-border"
|
|
31
|
+
], {
|
|
32
|
+
variants: { orientation: {
|
|
33
|
+
horizontal: "h-px w-full",
|
|
34
|
+
vertical: "h-full w-px"
|
|
35
|
+
} },
|
|
36
|
+
defaultVariants: { orientation: "horizontal" }
|
|
37
|
+
});
|
|
38
|
+
function Separator$1({ className, orientation = "horizontal", ...props }) {
|
|
39
|
+
return /* @__PURE__ */ jsx(Separator, {
|
|
40
|
+
...props,
|
|
41
|
+
orientation,
|
|
42
|
+
"data-slot": "separator",
|
|
43
|
+
className: cn(separatorVariants({ orientation }), className)
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
//#endregion
|
|
47
|
+
export { Separator$1 as Separator, separatorVariants };
|
|
48
|
+
|
|
49
|
+
//# sourceMappingURL=separator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"separator.js","names":["Separator","AriaSeparator"],"sources":["../../src/separator/separator.tsx"],"sourcesContent":["\"use client\";\n\nimport { cva } from \"class-variance-authority\";\nimport {\n Separator as AriaSeparator,\n type SeparatorProps as AriaSeparatorProps,\n} from \"react-aria-components\";\n\nimport { cn } from \"../lib/cn.js\";\n\n/**\n * Separator, ported from the legacy component onto React Aria's Separator.\n *\n * Census: 7 files, 6 uses, one of them `orientation=\"vertical\"`. No other\n * prop is ever passed.\n *\n * `decorative` is NOT ported -- zero call sites pass it, and React Aria has\n * no equivalent. That does change the semantics of all six existing uses,\n * which is worth saying out loud rather than burying: the legacy defaulted\n * to `decorative: true` and therefore rendered `role=\"none\"`, so every\n * divider in the app is currently invisible to assistive technology. React\n * Aria renders an `<hr>` for a horizontal separator, whose implicit role IS\n * separator. A visible line between two groups of content is a separator;\n * announcing it is the correct behaviour, and it is what the element the\n * legacy chose (a `div`) was pretending not to be.\n *\n * Every string in the cva blocks below is a STATIC literal. Tailwind scans\n * source text, so a class assembled at runtime is invisible to it and would\n * simply never be emitted.\n */\nexport const separatorVariants = cva(\n [\n // border-none, not border-0. `border-0` sets the WIDTH to zero but leaves\n // border-style at solid, so the computed style still claims a border that\n // simply measures nothing -- the same bookkeeping the Checkbox parity run\n // turned up on the legacy input. `border-none` says what is true.\n \"shrink-0 bg-border border-none\",\n // The user-agent <hr> ships a block margin and a 1px inset border, and\n // React Aria renders an <hr> for the horizontal case. The reset in\n // @seekalfred/styles is OPT-IN, so a consumer may load none -- unreset,\n // every divider is a grooved double line with space around it.\n \"m-0\",\n \"box-border\",\n ],\n {\n variants: {\n orientation: {\n // h-px / w-px, not the legacy h-[1px] / w-[1px]. Those are bracket\n // values that check:hardcoding rejects, and `px` is a first-class\n // Tailwind step rather than an arbitrary one.\n horizontal: \"h-px w-full\",\n vertical: \"h-full w-px\",\n },\n },\n defaultVariants: { orientation: \"horizontal\" },\n },\n);\n\n/**\n * A visual divider between two groups of content.\n *\n * @example\n * <Separator />\n * <Separator orientation=\"vertical\" />\n */\nexport interface SeparatorProps extends Omit<AriaSeparatorProps, \"className\" | \"style\"> {\n /** @default \"horizontal\" */\n orientation?: \"horizontal\" | \"vertical\";\n /** Extra classes. Merged last, so they win conflicts with ours. */\n className?: string;\n}\n\nexport function Separator({ className, orientation = \"horizontal\", ...props }: SeparatorProps) {\n return (\n <AriaSeparator\n {...props}\n orientation={orientation}\n data-slot=\"separator\"\n className={cn(separatorVariants({ orientation }), className)}\n />\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AA8BA,MAAa,oBAAoB,IAC/B;CAKE;CAKA;CACA;AACF,GACA;CACE,UAAU,EACR,aAAa;EAIX,YAAY;EACZ,UAAU;CACZ,EACF;CACA,iBAAiB,EAAE,aAAa,aAAa;AAC/C,CACF;AAgBA,SAAgBA,YAAU,EAAE,WAAW,cAAc,cAAc,GAAG,SAAyB;CAC7F,OACE,oBAACC,WAAD;EACE,GAAI;EACS;EACb,aAAU;EACV,WAAW,GAAG,kBAAkB,EAAE,YAAY,CAAC,GAAG,SAAS;CAC5D,CAAA;AAEL"}
|
|
@@ -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
|