@craftzbay/ui 0.9.0 → 0.10.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 +72 -6
- package/dist-lib/components/ui/Accordion.js +60 -0
- package/dist-lib/components/ui/Accordion.js.map +1 -0
- package/dist-lib/components/ui/Alert.d.ts +1 -1
- package/dist-lib/components/ui/Alert.js +66 -0
- package/dist-lib/components/ui/Alert.js.map +1 -0
- package/dist-lib/components/ui/Avatar.d.ts +2 -2
- package/dist-lib/components/ui/Avatar.js +96 -0
- package/dist-lib/components/ui/Avatar.js.map +1 -0
- package/dist-lib/components/ui/Badge.d.ts +1 -1
- package/dist-lib/components/ui/Badge.js +67 -0
- package/dist-lib/components/ui/Badge.js.map +1 -0
- package/dist-lib/components/ui/Breadcrumbs.js +34 -0
- package/dist-lib/components/ui/Breadcrumbs.js.map +1 -0
- package/dist-lib/components/ui/Button.d.ts +1 -1
- package/dist-lib/components/ui/Button.js +117 -0
- package/dist-lib/components/ui/Button.js.map +1 -0
- package/dist-lib/components/ui/Calendar.d.ts +7 -1
- package/dist-lib/components/ui/Calendar.js +111 -0
- package/dist-lib/components/ui/Calendar.js.map +1 -0
- package/dist-lib/components/ui/Card.d.ts +1 -1
- package/dist-lib/components/ui/Card.js +116 -0
- package/dist-lib/components/ui/Card.js.map +1 -0
- package/dist-lib/components/ui/Carousel.d.ts +5 -1
- package/dist-lib/components/ui/Carousel.js +143 -0
- package/dist-lib/components/ui/Carousel.js.map +1 -0
- package/dist-lib/components/ui/Chart.d.ts +26 -4
- package/dist-lib/components/ui/Chart.js +253 -0
- package/dist-lib/components/ui/Chart.js.map +1 -0
- package/dist-lib/components/ui/Checkbox.js +56 -0
- package/dist-lib/components/ui/Checkbox.js.map +1 -0
- package/dist-lib/components/ui/Combobox.d.ts +7 -0
- package/dist-lib/components/ui/Combobox.js +164 -0
- package/dist-lib/components/ui/Combobox.js.map +1 -0
- package/dist-lib/components/ui/CommandPalette.d.ts +3 -3
- package/dist-lib/components/ui/CommandPalette.js +174 -0
- package/dist-lib/components/ui/CommandPalette.js.map +1 -0
- package/dist-lib/components/ui/ContextMenu.js +150 -0
- package/dist-lib/components/ui/ContextMenu.js.map +1 -0
- package/dist-lib/components/ui/DataGrid.js +84 -0
- package/dist-lib/components/ui/DataGrid.js.map +1 -0
- package/dist-lib/components/ui/DatePicker.d.ts +16 -13
- package/dist-lib/components/ui/DatePicker.js +174 -0
- package/dist-lib/components/ui/DatePicker.js.map +1 -0
- package/dist-lib/components/ui/DesignSystemProvider.d.ts +8 -1
- package/dist-lib/components/ui/DesignSystemProvider.js +79 -0
- package/dist-lib/components/ui/DesignSystemProvider.js.map +1 -0
- package/dist-lib/components/ui/Dialog.d.ts +1 -1
- package/dist-lib/components/ui/Dialog.js +140 -0
- package/dist-lib/components/ui/Dialog.js.map +1 -0
- package/dist-lib/components/ui/Drawer.d.ts +6 -0
- package/dist-lib/components/ui/Drawer.js +98 -0
- package/dist-lib/components/ui/Drawer.js.map +1 -0
- package/dist-lib/components/ui/DropdownMenu.js +164 -0
- package/dist-lib/components/ui/DropdownMenu.js.map +1 -0
- package/dist-lib/components/ui/EmptyState.js +32 -0
- package/dist-lib/components/ui/EmptyState.js.map +1 -0
- package/dist-lib/components/ui/ErrorState.js +48 -0
- package/dist-lib/components/ui/ErrorState.js.map +1 -0
- package/dist-lib/components/ui/FileUpload.d.ts +3 -0
- package/dist-lib/components/ui/FileUpload.js +108 -0
- package/dist-lib/components/ui/FileUpload.js.map +1 -0
- package/dist-lib/components/ui/Form.d.ts +1 -0
- package/dist-lib/components/ui/Form.js +105 -0
- package/dist-lib/components/ui/Form.js.map +1 -0
- package/dist-lib/components/ui/Icon.js +26 -0
- package/dist-lib/components/ui/Icon.js.map +1 -0
- package/dist-lib/components/ui/IconButton.d.ts +1 -1
- package/dist-lib/components/ui/IconButton.js +56 -0
- package/dist-lib/components/ui/IconButton.js.map +1 -0
- package/dist-lib/components/ui/Input.d.ts +6 -3
- package/dist-lib/components/ui/Input.js +135 -0
- package/dist-lib/components/ui/Input.js.map +1 -0
- package/dist-lib/components/ui/Kbd.js +24 -0
- package/dist-lib/components/ui/Kbd.js.map +1 -0
- package/dist-lib/components/ui/MultiSelect.d.ts +3 -1
- package/dist-lib/components/ui/MultiSelect.js +196 -0
- package/dist-lib/components/ui/MultiSelect.js.map +1 -0
- package/dist-lib/components/ui/Pagination.d.ts +23 -0
- package/dist-lib/components/ui/Pagination.js +148 -0
- package/dist-lib/components/ui/Pagination.js.map +1 -0
- package/dist-lib/components/ui/Popover.js +35 -0
- package/dist-lib/components/ui/Popover.js.map +1 -0
- package/dist-lib/components/ui/Progress.js +95 -0
- package/dist-lib/components/ui/Progress.js.map +1 -0
- package/dist-lib/components/ui/RadioGroup.js +64 -0
- package/dist-lib/components/ui/RadioGroup.js.map +1 -0
- package/dist-lib/components/ui/ScrollArea.js +44 -0
- package/dist-lib/components/ui/ScrollArea.js.map +1 -0
- package/dist-lib/components/ui/Select.js +131 -0
- package/dist-lib/components/ui/Select.js.map +1 -0
- package/dist-lib/components/ui/Separator.js +26 -0
- package/dist-lib/components/ui/Separator.js.map +1 -0
- package/dist-lib/components/ui/Sheet.d.ts +1 -1
- package/dist-lib/components/ui/Sheet.js +113 -0
- package/dist-lib/components/ui/Sheet.js.map +1 -0
- package/dist-lib/components/ui/Sidebar.d.ts +18 -6
- package/dist-lib/components/ui/Sidebar.js +169 -0
- package/dist-lib/components/ui/Sidebar.js.map +1 -0
- package/dist-lib/components/ui/Skeleton.js +28 -0
- package/dist-lib/components/ui/Skeleton.js.map +1 -0
- package/dist-lib/components/ui/Slider.js +52 -0
- package/dist-lib/components/ui/Slider.js.map +1 -0
- package/dist-lib/components/ui/Snackbar.d.ts +1 -1
- package/dist-lib/components/ui/Snackbar.js +56 -0
- package/dist-lib/components/ui/Snackbar.js.map +1 -0
- package/dist-lib/components/ui/Spinner.js +42 -0
- package/dist-lib/components/ui/Spinner.js.map +1 -0
- package/dist-lib/components/ui/Stepper.js +88 -0
- package/dist-lib/components/ui/Stepper.js.map +1 -0
- package/dist-lib/components/ui/Switch.js +77 -0
- package/dist-lib/components/ui/Switch.js.map +1 -0
- package/dist-lib/components/ui/Table.d.ts +9 -1
- package/dist-lib/components/ui/Table.js +157 -0
- package/dist-lib/components/ui/Table.js.map +1 -0
- package/dist-lib/components/ui/Tabs.d.ts +1 -1
- package/dist-lib/components/ui/Tabs.js +89 -0
- package/dist-lib/components/ui/Tabs.js.map +1 -0
- package/dist-lib/components/ui/TagInput.d.ts +17 -2
- package/dist-lib/components/ui/TagInput.js +111 -0
- package/dist-lib/components/ui/TagInput.js.map +1 -0
- package/dist-lib/components/ui/Textarea.js +77 -0
- package/dist-lib/components/ui/Textarea.js.map +1 -0
- package/dist-lib/components/ui/Timeline.d.ts +16 -6
- package/dist-lib/components/ui/Timeline.js +52 -0
- package/dist-lib/components/ui/Timeline.js.map +1 -0
- package/dist-lib/components/ui/Toast.d.ts +1 -1
- package/dist-lib/components/ui/Toast.js +171 -0
- package/dist-lib/components/ui/Toast.js.map +1 -0
- package/dist-lib/components/ui/Tooltip.js +58 -0
- package/dist-lib/components/ui/Tooltip.js.map +1 -0
- package/dist-lib/components/ui/TopNav.js +52 -0
- package/dist-lib/components/ui/TopNav.js.map +1 -0
- package/dist-lib/components/ui/Tree.d.ts +27 -5
- package/dist-lib/components/ui/Tree.js +167 -0
- package/dist-lib/components/ui/Tree.js.map +1 -0
- package/dist-lib/hooks/use-field-ids.d.ts +29 -0
- package/dist-lib/hooks/use-field-ids.js +18 -0
- package/dist-lib/hooks/use-field-ids.js.map +1 -0
- package/dist-lib/hooks/use-media-query.js +18 -0
- package/dist-lib/hooks/use-media-query.js.map +1 -0
- package/dist-lib/hooks/use-strings.d.ts +5 -0
- package/dist-lib/hooks/use-strings.js +12 -0
- package/dist-lib/hooks/use-strings.js.map +1 -0
- package/dist-lib/hooks/use-toast.js +36 -0
- package/dist-lib/hooks/use-toast.js.map +1 -0
- package/dist-lib/icon.d.ts +8 -0
- package/dist-lib/icon.js +7 -0
- package/dist-lib/icon.js.map +1 -0
- package/dist-lib/icons/index.d.ts +71 -88
- package/dist-lib/icons/index.js +145 -0
- package/dist-lib/icons/index.js.map +1 -0
- package/dist-lib/illustrations/index.js +105 -0
- package/dist-lib/illustrations/index.js.map +1 -0
- package/dist-lib/index.d.ts +62 -59
- package/dist-lib/index.js +267 -4529
- package/dist-lib/index.js.map +1 -1
- package/dist-lib/lib/strings.d.ts +129 -0
- package/dist-lib/lib/strings.js +85 -0
- package/dist-lib/lib/strings.js.map +1 -0
- package/dist-lib/lib/strings.mn.d.ts +3 -0
- package/dist-lib/lib/strings.mn.js +70 -0
- package/dist-lib/lib/strings.mn.js.map +1 -0
- package/dist-lib/lib/utils.d.ts +0 -1
- package/dist-lib/lib/utils.js +10 -0
- package/dist-lib/lib/utils.js.map +1 -0
- package/dist-lib/styles.css +1 -1
- package/dist-lib/theme.css +777 -0
- package/package.json +31 -8
- package/dist-lib/index.cjs +0 -8
- package/dist-lib/index.cjs.map +0 -1
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsxs as a, jsx as o } from "react/jsx-runtime";
|
|
3
|
+
import { forwardRef as d } from "react";
|
|
4
|
+
import { cn as l } from "../../lib/utils.js";
|
|
5
|
+
const f = d(function({ logo: n, nav: e, search: r, actions: i, className: t, ...s }, m) {
|
|
6
|
+
return /* @__PURE__ */ a(
|
|
7
|
+
"header",
|
|
8
|
+
{
|
|
9
|
+
ref: m,
|
|
10
|
+
className: l(
|
|
11
|
+
"sticky top-0 z-[var(--z-sticky)] flex h-14 w-full items-center gap-4",
|
|
12
|
+
"border-b border-border bg-background px-4",
|
|
13
|
+
t
|
|
14
|
+
),
|
|
15
|
+
...s,
|
|
16
|
+
children: [
|
|
17
|
+
/* @__PURE__ */ a("div", { className: "flex items-center gap-6 shrink-0", children: [
|
|
18
|
+
n,
|
|
19
|
+
e && /* @__PURE__ */ o("nav", { className: "hidden md:flex items-center gap-1", children: e })
|
|
20
|
+
] }),
|
|
21
|
+
r && /* @__PURE__ */ o("div", { className: "flex-1 max-w-md mx-auto", children: r }),
|
|
22
|
+
i && /* @__PURE__ */ o("div", { className: "ml-auto flex items-center gap-2 shrink-0", children: i })
|
|
23
|
+
]
|
|
24
|
+
}
|
|
25
|
+
);
|
|
26
|
+
});
|
|
27
|
+
f.displayName = "TopNav";
|
|
28
|
+
const u = d(function({ href: n, active: e, className: r, children: i, ...t }, s) {
|
|
29
|
+
return /* @__PURE__ */ o(
|
|
30
|
+
"a",
|
|
31
|
+
{
|
|
32
|
+
ref: s,
|
|
33
|
+
href: n,
|
|
34
|
+
"aria-current": e ? "page" : void 0,
|
|
35
|
+
className: l(
|
|
36
|
+
"inline-flex h-9 items-center rounded-md px-3 text-sm font-medium outline-none",
|
|
37
|
+
"transition-colors duration-[var(--duration-fast)]",
|
|
38
|
+
e ? "text-foreground" : "text-foreground-muted hover:text-foreground",
|
|
39
|
+
"focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background",
|
|
40
|
+
r
|
|
41
|
+
),
|
|
42
|
+
...t,
|
|
43
|
+
children: i
|
|
44
|
+
}
|
|
45
|
+
);
|
|
46
|
+
});
|
|
47
|
+
u.displayName = "TopNavLink";
|
|
48
|
+
export {
|
|
49
|
+
f as TopNav,
|
|
50
|
+
u as TopNavLink
|
|
51
|
+
};
|
|
52
|
+
//# sourceMappingURL=TopNav.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TopNav.js","sources":["../../../src/components/ui/TopNav.tsx"],"sourcesContent":["'use client';\n\nimport { forwardRef, type HTMLAttributes, type ReactNode } from 'react';\nimport { cn } from '@/lib/utils';\n\nexport interface TopNavProps extends HTMLAttributes<HTMLElement> {\n /** Logo / wordmark slot. Shown on the far left. */\n logo: ReactNode;\n /** Primary navigation links — typically a list of `<TopNavLink>`. */\n nav?: ReactNode;\n /** Search / command bar slot — fills the centre on wide screens. */\n search?: ReactNode;\n /** Right-aligned cluster — notifications, theme toggle, user menu. */\n actions?: ReactNode;\n}\n\n/**\n * App-level top bar. Layout: `logo · nav · search · actions`. Search and nav\n * are optional.\n *\n * @example\n * <TopNav\n * logo={<Logo />}\n * nav={<><TopNavLink href=\"/\" active>Home</TopNavLink>…</>}\n * search={<Input type=\"search\" placeholder=\"Search…\" />}\n * actions={<><Bell /><Avatar /></>}\n * />\n *\n * @do Pick one primary nav style — links here or in the Sidebar, not both.\n * @dont Stack two rows of navigation in the TopNav. If you need tabs as well,\n * put them below the bar inside the page content.\n */\nexport const TopNav = forwardRef<HTMLElement, TopNavProps>(function TopNav(\n { logo, nav, search, actions, className, ...props },\n ref,\n) {\n return (\n <header\n ref={ref}\n className={cn(\n 'sticky top-0 z-[var(--z-sticky)] flex h-14 w-full items-center gap-4',\n 'border-b border-border bg-background px-4',\n className,\n )}\n {...props}\n >\n <div className=\"flex items-center gap-6 shrink-0\">\n {logo}\n {nav && <nav className=\"hidden md:flex items-center gap-1\">{nav}</nav>}\n </div>\n {search && <div className=\"flex-1 max-w-md mx-auto\">{search}</div>}\n {actions && <div className=\"ml-auto flex items-center gap-2 shrink-0\">{actions}</div>}\n </header>\n );\n});\nTopNav.displayName = 'TopNav';\n\nexport interface TopNavLinkProps extends HTMLAttributes<HTMLAnchorElement> {\n href: string;\n active?: boolean;\n}\n\nexport const TopNavLink = forwardRef<HTMLAnchorElement, TopNavLinkProps>(function TopNavLink(\n { href, active, className, children, ...props },\n ref,\n) {\n return (\n <a\n ref={ref}\n href={href}\n aria-current={active ? 'page' : undefined}\n className={cn(\n 'inline-flex h-9 items-center rounded-md px-3 text-sm font-medium outline-none',\n 'transition-colors duration-[var(--duration-fast)]',\n active ? 'text-foreground' : 'text-foreground-muted hover:text-foreground',\n 'focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background',\n className,\n )}\n {...props}\n >\n {children}\n </a>\n );\n});\nTopNavLink.displayName = 'TopNavLink';\n"],"names":["TopNav","forwardRef","logo","nav","search","actions","className","props","ref","jsxs","cn","jsx","TopNavLink","href","active","children"],"mappings":";;;;AAgCO,MAAMA,IAASC,EAAqC,SACzD,EAAE,MAAAC,GAAM,KAAAC,GAAK,QAAAC,GAAQ,SAAAC,GAAS,WAAAC,GAAW,GAAGC,EAAA,GAC5CC,GACA;AACA,SACE,gBAAAC;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,KAAAD;AAAA,MACA,WAAWE;AAAA,QACT;AAAA,QACA;AAAA,QACAJ;AAAA,MAAA;AAAA,MAED,GAAGC;AAAA,MAEJ,UAAA;AAAA,QAAA,gBAAAE,EAAC,OAAA,EAAI,WAAU,oCACZ,UAAA;AAAA,UAAAP;AAAA,UACAC,KAAO,gBAAAQ,EAAC,OAAA,EAAI,WAAU,qCAAqC,UAAAR,EAAA,CAAI;AAAA,QAAA,GAClE;AAAA,QACCC,KAAU,gBAAAO,EAAC,OAAA,EAAI,WAAU,2BAA2B,UAAAP,GAAO;AAAA,QAC3DC,KAAW,gBAAAM,EAAC,OAAA,EAAI,WAAU,4CAA4C,UAAAN,EAAA,CAAQ;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA;AAGrF,CAAC;AACDL,EAAO,cAAc;AAOd,MAAMY,IAAaX,EAA+C,SACvE,EAAE,MAAAY,GAAM,QAAAC,GAAQ,WAAAR,GAAW,UAAAS,GAAU,GAAGR,EAAA,GACxCC,GACA;AACA,SACE,gBAAAG;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,KAAAH;AAAA,MACA,MAAAK;AAAA,MACA,gBAAcC,IAAS,SAAS;AAAA,MAChC,WAAWJ;AAAA,QACT;AAAA,QACA;AAAA,QACAI,IAAS,oBAAoB;AAAA,QAC7B;AAAA,QACAR;AAAA,MAAA;AAAA,MAED,GAAGC;AAAA,MAEH,UAAAQ;AAAA,IAAA;AAAA,EAAA;AAGP,CAAC;AACDH,EAAW,cAAc;"}
|
|
@@ -10,15 +10,37 @@ export interface TreeNode {
|
|
|
10
10
|
export interface TreeProps {
|
|
11
11
|
/** The root nodes. */
|
|
12
12
|
data: TreeNode[];
|
|
13
|
-
/** Ids to expand by default. */
|
|
13
|
+
/** Ids to expand by default (uncontrolled). */
|
|
14
14
|
defaultExpanded?: string[];
|
|
15
|
-
/**
|
|
15
|
+
/** Controlled set of expanded ids. */
|
|
16
|
+
expanded?: string[];
|
|
17
|
+
/** Called with the full next set of expanded ids. */
|
|
18
|
+
onExpandedChange?: (next: string[]) => void;
|
|
19
|
+
/** Currently selected node id (controlled). Alias: `selected`. */
|
|
16
20
|
selectedId?: string;
|
|
21
|
+
/** Controlled selected id. Takes precedence over `selectedId`. */
|
|
22
|
+
selected?: string;
|
|
23
|
+
/** Default selected id (uncontrolled). */
|
|
24
|
+
defaultSelected?: string;
|
|
25
|
+
/** Called when a node is activated (click / Enter / Space). */
|
|
17
26
|
onSelect?: (id: string) => void;
|
|
27
|
+
/** Alias of `onSelect` for the controlled `selected` pair. */
|
|
28
|
+
onSelectedChange?: (id: string) => void;
|
|
29
|
+
/** Accessible name for the tree. */
|
|
30
|
+
'aria-label'?: string;
|
|
31
|
+
'aria-labelledby'?: string;
|
|
18
32
|
className?: string;
|
|
19
33
|
}
|
|
20
34
|
/**
|
|
21
|
-
* Expandable file-tree style list
|
|
22
|
-
*
|
|
35
|
+
* Expandable file-tree style list following the WAI-ARIA tree pattern.
|
|
36
|
+
* Single selection, roving tabindex (one tab stop), and full arrow-key
|
|
37
|
+
* navigation: Up/Down move, Right expands or moves into a folder, Left
|
|
38
|
+
* collapses or moves to the parent, Home/End jump, Enter/Space select.
|
|
39
|
+
*
|
|
40
|
+
* Expansion and selection are uncontrolled by default; pass
|
|
41
|
+
* `expanded`/`onExpandedChange` and `selected`/`onSelectedChange` to control.
|
|
23
42
|
*/
|
|
24
|
-
export declare function Tree({ data, defaultExpanded, selectedId, onSelect, className }: TreeProps): import("react/jsx-runtime").JSX.Element;
|
|
43
|
+
export declare function Tree({ data, defaultExpanded, expanded: expandedProp, onExpandedChange, selectedId, selected: selectedProp, defaultSelected, onSelect, onSelectedChange, className, 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledby, }: TreeProps): import("react/jsx-runtime").JSX.Element;
|
|
44
|
+
export declare namespace Tree {
|
|
45
|
+
var displayName: string;
|
|
46
|
+
}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as f, jsxs as L } from "react/jsx-runtime";
|
|
3
|
+
import { useState as A, useMemo as S, useRef as Z, useCallback as O } from "react";
|
|
4
|
+
import { ChevronRight as _, FolderOpen as $, Folder as P, File as C } from "../../icons/index.js";
|
|
5
|
+
import { cn as R } from "../../lib/utils.js";
|
|
6
|
+
import { useStrings as ee } from "../../hooks/use-strings.js";
|
|
7
|
+
function te({
|
|
8
|
+
data: p,
|
|
9
|
+
defaultExpanded: U = [],
|
|
10
|
+
expanded: d,
|
|
11
|
+
onExpandedChange: b,
|
|
12
|
+
selectedId: D,
|
|
13
|
+
selected: I,
|
|
14
|
+
defaultSelected: q,
|
|
15
|
+
onSelect: h,
|
|
16
|
+
onSelectedChange: g,
|
|
17
|
+
className: B,
|
|
18
|
+
"aria-label": G,
|
|
19
|
+
"aria-labelledby": T
|
|
20
|
+
}) {
|
|
21
|
+
var H;
|
|
22
|
+
const J = ee(), [j, Q] = A(
|
|
23
|
+
() => new Set(U)
|
|
24
|
+
), o = S(
|
|
25
|
+
() => d ? new Set(d) : j,
|
|
26
|
+
[d, j]
|
|
27
|
+
), [V, W] = A(q), v = I ?? D ?? V, [N, y] = A(void 0), F = Z(/* @__PURE__ */ new Map()), K = O(
|
|
28
|
+
(e) => {
|
|
29
|
+
d === void 0 && Q(e), b == null || b([...e]);
|
|
30
|
+
},
|
|
31
|
+
[d, b]
|
|
32
|
+
), k = O(
|
|
33
|
+
(e, t) => {
|
|
34
|
+
if (o.has(e) === t) return;
|
|
35
|
+
const r = new Set(o);
|
|
36
|
+
t ? r.add(e) : r.delete(e), K(r);
|
|
37
|
+
},
|
|
38
|
+
[o, K]
|
|
39
|
+
), M = O(
|
|
40
|
+
(e) => {
|
|
41
|
+
I === void 0 && D === void 0 && W(e), h == null || h(e), g == null || g(e);
|
|
42
|
+
},
|
|
43
|
+
[h, g, I, D]
|
|
44
|
+
), a = S(() => {
|
|
45
|
+
const e = [], t = (r, l, c) => {
|
|
46
|
+
r.forEach((s, n) => {
|
|
47
|
+
e.push({ node: s, level: l, setSize: r.length, posInSet: n + 1, parentId: c }), s.children && o.has(s.id) && t(s.children, l + 1, s.id);
|
|
48
|
+
});
|
|
49
|
+
};
|
|
50
|
+
return t(p, 1, null), e;
|
|
51
|
+
}, [p, o]), X = N && a.some((e) => e.node.id === N) && N || v && a.some((e) => e.node.id === v) && v || ((H = a[0]) == null ? void 0 : H.node.id), u = (e) => {
|
|
52
|
+
var t;
|
|
53
|
+
e && (y(e), (t = F.current.get(e)) == null || t.focus());
|
|
54
|
+
}, Y = (e, t) => {
|
|
55
|
+
var n, m, w, x, i;
|
|
56
|
+
const { node: r } = t, l = !!r.children, c = o.has(r.id), s = a.findIndex((z) => z.node.id === r.id);
|
|
57
|
+
switch (e.key) {
|
|
58
|
+
case "ArrowDown":
|
|
59
|
+
e.preventDefault(), u((n = a[s + 1]) == null ? void 0 : n.node.id);
|
|
60
|
+
break;
|
|
61
|
+
case "ArrowUp":
|
|
62
|
+
e.preventDefault(), u((m = a[s - 1]) == null ? void 0 : m.node.id);
|
|
63
|
+
break;
|
|
64
|
+
case "ArrowRight":
|
|
65
|
+
e.preventDefault(), l && !c ? k(r.id, !0) : l && c && u((w = r.children[0]) == null ? void 0 : w.id);
|
|
66
|
+
break;
|
|
67
|
+
case "ArrowLeft":
|
|
68
|
+
e.preventDefault(), l && c ? k(r.id, !1) : t.parentId && u(t.parentId);
|
|
69
|
+
break;
|
|
70
|
+
case "Home":
|
|
71
|
+
e.preventDefault(), u((x = a[0]) == null ? void 0 : x.node.id);
|
|
72
|
+
break;
|
|
73
|
+
case "End":
|
|
74
|
+
e.preventDefault(), u((i = a[a.length - 1]) == null ? void 0 : i.node.id);
|
|
75
|
+
break;
|
|
76
|
+
case "Enter":
|
|
77
|
+
case " ":
|
|
78
|
+
e.preventDefault(), M(r.id), l && k(r.id, !c);
|
|
79
|
+
break;
|
|
80
|
+
default:
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
e.stopPropagation();
|
|
84
|
+
};
|
|
85
|
+
function E(e, t, r, l, c) {
|
|
86
|
+
const s = !!e.children, n = o.has(e.id), m = v === e.id, w = s ? n ? $ : P : C, x = { node: e, parentId: c };
|
|
87
|
+
return /* @__PURE__ */ L(
|
|
88
|
+
"li",
|
|
89
|
+
{
|
|
90
|
+
ref: (i) => {
|
|
91
|
+
i ? F.current.set(e.id, i) : F.current.delete(e.id);
|
|
92
|
+
},
|
|
93
|
+
role: "treeitem",
|
|
94
|
+
"aria-expanded": s ? n : void 0,
|
|
95
|
+
"aria-selected": m,
|
|
96
|
+
"aria-level": t,
|
|
97
|
+
"aria-setsize": r,
|
|
98
|
+
"aria-posinset": l,
|
|
99
|
+
tabIndex: X === e.id ? 0 : -1,
|
|
100
|
+
onFocus: (i) => {
|
|
101
|
+
i.target === i.currentTarget && y(e.id);
|
|
102
|
+
},
|
|
103
|
+
onKeyDown: (i) => {
|
|
104
|
+
i.target === i.currentTarget && Y(i, x);
|
|
105
|
+
},
|
|
106
|
+
onClick: (i) => {
|
|
107
|
+
i.stopPropagation(), y(e.id), M(e.id), s && k(e.id, !n);
|
|
108
|
+
},
|
|
109
|
+
className: "outline-none",
|
|
110
|
+
children: [
|
|
111
|
+
/* @__PURE__ */ L(
|
|
112
|
+
"div",
|
|
113
|
+
{
|
|
114
|
+
style: { paddingLeft: (t - 1) * 16 + 8 },
|
|
115
|
+
className: R(
|
|
116
|
+
"flex w-full cursor-default items-center gap-1.5 rounded-md py-1 pr-2 text-left text-sm",
|
|
117
|
+
"hover:bg-background-muted",
|
|
118
|
+
"[li:focus-visible>&]:ring-ring [li:focus-visible>&]:ring-offset-background [li:focus-visible>&]:ring-2 [li:focus-visible>&]:ring-offset-1",
|
|
119
|
+
m && "bg-accent-soft text-foreground"
|
|
120
|
+
),
|
|
121
|
+
children: [
|
|
122
|
+
s ? /* @__PURE__ */ f(
|
|
123
|
+
_,
|
|
124
|
+
{
|
|
125
|
+
className: R(
|
|
126
|
+
"text-foreground-subtle size-3.5 shrink-0 transition-transform",
|
|
127
|
+
n && "rotate-90"
|
|
128
|
+
),
|
|
129
|
+
"aria-hidden": !0
|
|
130
|
+
}
|
|
131
|
+
) : /* @__PURE__ */ f("span", { className: "w-3.5 shrink-0", "aria-hidden": !0 }),
|
|
132
|
+
e.icon ?? /* @__PURE__ */ f(w, { className: "text-foreground-muted size-4 shrink-0", "aria-hidden": !0 }),
|
|
133
|
+
/* @__PURE__ */ f(
|
|
134
|
+
"span",
|
|
135
|
+
{
|
|
136
|
+
className: "truncate",
|
|
137
|
+
title: typeof e.label == "string" ? e.label : void 0,
|
|
138
|
+
children: e.label
|
|
139
|
+
}
|
|
140
|
+
)
|
|
141
|
+
]
|
|
142
|
+
}
|
|
143
|
+
),
|
|
144
|
+
s && n && /* @__PURE__ */ f("ul", { role: "group", className: "mt-0.5", children: e.children.map(
|
|
145
|
+
(i, z) => E(i, t + 1, e.children.length, z + 1, e.id)
|
|
146
|
+
) })
|
|
147
|
+
]
|
|
148
|
+
},
|
|
149
|
+
e.id
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
return /* @__PURE__ */ f(
|
|
153
|
+
"ul",
|
|
154
|
+
{
|
|
155
|
+
role: "tree",
|
|
156
|
+
"aria-label": G ?? (T ? void 0 : J.tree.label),
|
|
157
|
+
"aria-labelledby": T,
|
|
158
|
+
className: R("flex flex-col gap-0.5", B),
|
|
159
|
+
children: p.map((e, t) => E(e, 1, p.length, t + 1, null))
|
|
160
|
+
}
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
te.displayName = "Tree";
|
|
164
|
+
export {
|
|
165
|
+
te as Tree
|
|
166
|
+
};
|
|
167
|
+
//# sourceMappingURL=Tree.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Tree.js","sources":["../../../src/components/ui/Tree.tsx"],"sourcesContent":["'use client';\n\nimport { useCallback, useMemo, useRef, useState, type KeyboardEvent, type ReactNode } from 'react';\nimport { ChevronRight, File as FileIcon, Folder, FolderOpen } from '@/icons';\nimport { cn } from '@/lib/utils';\nimport { useStrings } from '@/hooks/use-strings';\n\nexport interface TreeNode {\n id: string;\n label: ReactNode;\n /** If undefined the node is a leaf; if empty array it is an empty folder. */\n children?: TreeNode[];\n /** Optional custom icon (overrides default file/folder icons). */\n icon?: ReactNode;\n}\n\nexport interface TreeProps {\n /** The root nodes. */\n data: TreeNode[];\n /** Ids to expand by default (uncontrolled). */\n defaultExpanded?: string[];\n /** Controlled set of expanded ids. */\n expanded?: string[];\n /** Called with the full next set of expanded ids. */\n onExpandedChange?: (next: string[]) => void;\n /** Currently selected node id (controlled). Alias: `selected`. */\n selectedId?: string;\n /** Controlled selected id. Takes precedence over `selectedId`. */\n selected?: string;\n /** Default selected id (uncontrolled). */\n defaultSelected?: string;\n /** Called when a node is activated (click / Enter / Space). */\n onSelect?: (id: string) => void;\n /** Alias of `onSelect` for the controlled `selected` pair. */\n onSelectedChange?: (id: string) => void;\n /** Accessible name for the tree. */\n 'aria-label'?: string;\n 'aria-labelledby'?: string;\n className?: string;\n}\n\ninterface FlatNode {\n node: TreeNode;\n level: number;\n setSize: number;\n posInSet: number;\n parentId: string | null;\n}\n\n/**\n * Expandable file-tree style list following the WAI-ARIA tree pattern.\n * Single selection, roving tabindex (one tab stop), and full arrow-key\n * navigation: Up/Down move, Right expands or moves into a folder, Left\n * collapses or moves to the parent, Home/End jump, Enter/Space select.\n *\n * Expansion and selection are uncontrolled by default; pass\n * `expanded`/`onExpandedChange` and `selected`/`onSelectedChange` to control.\n */\nexport function Tree({\n data,\n defaultExpanded = [],\n expanded: expandedProp,\n onExpandedChange,\n selectedId,\n selected: selectedProp,\n defaultSelected,\n onSelect,\n onSelectedChange,\n className,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledby,\n}: TreeProps) {\n const strings = useStrings();\n const [internalExpanded, setInternalExpanded] = useState<Set<string>>(\n () => new Set(defaultExpanded),\n );\n const expanded = useMemo(\n () => (expandedProp ? new Set(expandedProp) : internalExpanded),\n [expandedProp, internalExpanded],\n );\n\n const [internalSelected, setInternalSelected] = useState<string | undefined>(defaultSelected);\n const selected = selectedProp ?? selectedId ?? internalSelected;\n\n const [focusedId, setFocusedId] = useState<string | undefined>(undefined);\n const itemRefs = useRef(new Map<string, HTMLLIElement>());\n\n const setExpanded = useCallback(\n (next: Set<string>) => {\n if (expandedProp === undefined) setInternalExpanded(next);\n onExpandedChange?.([...next]);\n },\n [expandedProp, onExpandedChange],\n );\n\n const setOpen = useCallback(\n (id: string, open: boolean) => {\n if (expanded.has(id) === open) return;\n const next = new Set(expanded);\n if (open) next.add(id);\n else next.delete(id);\n setExpanded(next);\n },\n [expanded, setExpanded],\n );\n\n const select = useCallback(\n (id: string) => {\n if (selectedProp === undefined && selectedId === undefined) setInternalSelected(id);\n onSelect?.(id);\n onSelectedChange?.(id);\n },\n [onSelect, onSelectedChange, selectedProp, selectedId],\n );\n\n // Flatten the *visible* nodes in DOM order — this is what arrow keys walk.\n const visible = useMemo(() => {\n const out: FlatNode[] = [];\n const walk = (nodes: TreeNode[], level: number, parentId: string | null) => {\n nodes.forEach((node, i) => {\n out.push({ node, level, setSize: nodes.length, posInSet: i + 1, parentId });\n if (node.children && expanded.has(node.id)) walk(node.children, level + 1, node.id);\n });\n };\n walk(data, 1, null);\n return out;\n }, [data, expanded]);\n\n // The single tab stop: focused item if still visible, else selected, else first.\n const tabStopId =\n (focusedId && visible.some((v) => v.node.id === focusedId) && focusedId) ||\n (selected && visible.some((v) => v.node.id === selected) && selected) ||\n visible[0]?.node.id;\n\n const focusItem = (id: string | undefined) => {\n if (!id) return;\n setFocusedId(id);\n itemRefs.current.get(id)?.focus();\n };\n\n const handleKeyDown = (e: KeyboardEvent<HTMLLIElement>, flat: FlatNode) => {\n const { node } = flat;\n const hasChildren = !!node.children;\n const isOpen = expanded.has(node.id);\n const index = visible.findIndex((v) => v.node.id === node.id);\n\n switch (e.key) {\n case 'ArrowDown':\n e.preventDefault();\n focusItem(visible[index + 1]?.node.id);\n break;\n case 'ArrowUp':\n e.preventDefault();\n focusItem(visible[index - 1]?.node.id);\n break;\n case 'ArrowRight':\n e.preventDefault();\n if (hasChildren && !isOpen) setOpen(node.id, true);\n else if (hasChildren && isOpen) focusItem(node.children![0]?.id);\n break;\n case 'ArrowLeft':\n e.preventDefault();\n if (hasChildren && isOpen) setOpen(node.id, false);\n else if (flat.parentId) focusItem(flat.parentId);\n break;\n case 'Home':\n e.preventDefault();\n focusItem(visible[0]?.node.id);\n break;\n case 'End':\n e.preventDefault();\n focusItem(visible[visible.length - 1]?.node.id);\n break;\n case 'Enter':\n case ' ':\n e.preventDefault();\n select(node.id);\n if (hasChildren) setOpen(node.id, !isOpen);\n break;\n default:\n return;\n }\n e.stopPropagation();\n };\n\n function renderNode(\n node: TreeNode,\n level: number,\n setSize: number,\n posInSet: number,\n parentId: string | null,\n ): ReactNode {\n const hasChildren = !!node.children;\n const isOpen = expanded.has(node.id);\n const isSelected = selected === node.id;\n const DefaultIcon = hasChildren ? (isOpen ? FolderOpen : Folder) : FileIcon;\n const flat: FlatNode = { node, level, setSize, posInSet, parentId };\n\n return (\n <li\n key={node.id}\n ref={(el) => {\n if (el) itemRefs.current.set(node.id, el);\n else itemRefs.current.delete(node.id);\n }}\n role=\"treeitem\"\n aria-expanded={hasChildren ? isOpen : undefined}\n aria-selected={isSelected}\n aria-level={level}\n aria-setsize={setSize}\n aria-posinset={posInSet}\n tabIndex={tabStopId === node.id ? 0 : -1}\n onFocus={(e) => {\n if (e.target === e.currentTarget) setFocusedId(node.id);\n }}\n onKeyDown={(e) => {\n if (e.target === e.currentTarget) handleKeyDown(e, flat);\n }}\n onClick={(e) => {\n e.stopPropagation();\n setFocusedId(node.id);\n select(node.id);\n if (hasChildren) setOpen(node.id, !isOpen);\n }}\n className=\"outline-none\"\n >\n <div\n style={{ paddingLeft: (level - 1) * 16 + 8 }}\n className={cn(\n 'flex w-full cursor-default items-center gap-1.5 rounded-md py-1 pr-2 text-left text-sm',\n 'hover:bg-background-muted',\n '[li:focus-visible>&]:ring-ring [li:focus-visible>&]:ring-offset-background [li:focus-visible>&]:ring-2 [li:focus-visible>&]:ring-offset-1',\n isSelected && 'bg-accent-soft text-foreground',\n )}\n >\n {hasChildren ? (\n <ChevronRight\n className={cn(\n 'text-foreground-subtle size-3.5 shrink-0 transition-transform',\n isOpen && 'rotate-90',\n )}\n aria-hidden\n />\n ) : (\n <span className=\"w-3.5 shrink-0\" aria-hidden />\n )}\n {node.icon ?? (\n <DefaultIcon className=\"text-foreground-muted size-4 shrink-0\" aria-hidden />\n )}\n <span\n className=\"truncate\"\n title={typeof node.label === 'string' ? node.label : undefined}\n >\n {node.label}\n </span>\n </div>\n {hasChildren && isOpen && (\n <ul role=\"group\" className=\"mt-0.5\">\n {node.children!.map((c, i) =>\n renderNode(c, level + 1, node.children!.length, i + 1, node.id),\n )}\n </ul>\n )}\n </li>\n );\n }\n\n return (\n <ul\n role=\"tree\"\n aria-label={ariaLabel ?? (ariaLabelledby ? undefined : strings.tree.label)}\n aria-labelledby={ariaLabelledby}\n className={cn('flex flex-col gap-0.5', className)}\n >\n {data.map((n, i) => renderNode(n, 1, data.length, i + 1, null))}\n </ul>\n );\n}\nTree.displayName = 'Tree';\n"],"names":["Tree","data","defaultExpanded","expandedProp","onExpandedChange","selectedId","selectedProp","defaultSelected","onSelect","onSelectedChange","className","ariaLabel","ariaLabelledby","strings","useStrings","internalExpanded","setInternalExpanded","useState","expanded","useMemo","internalSelected","setInternalSelected","selected","focusedId","setFocusedId","itemRefs","useRef","setExpanded","useCallback","next","setOpen","id","open","select","visible","out","walk","nodes","level","parentId","node","i","tabStopId","v","_a","focusItem","handleKeyDown","flat","hasChildren","isOpen","index","_b","_c","_d","_e","renderNode","setSize","posInSet","isSelected","DefaultIcon","FolderOpen","Folder","FileIcon","jsxs","el","e","cn","jsx","ChevronRight","c","n"],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export interface FieldIds {
|
|
2
|
+
/** Id for the control itself (`<input id>` / `<label htmlFor>`). */
|
|
3
|
+
fieldId: string;
|
|
4
|
+
/** Id for the description / helper text element. */
|
|
5
|
+
helperId: string;
|
|
6
|
+
/** Id for the validation message element. */
|
|
7
|
+
errorId: string;
|
|
8
|
+
/**
|
|
9
|
+
* `aria-describedby` value computed from what actually renders — the error
|
|
10
|
+
* wins over the helper; `undefined` when neither is present.
|
|
11
|
+
*/
|
|
12
|
+
describedBy: string | undefined;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Shared id wiring for form fields: one stable id for the control plus
|
|
16
|
+
* derived helper / error ids, and an `aria-describedby` that only references
|
|
17
|
+
* elements that exist. Pass a consumer `id` to keep `<label htmlFor>` in sync.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* const { fieldId, helperId, errorId, describedBy } = useFieldIds(id, {
|
|
21
|
+
* hasHelper: Boolean(helperText),
|
|
22
|
+
* hasError: Boolean(error),
|
|
23
|
+
* });
|
|
24
|
+
*/
|
|
25
|
+
export declare function useFieldIds(id?: string, present?: {
|
|
26
|
+
hasHelper?: boolean;
|
|
27
|
+
hasError?: boolean;
|
|
28
|
+
extra?: string;
|
|
29
|
+
}): FieldIds;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useId as n } from "react";
|
|
3
|
+
function c(s, r = {}) {
|
|
4
|
+
const i = n(), e = s ?? i, o = `${e}-helper`, t = `${e}-error`, d = [
|
|
5
|
+
r.hasError ? t : r.hasHelper ? o : void 0,
|
|
6
|
+
r.extra
|
|
7
|
+
].filter(Boolean);
|
|
8
|
+
return {
|
|
9
|
+
fieldId: e,
|
|
10
|
+
helperId: o,
|
|
11
|
+
errorId: t,
|
|
12
|
+
describedBy: d.length ? d.join(" ") : void 0
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
export {
|
|
16
|
+
c as useFieldIds
|
|
17
|
+
};
|
|
18
|
+
//# sourceMappingURL=use-field-ids.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-field-ids.js","sources":["../../src/hooks/use-field-ids.ts"],"sourcesContent":["'use client';\n\nimport { useId } from 'react';\n\nexport interface FieldIds {\n /** Id for the control itself (`<input id>` / `<label htmlFor>`). */\n fieldId: string;\n /** Id for the description / helper text element. */\n helperId: string;\n /** Id for the validation message element. */\n errorId: string;\n /**\n * `aria-describedby` value computed from what actually renders — the error\n * wins over the helper; `undefined` when neither is present.\n */\n describedBy: string | undefined;\n}\n\n/**\n * Shared id wiring for form fields: one stable id for the control plus\n * derived helper / error ids, and an `aria-describedby` that only references\n * elements that exist. Pass a consumer `id` to keep `<label htmlFor>` in sync.\n *\n * @example\n * const { fieldId, helperId, errorId, describedBy } = useFieldIds(id, {\n * hasHelper: Boolean(helperText),\n * hasError: Boolean(error),\n * });\n */\nexport function useFieldIds(\n id?: string,\n present: { hasHelper?: boolean; hasError?: boolean; extra?: string } = {},\n): FieldIds {\n const autoId = useId();\n const fieldId = id ?? autoId;\n const helperId = `${fieldId}-helper`;\n const errorId = `${fieldId}-error`;\n const parts = [\n present.hasError ? errorId : present.hasHelper ? helperId : undefined,\n present.extra,\n ].filter(Boolean);\n return {\n fieldId,\n helperId,\n errorId,\n describedBy: parts.length ? parts.join(' ') : undefined,\n };\n}\n"],"names":["useFieldIds","id","present","autoId","useId","fieldId","helperId","errorId","parts"],"mappings":";;AA6BO,SAASA,EACdC,GACAC,IAAuE,IAC7D;AACV,QAAMC,IAASC,EAAA,GACTC,IAAUJ,KAAME,GAChBG,IAAW,GAAGD,CAAO,WACrBE,IAAU,GAAGF,CAAO,UACpBG,IAAQ;AAAA,IACZN,EAAQ,WAAWK,IAAUL,EAAQ,YAAYI,IAAW;AAAA,IAC5DJ,EAAQ;AAAA,EAAA,EACR,OAAO,OAAO;AAChB,SAAO;AAAA,IACL,SAAAG;AAAA,IACA,UAAAC;AAAA,IACA,SAAAC;AAAA,IACA,aAAaC,EAAM,SAASA,EAAM,KAAK,GAAG,IAAI;AAAA,EAAA;AAElD;"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useState as s, useEffect as c } from "react";
|
|
3
|
+
function d(e) {
|
|
4
|
+
const [i, n] = s(() => typeof window > "u" ? !1 : window.matchMedia(e).matches);
|
|
5
|
+
return c(() => {
|
|
6
|
+
if (typeof window > "u") return;
|
|
7
|
+
const t = window.matchMedia(e), r = (o) => n(o.matches);
|
|
8
|
+
return n(t.matches), t.addEventListener("change", r), () => t.removeEventListener("change", r);
|
|
9
|
+
}, [e]), i;
|
|
10
|
+
}
|
|
11
|
+
function a() {
|
|
12
|
+
return d("(prefers-reduced-motion: reduce)");
|
|
13
|
+
}
|
|
14
|
+
export {
|
|
15
|
+
d as useMediaQuery,
|
|
16
|
+
a as usePrefersReducedMotion
|
|
17
|
+
};
|
|
18
|
+
//# sourceMappingURL=use-media-query.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-media-query.js","sources":["../../src/hooks/use-media-query.ts"],"sourcesContent":["'use client';\n\nimport { useEffect, useState } from 'react';\n\n/**\n * Subscribe to a CSS media query. SSR-safe — returns `false` on the server.\n *\n * @example\n * const isDesktop = useMediaQuery('(min-width: 1024px)');\n */\nexport function useMediaQuery(query: string): boolean {\n const [matches, setMatches] = useState(() => {\n if (typeof window === 'undefined') return false;\n return window.matchMedia(query).matches;\n });\n\n useEffect(() => {\n if (typeof window === 'undefined') return;\n const list = window.matchMedia(query);\n const onChange = (event: MediaQueryListEvent) => setMatches(event.matches);\n setMatches(list.matches);\n list.addEventListener('change', onChange);\n return () => list.removeEventListener('change', onChange);\n }, [query]);\n\n return matches;\n}\n\n/** Convenience: respect `prefers-reduced-motion`. */\nexport function usePrefersReducedMotion(): boolean {\n return useMediaQuery('(prefers-reduced-motion: reduce)');\n}\n"],"names":["useMediaQuery","query","matches","setMatches","useState","useEffect","list","onChange","event","usePrefersReducedMotion"],"mappings":";;AAUO,SAASA,EAAcC,GAAwB;AACpD,QAAM,CAACC,GAASC,CAAU,IAAIC,EAAS,MACjC,OAAO,SAAW,MAAoB,KACnC,OAAO,WAAWH,CAAK,EAAE,OACjC;AAED,SAAAI,EAAU,MAAM;AACd,QAAI,OAAO,SAAW,IAAa;AACnC,UAAMC,IAAO,OAAO,WAAWL,CAAK,GAC9BM,IAAW,CAACC,MAA+BL,EAAWK,EAAM,OAAO;AACzE,WAAAL,EAAWG,EAAK,OAAO,GACvBA,EAAK,iBAAiB,UAAUC,CAAQ,GACjC,MAAMD,EAAK,oBAAoB,UAAUC,CAAQ;AAAA,EAC1D,GAAG,CAACN,CAAK,CAAC,GAEHC;AACT;AAGO,SAASO,IAAmC;AACjD,SAAOT,EAAc,kCAAkC;AACzD;"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { UiStrings } from '../lib/strings.js';
|
|
2
|
+
/** Resolved (complete) strings for the current subtree. */
|
|
3
|
+
export declare const StringsContext: import('react').Context<UiStrings>;
|
|
4
|
+
/** Read the library strings — falls back to the English defaults without a provider. */
|
|
5
|
+
export declare function useStrings(): UiStrings;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useContext as t, createContext as e } from "react";
|
|
3
|
+
import { defaultStrings as n } from "../lib/strings.js";
|
|
4
|
+
const r = e(n);
|
|
5
|
+
function s() {
|
|
6
|
+
return t(r);
|
|
7
|
+
}
|
|
8
|
+
export {
|
|
9
|
+
r as StringsContext,
|
|
10
|
+
s as useStrings
|
|
11
|
+
};
|
|
12
|
+
//# sourceMappingURL=use-strings.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-strings.js","sources":["../../src/hooks/use-strings.ts"],"sourcesContent":["'use client';\n\nimport { createContext, useContext } from 'react';\nimport { defaultStrings, type UiStrings } from '@/lib/strings';\n\n/** Resolved (complete) strings for the current subtree. */\nexport const StringsContext = createContext<UiStrings>(defaultStrings);\n\n/** Read the library strings — falls back to the English defaults without a provider. */\nexport function useStrings(): UiStrings {\n return useContext(StringsContext);\n}\n"],"names":["StringsContext","createContext","defaultStrings","useStrings","useContext"],"mappings":";;;AAMO,MAAMA,IAAiBC,EAAyBC,CAAc;AAG9D,SAASC,IAAwB;AACtC,SAAOC,EAAWJ,CAAc;AAClC;"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useState as h, useEffect as u, useCallback as n } from "react";
|
|
3
|
+
const i = {
|
|
4
|
+
toasts: [],
|
|
5
|
+
listeners: /* @__PURE__ */ new Set(),
|
|
6
|
+
emit() {
|
|
7
|
+
for (const s of this.listeners) s(this.toasts);
|
|
8
|
+
},
|
|
9
|
+
push(s) {
|
|
10
|
+
const t = s.id ?? `t_${Date.now()}_${Math.random().toString(36).slice(2, 7)}`, o = { open: !0, duration: 5e3, variant: "default", ...s, id: t };
|
|
11
|
+
return this.toasts = [o, ...this.toasts].slice(0, 3), this.emit(), t;
|
|
12
|
+
},
|
|
13
|
+
dismiss(s) {
|
|
14
|
+
this.toasts = this.toasts.map((t) => t.id === s ? { ...t, open: !1 } : t), this.emit();
|
|
15
|
+
},
|
|
16
|
+
remove(s) {
|
|
17
|
+
this.toasts = this.toasts.filter((t) => t.id !== s), this.emit();
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
function m() {
|
|
21
|
+
const [s, t] = h(() => [...i.toasts]);
|
|
22
|
+
u(() => {
|
|
23
|
+
const e = (c) => t([...c]);
|
|
24
|
+
return i.listeners.add(e), () => {
|
|
25
|
+
i.listeners.delete(e);
|
|
26
|
+
};
|
|
27
|
+
}, []);
|
|
28
|
+
const o = n((e) => i.push(e), []), a = n((e) => i.dismiss(e), []), r = n((e) => i.remove(e), []);
|
|
29
|
+
return { toasts: s, push: o, dismiss: a, remove: r };
|
|
30
|
+
}
|
|
31
|
+
const d = (s) => i.push(s);
|
|
32
|
+
export {
|
|
33
|
+
d as toast,
|
|
34
|
+
m as useToast
|
|
35
|
+
};
|
|
36
|
+
//# sourceMappingURL=use-toast.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-toast.js","sources":["../../src/hooks/use-toast.ts"],"sourcesContent":["'use client';\n\nimport { useCallback, useEffect, useState, type ReactNode } from 'react';\n\n/* -----------------------------------------------------------------------------\n * Minimal toast queue + hook. The host app mounts a single <ToastHub> near\n * the root which subscribes to this store; any component can call\n * `useToast().push({...})` from anywhere.\n *\n * Kept dependency-free intentionally — no external state library.\n * --------------------------------------------------------------------------- */\n\nexport type ToastVariant = 'default' | 'success' | 'warning' | 'danger' | 'info';\n\nexport interface ToastDescriptor {\n /** Auto-generated; consumers can pass an explicit id to update an in-flight toast. */\n id?: string;\n title?: ReactNode;\n description?: ReactNode;\n variant?: ToastVariant;\n /** Milliseconds before auto-dismiss. Default 5000. Set to 0 to keep open. */\n duration?: number;\n /** Action button. The alt text is used for screen reader announcements. */\n action?: {\n label: ReactNode;\n altText: string;\n onClick: () => void;\n };\n}\n\ninterface InternalToast extends ToastDescriptor {\n id: string;\n open: boolean;\n}\n\ntype Listener = (toasts: InternalToast[]) => void;\n\ninterface ToastStore {\n toasts: InternalToast[];\n listeners: Set<Listener>;\n emit(): void;\n push(t: ToastDescriptor): string;\n dismiss(id: string): void;\n remove(id: string): void;\n}\n\nconst store: ToastStore = {\n toasts: [],\n listeners: new Set<Listener>(),\n emit() {\n for (const listener of this.listeners) listener(this.toasts);\n },\n push(t: ToastDescriptor) {\n const id = t.id ?? `t_${Date.now()}_${Math.random().toString(36).slice(2, 7)}`;\n const next: InternalToast = { open: true, duration: 5000, variant: 'default', ...t, id };\n this.toasts = [next, ...this.toasts].slice(0, 3);\n this.emit();\n return id;\n },\n dismiss(id: string) {\n this.toasts = this.toasts.map((t: InternalToast) => (t.id === id ? { ...t, open: false } : t));\n this.emit();\n },\n remove(id: string) {\n this.toasts = this.toasts.filter((t: InternalToast) => t.id !== id);\n this.emit();\n },\n};\n\n/**\n * Subscribe to the toast queue and dispatch new toasts.\n *\n * @example\n * const toast = useToast();\n * toast.push({ variant: 'success', title: 'Saved' });\n *\n * // With an action\n * toast.push({\n * title: 'Project archived',\n * action: { label: 'Undo', altText: 'Undo archive', onClick: undo },\n * });\n */\nexport function useToast() {\n const [toasts, setToasts] = useState<InternalToast[]>(() => [...store.toasts]);\n\n useEffect(() => {\n const listener: Listener = (next) => setToasts([...next]);\n store.listeners.add(listener);\n return () => {\n store.listeners.delete(listener);\n };\n }, []);\n\n const push = useCallback((t: ToastDescriptor) => store.push(t), []);\n const dismiss = useCallback((id: string) => store.dismiss(id), []);\n const remove = useCallback((id: string) => store.remove(id), []);\n\n return { toasts, push, dismiss, remove };\n}\n\n/** Convenience export — dispatch a toast outside React (e.g. from an API client). */\nexport const toast = (t: ToastDescriptor) => store.push(t);\n"],"names":["store","listener","t","id","next","useToast","toasts","setToasts","useState","useEffect","push","useCallback","dismiss","remove","toast"],"mappings":";;AA8CA,MAAMA,IAAoB;AAAA,EACxB,QAAQ,CAAA;AAAA,EACR,+BAAe,IAAA;AAAA,EACf,OAAO;AACL,eAAWC,KAAY,KAAK,UAAW,CAAAA,EAAS,KAAK,MAAM;AAAA,EAC7D;AAAA,EACA,KAAKC,GAAoB;AACvB,UAAMC,IAAKD,EAAE,MAAM,KAAK,KAAK,KAAK,IAAI,KAAK,OAAA,EAAS,SAAS,EAAE,EAAE,MAAM,GAAG,CAAC,CAAC,IACtEE,IAAsB,EAAE,MAAM,IAAM,UAAU,KAAM,SAAS,WAAW,GAAGF,GAAG,IAAAC,EAAA;AACpF,gBAAK,SAAS,CAACC,GAAM,GAAG,KAAK,MAAM,EAAE,MAAM,GAAG,CAAC,GAC/C,KAAK,KAAA,GACED;AAAA,EACT;AAAA,EACA,QAAQA,GAAY;AAClB,SAAK,SAAS,KAAK,OAAO,IAAI,CAAC,MAAsB,EAAE,OAAOA,IAAK,EAAE,GAAG,GAAG,MAAM,GAAA,IAAU,CAAE,GAC7F,KAAK,KAAA;AAAA,EACP;AAAA,EACA,OAAOA,GAAY;AACjB,SAAK,SAAS,KAAK,OAAO,OAAO,CAAC,MAAqB,EAAE,OAAOA,CAAE,GAClE,KAAK,KAAA;AAAA,EACP;AACF;AAeO,SAASE,IAAW;AACzB,QAAM,CAACC,GAAQC,CAAS,IAAIC,EAA0B,MAAM,CAAC,GAAGR,EAAM,MAAM,CAAC;AAE7E,EAAAS,EAAU,MAAM;AACd,UAAMR,IAAqB,CAACG,MAASG,EAAU,CAAC,GAAGH,CAAI,CAAC;AACxD,WAAAJ,EAAM,UAAU,IAAIC,CAAQ,GACrB,MAAM;AACX,MAAAD,EAAM,UAAU,OAAOC,CAAQ;AAAA,IACjC;AAAA,EACF,GAAG,CAAA,CAAE;AAEL,QAAMS,IAAOC,EAAY,CAACT,MAAuBF,EAAM,KAAKE,CAAC,GAAG,EAAE,GAC5DU,IAAUD,EAAY,CAACR,MAAeH,EAAM,QAAQG,CAAE,GAAG,EAAE,GAC3DU,IAASF,EAAY,CAACR,MAAeH,EAAM,OAAOG,CAAE,GAAG,EAAE;AAE/D,SAAO,EAAE,QAAAG,GAAQ,MAAAI,GAAM,SAAAE,GAAS,QAAAC,EAAA;AAClC;AAGO,MAAMC,IAAQ,CAACZ,MAAuBF,EAAM,KAAKE,CAAC;"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Secondary entry — `@craftzbay/ui/icon`.
|
|
3
|
+
*
|
|
4
|
+
* Name-addressed `<Icon name="…">` plus the full `iconNames` list. Kept out of
|
|
5
|
+
* the main barrel because it references `lucide-react/dynamicIconImports`
|
|
6
|
+
* (a map of ~1500 lazy imports) which bundlers cannot tree-shake.
|
|
7
|
+
*/
|
|
8
|
+
export { Icon, iconNames, type IconName, type IconProps } from './components/ui/Icon.js';
|
package/dist-lib/icon.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"icon.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;"}
|