@akhil-saxena/design-system 1.2.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Akhil Saxena
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,122 @@
1
+ # @akhil-saxena/design-system
2
+
3
+ Accessible React primitives with semantic tokens. Full dark mode, cream + ink + amber design language.
4
+
5
+ **v1.2.0 - 55 components across 9 categories.**
6
+
7
+ [![npm](https://img.shields.io/npm/v/@akhil-saxena/design-system)](https://www.npmjs.com/package/@akhil-saxena/design-system)
8
+
9
+ ## Install
10
+
11
+ ```bash
12
+ npm install @akhil-saxena/design-system
13
+ ```
14
+
15
+ Peer deps: `react@^19`, `react-dom@^19`.
16
+
17
+ ## Quick Start
18
+
19
+ Import the CSS layers in your app entry point (order matters):
20
+
21
+ ```ts
22
+ import "@akhil-saxena/design-system/tokens.css";
23
+ import "@akhil-saxena/design-system/primitives.css";
24
+ import "@akhil-saxena/design-system/utilities.css";
25
+ ```
26
+
27
+ Then use components:
28
+
29
+ ```tsx
30
+ import { Button, Badge, Card } from "@akhil-saxena/design-system";
31
+
32
+ export function App() {
33
+ return (
34
+ <Card>
35
+ <Badge tone="amber">Active</Badge>
36
+ <Button variant="primary">Apply</Button>
37
+ </Card>
38
+ );
39
+ }
40
+ ```
41
+
42
+ Toggle dark mode by adding `class="dark"` on `<html>`:
43
+
44
+ ```ts
45
+ document.documentElement.classList.toggle("dark");
46
+ ```
47
+
48
+ ## Subpath imports
49
+
50
+ ```ts
51
+ import { Button, Modal, Table, AppShell } from "@akhil-saxena/design-system";
52
+
53
+ import { useFocusTrap, useReducedMotion } from "@akhil-saxena/design-system/hooks";
54
+
55
+ import { ChevronDown, Search } from "@akhil-saxena/design-system/icons";
56
+ ```
57
+
58
+ ## Components
59
+
60
+ ### Inputs (16)
61
+
62
+ Button, TextInput, Textarea, Badge, Chip, Checkbox, Radio, Toggle, NumberStepper, RangeSlider, StarRating, Autocomplete, DatePicker, DateRangePicker, MultiSelect, Select
63
+
64
+ ### Overlays (9)
65
+
66
+ Popover, Modal, BottomSheet, Tooltip, Sheet, HoverCard, Card, StickyNote, Lightbox
67
+
68
+ ### Data Display (9)
69
+
70
+ Table, Tabs, Accordion, Carousel, Timeline, InfiniteList, Calendar, Breadcrumbs, SegmentedControl
71
+
72
+ ### Feedback (6)
73
+
74
+ AlertBanner, Toast, Skeleton, ProgressBar, InlineConfirm, EmptyState
75
+
76
+ ### Interaction (6)
77
+
78
+ CopyToClipboard, InlineEdit, RichText, SearchAndFilters, Sortable, SplitButton
79
+
80
+ ### Layout (3)
81
+
82
+ AppShell, AppBar, Footer
83
+
84
+ ### Display (2)
85
+
86
+ Avatar, RollingNumber
87
+
88
+ ### Patterns (3)
89
+
90
+ Wizard, FormValidation, Coachmark
91
+
92
+ ### Foundation (1)
93
+
94
+ TokenCheck
95
+
96
+ ## Hooks
97
+
98
+ From `@akhil-saxena/design-system/hooks`:
99
+
100
+ - `useFocusTrap(ref, active)` - trap focus within an overlay
101
+ - `useClickOutside(ref, onOutside)` - fire callback on click outside ref
102
+ - `useReducedMotion()` - reflects `prefers-reduced-motion`
103
+ - `useMatchMedia(query)` - generic matchMedia hook
104
+ - `useTokens()` - read computed CSS custom property values at runtime
105
+ - `useSortableTable(data, options)` - sort state (column + direction)
106
+ - `useTableSelection(data, options)` - single + multi-select with indeterminate
107
+ - `useResizableColumns(initialWidths)` - pointer events column resize
108
+
109
+ ## Tokens
110
+
111
+ CSS custom properties in `tokens.css`:
112
+
113
+ - **Color** - cream/ink/amber ramps + AAA-tuned blue/purple/green/red status colors
114
+ - **Typography** - Inter (body), Archivo (display), JetBrains Mono (code/data)
115
+ - **Spacing** - 4px base, 12-step scale (4..64px)
116
+ - **Radius** - sm / md / lg / xl / pill
117
+ - **Shadow** - 1 / 2 / 3
118
+ - **Motion** - `--ease-out`, `--ease-in-out`, `--ease-spring` + `--dur-1..4`
119
+
120
+ ## License
121
+
122
+ MIT © 2026 Akhil Saxena
@@ -0,0 +1,10 @@
1
+ import { LucideProps, LucideIcon } from 'lucide-react';
2
+ import { ForwardRefExoticComponent, ReactNode, RefAttributes } from 'react';
3
+
4
+ interface IconProps extends Omit<LucideProps, "ref"> {
5
+ icon?: LucideIcon;
6
+ children?: ReactNode;
7
+ }
8
+ declare const Icon: ForwardRefExoticComponent<IconProps & RefAttributes<SVGSVGElement>>;
9
+
10
+ export { Icon as I, type IconProps as a };
@@ -0,0 +1,108 @@
1
+ import { useCallback, useEffect, useState } from 'react';
2
+
3
+ // src/hooks/useComposedRefs.ts
4
+ function setRef(ref, node) {
5
+ if (typeof ref === "function") ref(node);
6
+ else if (ref && "current" in ref) ref.current = node;
7
+ }
8
+ function useComposedRefs(...refs) {
9
+ return useCallback((node) => {
10
+ for (const ref of refs) setRef(ref, node);
11
+ }, refs);
12
+ }
13
+ function useClickOutside(ref, handler, enabled = true) {
14
+ useEffect(() => {
15
+ if (!enabled) return;
16
+ function listener(e) {
17
+ const el = ref.current;
18
+ if (!el || el.contains(e.target)) return;
19
+ handler(e);
20
+ }
21
+ document.addEventListener("mousedown", listener);
22
+ document.addEventListener("touchstart", listener);
23
+ return () => {
24
+ document.removeEventListener("mousedown", listener);
25
+ document.removeEventListener("touchstart", listener);
26
+ };
27
+ }, [ref, handler, enabled]);
28
+ }
29
+ var FOCUSABLE_SELECTOR = "a[href]:not([disabled]), button:not([disabled]), textarea:not([disabled]), input:not([disabled]):not([type='hidden']), select:not([disabled]), [tabindex]:not([tabindex='-1'])";
30
+ function useFocusTrap(container, active) {
31
+ useEffect(() => {
32
+ if (!active || !container) return;
33
+ const c = container;
34
+ const previouslyFocused = document.activeElement;
35
+ const focusables = () => Array.from(c.querySelectorAll(FOCUSABLE_SELECTOR)).filter(
36
+ (el) => !el.hasAttribute("inert")
37
+ );
38
+ const initial = focusables()[0];
39
+ if (initial) {
40
+ initial.focus();
41
+ } else {
42
+ c.focus();
43
+ }
44
+ function handleKeyDown(e) {
45
+ if (e.key !== "Tab") return;
46
+ const list = focusables();
47
+ const activeEl = document.activeElement;
48
+ const activeInside = activeEl ? c.contains(activeEl) : false;
49
+ if (list.length === 0) {
50
+ e.preventDefault();
51
+ if (!activeInside) c.focus();
52
+ return;
53
+ }
54
+ const firstEl = list[0];
55
+ const lastEl = list.at(-1);
56
+ if (!activeInside) {
57
+ e.preventDefault();
58
+ (e.shiftKey ? lastEl : firstEl).focus();
59
+ return;
60
+ }
61
+ if (e.shiftKey && activeEl === firstEl) {
62
+ e.preventDefault();
63
+ lastEl.focus();
64
+ } else if (!e.shiftKey && activeEl === lastEl) {
65
+ e.preventDefault();
66
+ firstEl.focus();
67
+ }
68
+ }
69
+ document.addEventListener("keydown", handleKeyDown);
70
+ return () => {
71
+ document.removeEventListener("keydown", handleKeyDown);
72
+ previouslyFocused?.focus?.();
73
+ };
74
+ }, [active, container]);
75
+ }
76
+ function useReducedMotion() {
77
+ const [reduced, setReduced] = useState(() => {
78
+ if (typeof window === "undefined") return false;
79
+ return window.matchMedia("(prefers-reduced-motion: reduce)").matches;
80
+ });
81
+ useEffect(() => {
82
+ if (typeof window === "undefined") return;
83
+ const mql = window.matchMedia("(prefers-reduced-motion: reduce)");
84
+ const handler = (e) => setReduced(e.matches);
85
+ mql.addEventListener("change", handler);
86
+ return () => mql.removeEventListener("change", handler);
87
+ }, []);
88
+ return reduced;
89
+ }
90
+ function useMatchMedia(query) {
91
+ const [matches, setMatches] = useState(() => {
92
+ if (typeof window === "undefined") return false;
93
+ return window.matchMedia(query).matches;
94
+ });
95
+ useEffect(() => {
96
+ if (typeof window === "undefined") return;
97
+ const mql = window.matchMedia(query);
98
+ const handler = (e) => setMatches(e.matches);
99
+ setMatches(mql.matches);
100
+ mql.addEventListener("change", handler);
101
+ return () => mql.removeEventListener("change", handler);
102
+ }, [query]);
103
+ return matches;
104
+ }
105
+
106
+ export { useClickOutside, useComposedRefs, useFocusTrap, useMatchMedia, useReducedMotion };
107
+ //# sourceMappingURL=chunk-FUXR6QZ3.js.map
108
+ //# sourceMappingURL=chunk-FUXR6QZ3.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/hooks/useComposedRefs.ts","../src/hooks/useClickOutside.ts","../src/hooks/useFocusTrap.ts","../src/hooks/useReducedMotion.ts","../src/hooks/useMatchMedia.ts"],"names":["useEffect","useState"],"mappings":"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","file":"chunk-FUXR6QZ3.js","sourcesContent":["import { type Ref, useCallback } from \"react\";\n\nfunction setRef<T>(ref: Ref<T> | undefined, node: T | null): void {\n\tif (typeof ref === \"function\") ref(node);\n\telse if (ref && \"current\" in ref) (ref as { current: T | null }).current = node;\n}\n\n/**\n * Combine multiple refs (function or object) into one callback ref.\n * Used by every primitive that does forwardRef + needs an internal ref\n * for a hook (Popover trigger, Modal container, etc.).\n */\nexport function useComposedRefs<T>(...refs: Array<Ref<T> | undefined>): (node: T | null) => void {\n\treturn useCallback((node: T | null) => {\n\t\tfor (const ref of refs) setRef(ref, node);\n\t}, refs);\n}\n","import { type RefObject, useEffect } from \"react\";\n\n/**\n * Calls `handler` when a mousedown/touchstart fires outside of `ref`'s element.\n * Used by Popover (Phase 14), Modal close-on-backdrop, Select dropdown dismiss.\n */\nexport function useClickOutside<T extends HTMLElement>(\n\tref: RefObject<T | null>,\n\thandler: (e: MouseEvent | TouchEvent) => void,\n\tenabled = true,\n): void {\n\tuseEffect(() => {\n\t\tif (!enabled) return;\n\t\tfunction listener(e: MouseEvent | TouchEvent) {\n\t\t\tconst el = ref.current;\n\t\t\tif (!el || el.contains(e.target as Node)) return;\n\t\t\thandler(e);\n\t\t}\n\t\tdocument.addEventListener(\"mousedown\", listener);\n\t\tdocument.addEventListener(\"touchstart\", listener);\n\t\treturn () => {\n\t\t\tdocument.removeEventListener(\"mousedown\", listener);\n\t\t\tdocument.removeEventListener(\"touchstart\", listener);\n\t\t};\n\t}, [ref, handler, enabled]);\n}\n","import { useEffect } from \"react\";\n\nconst FOCUSABLE_SELECTOR =\n\t\"a[href]:not([disabled]), button:not([disabled]), textarea:not([disabled]), \" +\n\t\"input:not([disabled]):not([type='hidden']), select:not([disabled]), \" +\n\t\"[tabindex]:not([tabindex='-1'])\";\n\n/**\n * Trap Tab/Shift+Tab focus inside `container` while `active` is true.\n *\n * Pass the actual DOM node (use a callback ref + useState pattern in the\n * caller). This guarantees the effect re-runs as soon as the node attaches,\n * which is necessary for portal-mounted containers (Modal, Sheet, BottomSheet)\n * where the node materializes one tick after the parent renders.\n *\n * On activation: focuses the first focusable child, OR the container itself\n * when it has no focusable descendants (container must have `tabIndex={-1}`).\n * On deactivation: restores focus to the previously-focused element.\n *\n * Listens at `document` level so the trap engages even when focus is currently\n * outside the container (e.g., focus leaked to background, or content has no\n * focusables and initial focus didn't land inside).\n */\nexport function useFocusTrap<T extends HTMLElement>(container: T | null, active: boolean): void {\n\tuseEffect(() => {\n\t\tif (!active || !container) return;\n\t\t// Capture as non-null local - TypeScript loses narrowing across closure boundaries.\n\t\tconst c = container;\n\t\tconst previouslyFocused = document.activeElement as HTMLElement | null;\n\n\t\tconst focusables = () =>\n\t\t\tArray.from(c.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTOR)).filter(\n\t\t\t\t(el) => !el.hasAttribute(\"inert\"),\n\t\t\t);\n\n\t\tconst initial = focusables()[0];\n\t\tif (initial) {\n\t\t\tinitial.focus();\n\t\t} else {\n\t\t\tc.focus();\n\t\t}\n\n\t\tfunction handleKeyDown(e: KeyboardEvent) {\n\t\t\tif (e.key !== \"Tab\") return;\n\t\t\tconst list = focusables();\n\t\t\tconst activeEl = document.activeElement as HTMLElement | null;\n\t\t\tconst activeInside = activeEl ? c.contains(activeEl) : false;\n\n\t\t\tif (list.length === 0) {\n\t\t\t\te.preventDefault();\n\t\t\t\tif (!activeInside) c.focus();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst firstEl = list[0]!;\n\t\t\tconst lastEl = list.at(-1)!;\n\n\t\t\tif (!activeInside) {\n\t\t\t\te.preventDefault();\n\t\t\t\t(e.shiftKey ? lastEl : firstEl).focus();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (e.shiftKey && activeEl === firstEl) {\n\t\t\t\te.preventDefault();\n\t\t\t\tlastEl.focus();\n\t\t\t} else if (!e.shiftKey && activeEl === lastEl) {\n\t\t\t\te.preventDefault();\n\t\t\t\tfirstEl.focus();\n\t\t\t}\n\t\t}\n\n\t\tdocument.addEventListener(\"keydown\", handleKeyDown);\n\t\treturn () => {\n\t\t\tdocument.removeEventListener(\"keydown\", handleKeyDown);\n\t\t\tpreviouslyFocused?.focus?.();\n\t\t};\n\t}, [active, container]);\n}\n","import { useEffect, useState } from \"react\";\n\n/**\n * Returns true when the user has set OS-level \"Reduce Motion\" preference.\n * Watches matchMedia for changes (e.g., user toggles preference at runtime).\n * SSR-safe - returns false on the server.\n * Used by Carousel (DS-65) autoplay gating, Accordion expand transition, etc.\n */\nexport function useReducedMotion(): boolean {\n\tconst [reduced, setReduced] = useState<boolean>(() => {\n\t\tif (typeof window === \"undefined\") return false;\n\t\treturn window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches;\n\t});\n\tuseEffect(() => {\n\t\tif (typeof window === \"undefined\") return;\n\t\tconst mql = window.matchMedia(\"(prefers-reduced-motion: reduce)\");\n\t\tconst handler = (e: MediaQueryListEvent) => setReduced(e.matches);\n\t\tmql.addEventListener(\"change\", handler);\n\t\treturn () => mql.removeEventListener(\"change\", handler);\n\t}, []);\n\treturn reduced;\n}\n","import { useEffect, useState } from \"react\";\n\n/**\n * Returns the `matches` boolean for an arbitrary CSS media query, reactive\n * to viewport changes (resize, rotation, prefers-* toggles at runtime).\n * SSR-safe - returns false on the server.\n * Used by Calendar (DS-68) for the mobile breakpoint switch between Popover\n * and BottomSheet at `(max-width: 640px)`. Generic over any media query string.\n */\nexport function useMatchMedia(query: string): boolean {\n\tconst [matches, setMatches] = useState<boolean>(() => {\n\t\tif (typeof window === \"undefined\") return false;\n\t\treturn window.matchMedia(query).matches;\n\t});\n\tuseEffect(() => {\n\t\tif (typeof window === \"undefined\") return;\n\t\tconst mql = window.matchMedia(query);\n\t\tconst handler = (e: MediaQueryListEvent) => setMatches(e.matches);\n\t\t// Sync once after subscription in case query changed between mount and effect.\n\t\tsetMatches(mql.matches);\n\t\tmql.addEventListener(\"change\", handler);\n\t\treturn () => mql.removeEventListener(\"change\", handler);\n\t}, [query]);\n\treturn matches;\n}\n"]}
@@ -0,0 +1,65 @@
1
+ import { AlertTriangle as AlertTriangle$1, Check as Check$1, CheckCircle2 as CheckCircle2$1, ChevronDown as ChevronDown$1, ChevronLeft as ChevronLeft$1, ChevronRight as ChevronRight$1, Clock as Clock$1, Copy as Copy$1, Info as Info$1, Link as Link$1, Minus as Minus$1, Plus as Plus$1, Search as Search$1, Star as Star$1, Trash as Trash$1, Trash2 as Trash2$1, X as X$1, XCircle as XCircle$1, Bold as Bold$1, ChevronUp as ChevronUp$1, Code as Code$1, Heading2 as Heading2$1, Heading3 as Heading3$1, Italic as Italic$1, Link2 as Link2$1, List as List$1, ListOrdered as ListOrdered$1, Moon as Moon$1, MoreHorizontal as MoreHorizontal$1, Sun as Sun$1, Quote as Quote$1, Strikethrough as Strikethrough$1, Underline as Underline$1 } from 'lucide-react';
2
+ import { forwardRef } from 'react';
3
+ import { jsx } from 'react/jsx-runtime';
4
+
5
+ // src/icons/index.ts
6
+ var ICON_DEFAULTS = {
7
+ size: 20,
8
+ strokeWidth: 1.5,
9
+ color: "currentColor"
10
+ };
11
+ var Icon = forwardRef(function Icon2({ icon: IconComponent, children, "aria-label": ariaLabel, ...rest }, ref) {
12
+ const a11y = ariaLabel ? { "aria-label": ariaLabel, role: "img" } : { "aria-hidden": true };
13
+ if (IconComponent) {
14
+ return /* @__PURE__ */ jsx(IconComponent, { ref, ...ICON_DEFAULTS, ...rest, ...a11y });
15
+ }
16
+ return /* @__PURE__ */ jsx("span", { className: "ds-atom-icon", ...a11y, children });
17
+ });
18
+ function wrap(LucideIconComponent) {
19
+ const Wrapped = forwardRef(
20
+ function WrappedIcon(props, ref) {
21
+ return /* @__PURE__ */ jsx(Icon, { ref, icon: LucideIconComponent, ...props });
22
+ }
23
+ );
24
+ Wrapped.displayName = `wrap(${LucideIconComponent.displayName ?? "LucideIcon"})`;
25
+ return Wrapped;
26
+ }
27
+
28
+ // src/icons/index.ts
29
+ var AlertTriangle = wrap(AlertTriangle$1);
30
+ var Check = wrap(Check$1);
31
+ var CheckCircle2 = wrap(CheckCircle2$1);
32
+ var ChevronDown = wrap(ChevronDown$1);
33
+ var ChevronLeft = wrap(ChevronLeft$1);
34
+ var ChevronRight = wrap(ChevronRight$1);
35
+ var Clock = wrap(Clock$1);
36
+ var Copy = wrap(Copy$1);
37
+ var Info = wrap(Info$1);
38
+ var Link = wrap(Link$1);
39
+ var Minus = wrap(Minus$1);
40
+ var Plus = wrap(Plus$1);
41
+ var Search = wrap(Search$1);
42
+ var Star = wrap(Star$1);
43
+ var Trash = wrap(Trash$1);
44
+ var Trash2 = wrap(Trash2$1);
45
+ var X = wrap(X$1);
46
+ var XCircle = wrap(XCircle$1);
47
+ var Bold = wrap(Bold$1);
48
+ var ChevronUp = wrap(ChevronUp$1);
49
+ var Code = wrap(Code$1);
50
+ var Heading2 = wrap(Heading2$1);
51
+ var Heading3 = wrap(Heading3$1);
52
+ var Italic = wrap(Italic$1);
53
+ var Link2 = wrap(Link2$1);
54
+ var List = wrap(List$1);
55
+ var ListOrdered = wrap(ListOrdered$1);
56
+ var Moon = wrap(Moon$1);
57
+ var MoreHorizontal = wrap(MoreHorizontal$1);
58
+ var Sun = wrap(Sun$1);
59
+ var Quote = wrap(Quote$1);
60
+ var Strikethrough = wrap(Strikethrough$1);
61
+ var Underline = wrap(Underline$1);
62
+
63
+ export { AlertTriangle, Bold, Check, CheckCircle2, ChevronDown, ChevronLeft, ChevronRight, ChevronUp, Clock, Code, Copy, Heading2, Heading3, Icon, Info, Italic, Link, Link2, List, ListOrdered, Minus, Moon, MoreHorizontal, Plus, Quote, Search, Star, Strikethrough, Sun, Trash, Trash2, Underline, X, XCircle };
64
+ //# sourceMappingURL=chunk-TG25XACB.js.map
65
+ //# sourceMappingURL=chunk-TG25XACB.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/_internals/Icon.tsx","../src/icons/index.ts"],"names":["Icon","L_AlertTriangle","L_Check","L_CheckCircle2","L_ChevronDown","L_ChevronLeft","L_ChevronRight","L_Clock","L_Copy","L_Info","L_Link","L_Minus","L_Plus","L_Search","L_Star","L_Trash","L_Trash2","L_X","L_XCircle","L_Bold","L_ChevronUp","L_Code","L_Heading2","L_Heading3","L_Italic","L_Link2","L_List","L_ListOrdered","L_Moon","L_MoreHorizontal","L_Sun","L_Quote","L_Strikethrough","L_Underline"],"mappings":";;;;;AAkBA,IAAM,aAAA,GAAgB;AAAA,EACrB,IAAA,EAAM,EAAA;AAAA,EACN,WAAA,EAAa,GAAA;AAAA,EACb,KAAA,EAAO;AACR,CAAA;AAEO,IAAM,IAAA,GAAO,UAAA,CAAqC,SAASA,KAAAA,CACjE,EAAE,IAAA,EAAM,aAAA,EAAe,QAAA,EAAU,YAAA,EAAc,SAAA,EAAW,GAAG,IAAA,IAC7D,GAAA,EACC;AACD,EAAA,MAAM,IAAA,GAAO,SAAA,GACV,EAAE,YAAA,EAAc,SAAA,EAAW,MAAM,KAAA,EAAe,GAChD,EAAE,aAAA,EAAe,IAAA,EAAc;AAClC,EAAA,IAAI,aAAA,EAAe;AAElB,IAAA,uBAAO,GAAA,CAAC,iBAAc,GAAA,EAAW,GAAG,eAAgB,GAAG,IAAA,EAAO,GAAG,IAAA,EAAM,CAAA;AAAA,EACxE;AAEA,EAAA,2BACE,MAAA,EAAA,EAAK,SAAA,EAAU,cAAA,EAAgB,GAAG,MACjC,QAAA,EACF,CAAA;AAEF,CAAC;AAOM,SAAS,KACf,mBAAA,EACoF;AACpF,EAAA,MAAM,OAAA,GAAU,UAAA;AAAA,IACf,SAAS,WAAA,CAAY,KAAA,EAAO,GAAA,EAAK;AAChC,MAAA,2BAAQ,IAAA,EAAA,EAAK,GAAA,EAAU,IAAA,EAAM,mBAAA,EAAsB,GAAG,KAAA,EAAO,CAAA;AAAA,IAC9D;AAAA,GACD;AAEA,EAAA,OAAA,CAAQ,WAAA,GAAc,CAAA,KAAA,EAAQ,mBAAA,CAAoB,WAAA,IAAe,YAAY,CAAA,CAAA,CAAA;AAC7E,EAAA,OAAO,OAAA;AACR;;;ACfO,IAAM,aAAA,GAAgB,KAAKC,eAAe;AAC1C,IAAM,KAAA,GAAQ,KAAKC,OAAO;AAC1B,IAAM,YAAA,GAAe,KAAKC,cAAc;AACxC,IAAM,WAAA,GAAc,KAAKC,aAAa;AACtC,IAAM,WAAA,GAAc,KAAKC,aAAa;AACtC,IAAM,YAAA,GAAe,KAAKC,cAAc;AACxC,IAAM,KAAA,GAAQ,KAAKC,OAAO;AAC1B,IAAM,IAAA,GAAO,KAAKC,MAAM;AACxB,IAAM,IAAA,GAAO,KAAKC,MAAM;AACxB,IAAM,IAAA,GAAO,KAAKC,MAAM;AACxB,IAAM,KAAA,GAAQ,KAAKC,OAAO;AAC1B,IAAM,IAAA,GAAO,KAAKC,MAAM;AACxB,IAAM,MAAA,GAAS,KAAKC,QAAQ;AAC5B,IAAM,IAAA,GAAO,KAAKC,MAAM;AACxB,IAAM,KAAA,GAAQ,KAAKC,OAAO;AAC1B,IAAM,MAAA,GAAS,KAAKC,QAAQ;AAC5B,IAAM,CAAA,GAAI,KAAKC,GAAG;AAClB,IAAM,OAAA,GAAU,KAAKC,SAAS;AAG9B,IAAM,IAAA,GAAO,KAAKC,MAAM;AACxB,IAAM,SAAA,GAAY,KAAKC,WAAW;AAClC,IAAM,IAAA,GAAO,KAAKC,MAAM;AACxB,IAAM,QAAA,GAAW,KAAKC,UAAU;AAChC,IAAM,QAAA,GAAW,KAAKC,UAAU;AAChC,IAAM,MAAA,GAAS,KAAKC,QAAQ;AAC5B,IAAM,KAAA,GAAQ,KAAKC,OAAO;AAC1B,IAAM,IAAA,GAAO,KAAKC,MAAM;AACxB,IAAM,WAAA,GAAc,KAAKC,aAAa;AACtC,IAAM,IAAA,GAAO,KAAKC,MAAM;AACxB,IAAM,cAAA,GAAiB,KAAKC,gBAAgB;AAC5C,IAAM,GAAA,GAAM,KAAKC,KAAK;AACtB,IAAM,KAAA,GAAQ,KAAKC,OAAO;AAC1B,IAAM,aAAA,GAAgB,KAAKC,eAAe;AAC1C,IAAM,SAAA,GAAY,KAAKC,WAAW","file":"chunk-TG25XACB.js","sourcesContent":["import type { LucideIcon, LucideProps } from \"lucide-react\";\n/**\n * Brand-lock icon wrapper (DS-60, D-17-02). Owns size/strokeWidth/color\n * defaults + aria toggle. Exported publicly via main barrel as Icon;\n * pre-wrapped icons live in src/icons/index.ts via wrap().\n */\nimport {\n\ttype ForwardRefExoticComponent,\n\ttype ReactNode,\n\ttype RefAttributes,\n\tforwardRef,\n} from \"react\";\n\nexport interface IconProps extends Omit<LucideProps, \"ref\"> {\n\ticon?: LucideIcon;\n\tchildren?: ReactNode;\n}\n\nconst ICON_DEFAULTS = {\n\tsize: 20,\n\tstrokeWidth: 1.5,\n\tcolor: \"currentColor\",\n} as const;\n\nexport const Icon = forwardRef<SVGSVGElement, IconProps>(function Icon(\n\t{ icon: IconComponent, children, \"aria-label\": ariaLabel, ...rest },\n\tref,\n) {\n\tconst a11y = ariaLabel\n\t\t? { \"aria-label\": ariaLabel, role: \"img\" as const }\n\t\t: { \"aria-hidden\": true as const };\n\tif (IconComponent) {\n\t\t// Defaults BEFORE rest spread so consumer overrides win.\n\t\treturn <IconComponent ref={ref} {...ICON_DEFAULTS} {...rest} {...a11y} />;\n\t}\n\t// Children escape hatch - custom SVG.\n\treturn (\n\t\t<span className=\"ds-atom-icon\" {...a11y}>\n\t\t\t{children}\n\t\t</span>\n\t);\n});\n\n/**\n * Pre-bind a lucide icon with brand-lock defaults. Used by src/icons/index.ts.\n * Returns a component callable as <ChevronDown size={14} /> with all the Icon\n * defaults already applied - so callers don't need to pass `icon={LucideChevronDown}`.\n */\nexport function wrap(\n\tLucideIconComponent: LucideIcon,\n): ForwardRefExoticComponent<Omit<IconProps, \"icon\"> & RefAttributes<SVGSVGElement>> {\n\tconst Wrapped = forwardRef<SVGSVGElement, Omit<IconProps, \"icon\">>(\n\t\tfunction WrappedIcon(props, ref) {\n\t\t\treturn <Icon ref={ref} icon={LucideIconComponent} {...props} />;\n\t\t},\n\t);\n\t// Preserve the lucide icon's display name for devtools.\n\tWrapped.displayName = `wrap(${LucideIconComponent.displayName ?? \"LucideIcon\"})`;\n\treturn Wrapped;\n}\n","// @akhil-saxena/design-system/icons - wrapped lucide-react icons (DS-60).\n// Consumed via:\n// import { ChevronDown } from \"@akhil-saxena/design-system/icons\";\n// Each icon is pre-wrapped with brand-lock defaults (size 20, stroke 1.5,\n// currentColor, aria-hidden) from _internals/Icon.tsx via the wrap() helper.\n\nimport {\n\tAlertTriangle as L_AlertTriangle,\n\tBold as L_Bold,\n\tCheck as L_Check,\n\tCheckCircle2 as L_CheckCircle2,\n\tChevronDown as L_ChevronDown,\n\tChevronLeft as L_ChevronLeft,\n\tChevronRight as L_ChevronRight,\n\tChevronUp as L_ChevronUp,\n\tClock as L_Clock,\n\tCode as L_Code,\n\tCopy as L_Copy,\n\tHeading2 as L_Heading2,\n\tHeading3 as L_Heading3,\n\tInfo as L_Info,\n\tItalic as L_Italic,\n\tLink as L_Link,\n\tLink2 as L_Link2,\n\tList as L_List,\n\tListOrdered as L_ListOrdered,\n\tMinus as L_Minus,\n\tMoon as L_Moon,\n\tMoreHorizontal as L_MoreHorizontal,\n\tPlus as L_Plus,\n\tQuote as L_Quote,\n\tSearch as L_Search,\n\tStar as L_Star,\n\tStrikethrough as L_Strikethrough,\n\tSun as L_Sun,\n\tTrash as L_Trash,\n\tTrash2 as L_Trash2,\n\tUnderline as L_Underline,\n\tX as L_X,\n\tXCircle as L_XCircle,\n} from \"lucide-react\";\nimport { wrap } from \"../_internals/Icon\";\n\n// Currently-used across the 13 primitives (DS-60 sweep)\nexport const AlertTriangle = wrap(L_AlertTriangle);\nexport const Check = wrap(L_Check);\nexport const CheckCircle2 = wrap(L_CheckCircle2);\nexport const ChevronDown = wrap(L_ChevronDown);\nexport const ChevronLeft = wrap(L_ChevronLeft);\nexport const ChevronRight = wrap(L_ChevronRight);\nexport const Clock = wrap(L_Clock);\nexport const Copy = wrap(L_Copy);\nexport const Info = wrap(L_Info);\nexport const Link = wrap(L_Link);\nexport const Minus = wrap(L_Minus);\nexport const Plus = wrap(L_Plus);\nexport const Search = wrap(L_Search);\nexport const Star = wrap(L_Star);\nexport const Trash = wrap(L_Trash);\nexport const Trash2 = wrap(L_Trash2);\nexport const X = wrap(L_X);\nexport const XCircle = wrap(L_XCircle);\n\n// Future-required by Phase 17 primitives (Plans 04, 08, 10, 11, 13)\nexport const Bold = wrap(L_Bold);\nexport const ChevronUp = wrap(L_ChevronUp);\nexport const Code = wrap(L_Code);\nexport const Heading2 = wrap(L_Heading2);\nexport const Heading3 = wrap(L_Heading3);\nexport const Italic = wrap(L_Italic);\nexport const Link2 = wrap(L_Link2);\nexport const List = wrap(L_List);\nexport const ListOrdered = wrap(L_ListOrdered);\nexport const Moon = wrap(L_Moon);\nexport const MoreHorizontal = wrap(L_MoreHorizontal);\nexport const Sun = wrap(L_Sun);\nexport const Quote = wrap(L_Quote);\nexport const Strikethrough = wrap(L_Strikethrough);\nexport const Underline = wrap(L_Underline);\n"]}
@@ -0,0 +1,145 @@
1
+ import react__default, { RefObject, Ref } from 'react';
2
+
3
+ /**
4
+ * Calls `handler` when a mousedown/touchstart fires outside of `ref`'s element.
5
+ * Used by Popover (Phase 14), Modal close-on-backdrop, Select dropdown dismiss.
6
+ */
7
+ declare function useClickOutside<T extends HTMLElement>(ref: RefObject<T | null>, handler: (e: MouseEvent | TouchEvent) => void, enabled?: boolean): void;
8
+
9
+ /**
10
+ * Combine multiple refs (function or object) into one callback ref.
11
+ * Used by every primitive that does forwardRef + needs an internal ref
12
+ * for a hook (Popover trigger, Modal container, etc.).
13
+ */
14
+ declare function useComposedRefs<T>(...refs: Array<Ref<T> | undefined>): (node: T | null) => void;
15
+
16
+ /**
17
+ * Trap Tab/Shift+Tab focus inside `container` while `active` is true.
18
+ *
19
+ * Pass the actual DOM node (use a callback ref + useState pattern in the
20
+ * caller). This guarantees the effect re-runs as soon as the node attaches,
21
+ * which is necessary for portal-mounted containers (Modal, Sheet, BottomSheet)
22
+ * where the node materializes one tick after the parent renders.
23
+ *
24
+ * On activation: focuses the first focusable child, OR the container itself
25
+ * when it has no focusable descendants (container must have `tabIndex={-1}`).
26
+ * On deactivation: restores focus to the previously-focused element.
27
+ *
28
+ * Listens at `document` level so the trap engages even when focus is currently
29
+ * outside the container (e.g., focus leaked to background, or content has no
30
+ * focusables and initial focus didn't land inside).
31
+ */
32
+ declare function useFocusTrap<T extends HTMLElement>(container: T | null, active: boolean): void;
33
+
34
+ interface ShortcutOptions {
35
+ enabled?: boolean;
36
+ preventDefault?: boolean;
37
+ target?: HTMLElement | Window;
38
+ }
39
+ /**
40
+ * Bind a keyboard shortcut. Use "mod+k" for Cmd-on-Mac, Ctrl-on-Win/Linux.
41
+ * Used by CommandPalette (Phase 17 - ⌘K), Modal Esc-to-close (Phase 14).
42
+ */
43
+ declare function useKeyboardShortcut(combo: string | string[], handler: (e: KeyboardEvent) => void, options?: ShortcutOptions): void;
44
+
45
+ /**
46
+ * Returns the `matches` boolean for an arbitrary CSS media query, reactive
47
+ * to viewport changes (resize, rotation, prefers-* toggles at runtime).
48
+ * SSR-safe - returns false on the server.
49
+ * Used by Calendar (DS-68) for the mobile breakpoint switch between Popover
50
+ * and BottomSheet at `(max-width: 640px)`. Generic over any media query string.
51
+ */
52
+ declare function useMatchMedia(query: string): boolean;
53
+
54
+ /**
55
+ * Returns true when the user has set OS-level "Reduce Motion" preference.
56
+ * Watches matchMedia for changes (e.g., user toggles preference at runtime).
57
+ * SSR-safe - returns false on the server.
58
+ * Used by Carousel (DS-65) autoplay gating, Accordion expand transition, etc.
59
+ */
60
+ declare function useReducedMotion(): boolean;
61
+
62
+ interface SortState<T> {
63
+ col: keyof T | null;
64
+ dir: "asc" | "desc";
65
+ }
66
+ /**
67
+ * Sortable table state + derived sorted rows.
68
+ * Used by Table primitive (DS-61, D-17-07) header click handlers.
69
+ * Stable sort via Array.prototype.sort (ES2019 guarantee).
70
+ *
71
+ * Usage:
72
+ * const { sorted, sortCol, sortDir, toggleSort } = useSortableTable(rows, { defaultCol: "name" });
73
+ * <Table.HeaderCell sortable sortDir={sortCol === "name" ? sortDir : null} onToggleSort={() => toggleSort("name")}>
74
+ * Name
75
+ * </Table.HeaderCell>
76
+ *
77
+ * @param rows - The source data array (reference-stable recommended for memo perf).
78
+ * @param options.defaultCol - Initial sort column (default: null = unsorted).
79
+ * @param options.defaultDir - Initial sort direction (default: "asc").
80
+ * @param options.comparator - Custom compare fn `(a, b, col) => number`.
81
+ */
82
+ declare function useSortableTable<T>(rows: T[], options?: {
83
+ defaultCol?: keyof T;
84
+ defaultDir?: "asc" | "desc";
85
+ comparator?: (a: T, b: T, col: keyof T) => number;
86
+ }): {
87
+ sorted: T[];
88
+ sortCol: keyof T | null;
89
+ sortDir: "asc" | "desc";
90
+ toggleSort: (col: keyof T) => void;
91
+ };
92
+
93
+ type SelectionMode = "single" | "multi";
94
+ /**
95
+ * Selection state for Table.SelectAllCell + Table.SelectCell (DS-61, D-17-09).
96
+ * Modes: "single" (at most 1 selected) | "multi" (default).
97
+ * Controlled via selectedIds + onSelectionChange; uncontrolled via defaultSelected.
98
+ *
99
+ * @example
100
+ * const { selectedIds, isAllSelected, isIndeterminate, toggle, toggleAll, clear } =
101
+ * useTableSelection(rowIds, { mode: "multi", defaultSelected: [] });
102
+ */
103
+ declare function useTableSelection<Id extends string | number>(ids: Id[], options?: {
104
+ mode?: SelectionMode;
105
+ defaultSelected?: Id[];
106
+ /** Controlled selected ids - takes priority over internal state. */
107
+ selectedIds?: Id[];
108
+ onSelectionChange?: (ids: Id[]) => void;
109
+ }): {
110
+ selectedIds: Id[];
111
+ isAllSelected: boolean;
112
+ isIndeterminate: boolean;
113
+ isSelected: (id: Id) => boolean;
114
+ toggle: (id: Id) => void;
115
+ toggleAll: () => void;
116
+ clear: () => void;
117
+ };
118
+
119
+ /**
120
+ * Column-resize state + Pointer Events drag handle (DS-61, D-17-10).
121
+ *
122
+ * Attach `startResize(col, event)` to a drag handle element's onPointerDown.
123
+ * Pointer is captured via setPointerCapture for reliable tracking on fast drags.
124
+ * onWidthsChange fires on pointerup with the final widths snapshot.
125
+ * Consumer owns persistence (e.g. localStorage) - this hook only emits the event.
126
+ *
127
+ * @example
128
+ * const { widths, startResize } = useResizableColumns({ name: 120, role: 100 });
129
+ * <Table.HeaderCell width={widths.name}>
130
+ * Name
131
+ * <span onPointerDown={(e) => startResize("name", e)} />
132
+ * </Table.HeaderCell>
133
+ */
134
+ declare function useResizableColumns(initialWidths: Record<string, number>, options?: {
135
+ /** Minimum column width in px. Default: 60. */
136
+ minWidth?: number;
137
+ /** Called on pointerup with the final widths record. */
138
+ onWidthsChange?: (widths: Record<string, number>) => void;
139
+ }): {
140
+ widths: Record<string, number>;
141
+ setWidth: (col: string, w: number) => void;
142
+ startResize: (col: string, e: react__default.PointerEvent) => void;
143
+ };
144
+
145
+ export { type SelectionMode, type SortState, useClickOutside, useComposedRefs, useFocusTrap, useKeyboardShortcut, useMatchMedia, useReducedMotion, useResizableColumns, useSortableTable, useTableSelection };