@craftzbay/ui 0.9.0 → 0.11.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 +82 -10
- package/dist-lib/__tests__/helpers/color.d.ts +9 -0
- package/dist-lib/__tests__/helpers/theme.d.ts +18 -0
- package/dist-lib/components/ui/Accordion.js +71 -0
- package/dist-lib/components/ui/Accordion.js.map +1 -0
- package/dist-lib/components/ui/Alert.d.ts +17 -1
- package/dist-lib/components/ui/Alert.js +79 -0
- package/dist-lib/components/ui/Alert.js.map +1 -0
- package/dist-lib/components/ui/Avatar.d.ts +3 -3
- package/dist-lib/components/ui/Avatar.js +94 -0
- package/dist-lib/components/ui/Avatar.js.map +1 -0
- package/dist-lib/components/ui/Badge.d.ts +4 -2
- package/dist-lib/components/ui/Badge.js +76 -0
- package/dist-lib/components/ui/Badge.js.map +1 -0
- package/dist-lib/components/ui/Breadcrumbs.d.ts +2 -0
- package/dist-lib/components/ui/Breadcrumbs.js +52 -0
- package/dist-lib/components/ui/Breadcrumbs.js.map +1 -0
- package/dist-lib/components/ui/Button.d.ts +6 -4
- package/dist-lib/components/ui/Button.js +131 -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 +139 -0
- package/dist-lib/components/ui/Calendar.js.map +1 -0
- package/dist-lib/components/ui/Card.d.ts +3 -1
- package/dist-lib/components/ui/Card.js +97 -0
- package/dist-lib/components/ui/Card.js.map +1 -0
- package/dist-lib/components/ui/Carousel.d.ts +11 -7
- package/dist-lib/components/ui/Carousel.js +174 -0
- package/dist-lib/components/ui/Carousel.js.map +1 -0
- package/dist-lib/components/ui/Chart.d.ts +68 -7
- package/dist-lib/components/ui/Chart.js +487 -0
- package/dist-lib/components/ui/Chart.js.map +1 -0
- package/dist-lib/components/ui/Checkbox.js +70 -0
- package/dist-lib/components/ui/Checkbox.js.map +1 -0
- package/dist-lib/components/ui/Combobox.d.ts +9 -0
- package/dist-lib/components/ui/Combobox.js +195 -0
- package/dist-lib/components/ui/Combobox.js.map +1 -0
- package/dist-lib/components/ui/CommandPalette.d.ts +9 -6
- package/dist-lib/components/ui/CommandPalette.js +179 -0
- package/dist-lib/components/ui/CommandPalette.js.map +1 -0
- package/dist-lib/components/ui/ContextMenu.d.ts +7 -2
- package/dist-lib/components/ui/ContextMenu.js +165 -0
- package/dist-lib/components/ui/ContextMenu.js.map +1 -0
- package/dist-lib/components/ui/DataGrid.d.ts +17 -4
- package/dist-lib/components/ui/DataGrid.js +99 -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 +193 -0
- package/dist-lib/components/ui/DatePicker.js.map +1 -0
- package/dist-lib/components/ui/DesignSystemProvider.d.ts +102 -41
- package/dist-lib/components/ui/DesignSystemProvider.js +135 -0
- package/dist-lib/components/ui/DesignSystemProvider.js.map +1 -0
- package/dist-lib/components/ui/Dialog.d.ts +19 -4
- package/dist-lib/components/ui/Dialog.js +165 -0
- package/dist-lib/components/ui/Dialog.js.map +1 -0
- package/dist-lib/components/ui/Drawer.d.ts +23 -2
- package/dist-lib/components/ui/Drawer.js +132 -0
- package/dist-lib/components/ui/Drawer.js.map +1 -0
- package/dist-lib/components/ui/DropdownMenu.js +169 -0
- package/dist-lib/components/ui/DropdownMenu.js.map +1 -0
- package/dist-lib/components/ui/EmptyState.d.ts +2 -0
- package/dist-lib/components/ui/EmptyState.js +46 -0
- package/dist-lib/components/ui/EmptyState.js.map +1 -0
- package/dist-lib/components/ui/ErrorState.d.ts +13 -2
- package/dist-lib/components/ui/ErrorState.js +69 -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 +111 -0
- package/dist-lib/components/ui/FileUpload.js.map +1 -0
- package/dist-lib/components/ui/Form.d.ts +16 -2
- package/dist-lib/components/ui/Form.js +131 -0
- package/dist-lib/components/ui/Form.js.map +1 -0
- package/dist-lib/components/ui/Icon.d.ts +1 -1
- package/dist-lib/components/ui/Icon.js +32 -0
- package/dist-lib/components/ui/Icon.js.map +1 -0
- package/dist-lib/components/ui/IconButton.d.ts +3 -3
- package/dist-lib/components/ui/IconButton.js +64 -0
- package/dist-lib/components/ui/IconButton.js.map +1 -0
- package/dist-lib/components/ui/Input.d.ts +9 -4
- package/dist-lib/components/ui/Input.js +149 -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 +210 -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 +153 -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.d.ts +3 -3
- package/dist-lib/components/ui/Progress.js +100 -0
- package/dist-lib/components/ui/Progress.js.map +1 -0
- package/dist-lib/components/ui/RadioGroup.d.ts +1 -1
- package/dist-lib/components/ui/RadioGroup.js +81 -0
- package/dist-lib/components/ui/RadioGroup.js.map +1 -0
- package/dist-lib/components/ui/RelativeTime.d.ts +19 -0
- package/dist-lib/components/ui/RelativeTime.js +31 -0
- package/dist-lib/components/ui/RelativeTime.js.map +1 -0
- package/dist-lib/components/ui/ScrollArea.d.ts +9 -1
- package/dist-lib/components/ui/ScrollArea.js +60 -0
- package/dist-lib/components/ui/ScrollArea.js.map +1 -0
- package/dist-lib/components/ui/Select.d.ts +6 -1
- package/dist-lib/components/ui/Select.js +139 -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 +10 -1
- package/dist-lib/components/ui/Sheet.js +136 -0
- package/dist-lib/components/ui/Sheet.js.map +1 -0
- package/dist-lib/components/ui/Sidebar.d.ts +29 -14
- package/dist-lib/components/ui/Sidebar.js +212 -0
- package/dist-lib/components/ui/Sidebar.js.map +1 -0
- package/dist-lib/components/ui/Skeleton.d.ts +13 -2
- package/dist-lib/components/ui/Skeleton.js +29 -0
- package/dist-lib/components/ui/Skeleton.js.map +1 -0
- package/dist-lib/components/ui/Slider.js +64 -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 +57 -0
- package/dist-lib/components/ui/Snackbar.js.map +1 -0
- package/dist-lib/components/ui/Spinner.js +54 -0
- package/dist-lib/components/ui/Spinner.js.map +1 -0
- package/dist-lib/components/ui/Stepper.d.ts +6 -0
- package/dist-lib/components/ui/Stepper.js +111 -0
- package/dist-lib/components/ui/Stepper.js.map +1 -0
- package/dist-lib/components/ui/Switch.js +80 -0
- package/dist-lib/components/ui/Switch.js.map +1 -0
- package/dist-lib/components/ui/Table.d.ts +41 -5
- package/dist-lib/components/ui/Table.js +179 -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 +91 -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 +88 -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 +56 -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 +158 -0
- package/dist-lib/components/ui/Toast.js.map +1 -0
- package/dist-lib/components/ui/Tooltip.d.ts +4 -1
- package/dist-lib/components/ui/Tooltip.js +60 -0
- package/dist-lib/components/ui/Tooltip.js.map +1 -0
- package/dist-lib/components/ui/TopNav.d.ts +7 -3
- package/dist-lib/components/ui/TopNav.js +62 -0
- package/dist-lib/components/ui/TopNav.js.map +1 -0
- package/dist-lib/components/ui/Tree.d.ts +34 -7
- package/dist-lib/components/ui/Tree.js +170 -0
- package/dist-lib/components/ui/Tree.js.map +1 -0
- package/dist-lib/hooks/use-debounce.d.ts +19 -0
- package/dist-lib/hooks/use-debounce.js +36 -0
- package/dist-lib/hooks/use-debounce.js.map +1 -0
- package/dist-lib/hooks/use-delayed-loading.d.ts +21 -0
- package/dist-lib/hooks/use-delayed-loading.js +20 -0
- package/dist-lib/hooks/use-delayed-loading.js.map +1 -0
- package/dist-lib/hooks/use-field-ids.d.ts +30 -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.d.ts +3 -1
- package/dist-lib/hooks/use-media-query.js +22 -0
- package/dist-lib/hooks/use-media-query.js.map +1 -0
- package/dist-lib/hooks/use-modifier-key.d.ts +13 -0
- package/dist-lib/hooks/use-modifier-key.js +19 -0
- package/dist-lib/hooks/use-modifier-key.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.d.ts +7 -2
- package/dist-lib/hooks/use-toast.js +67 -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 +80 -88
- package/dist-lib/icons/index.js +163 -0
- package/dist-lib/icons/index.js.map +1 -0
- package/dist-lib/illustrations/index.d.ts +1 -0
- package/dist-lib/illustrations/index.js +114 -0
- package/dist-lib/illustrations/index.js.map +1 -0
- package/dist-lib/index.d.ts +68 -59
- package/dist-lib/index.js +288 -4530
- package/dist-lib/index.js.map +1 -1
- package/dist-lib/lib/format.d.ts +65 -0
- package/dist-lib/lib/format.js +95 -0
- package/dist-lib/lib/format.js.map +1 -0
- package/dist-lib/lib/return-focus.d.ts +11 -0
- package/dist-lib/lib/return-focus.js +12 -0
- package/dist-lib/lib/return-focus.js.map +1 -0
- package/dist-lib/lib/strings.d.ts +190 -0
- package/dist-lib/lib/strings.js +141 -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 +115 -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 +840 -0
- package/package.json +38 -8
- package/dist-lib/index.cjs +0 -8
- package/dist-lib/index.cjs.map +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { HTMLAttributes, ReactNode } from 'react';
|
|
1
|
+
import { AnchorHTMLAttributes, HTMLAttributes, ReactNode } from 'react';
|
|
2
2
|
export interface TopNavProps extends HTMLAttributes<HTMLElement> {
|
|
3
3
|
/** Logo / wordmark slot. Shown on the far left. */
|
|
4
4
|
logo: ReactNode;
|
|
@@ -8,6 +8,8 @@ export interface TopNavProps extends HTMLAttributes<HTMLElement> {
|
|
|
8
8
|
search?: ReactNode;
|
|
9
9
|
/** Right-aligned cluster — notifications, theme toggle, user menu. */
|
|
10
10
|
actions?: ReactNode;
|
|
11
|
+
/** Accessible name of the inner `<nav>` landmark. Default "Primary". */
|
|
12
|
+
navLabel?: string;
|
|
11
13
|
}
|
|
12
14
|
/**
|
|
13
15
|
* App-level top bar. Layout: `logo · nav · search · actions`. Search and nav
|
|
@@ -26,8 +28,10 @@ export interface TopNavProps extends HTMLAttributes<HTMLElement> {
|
|
|
26
28
|
* put them below the bar inside the page content.
|
|
27
29
|
*/
|
|
28
30
|
export declare const TopNav: import('react').ForwardRefExoticComponent<TopNavProps & import('react').RefAttributes<HTMLElement>>;
|
|
29
|
-
export interface TopNavLinkProps extends
|
|
30
|
-
href
|
|
31
|
+
export interface TopNavLinkProps extends AnchorHTMLAttributes<HTMLAnchorElement> {
|
|
32
|
+
href?: string;
|
|
31
33
|
active?: boolean;
|
|
34
|
+
/** Render the child (e.g. router `<Link>`) instead of an `<a>`. */
|
|
35
|
+
asChild?: boolean;
|
|
32
36
|
}
|
|
33
37
|
export declare const TopNavLink: import('react').ForwardRefExoticComponent<TopNavLinkProps & import('react').RefAttributes<HTMLAnchorElement>>;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsxs as l, jsx as i } from "react/jsx-runtime";
|
|
3
|
+
import { forwardRef as d } from "react";
|
|
4
|
+
import { Slot as u } from "@radix-ui/react-slot";
|
|
5
|
+
import { useStrings as g } from "../../hooks/use-strings.js";
|
|
6
|
+
import { cn as c } from "../../lib/utils.js";
|
|
7
|
+
const v = d(function({ logo: n, nav: e, search: r, actions: o, navLabel: t, className: s, ...a }, m) {
|
|
8
|
+
const p = g();
|
|
9
|
+
return /* @__PURE__ */ l(
|
|
10
|
+
"header",
|
|
11
|
+
{
|
|
12
|
+
ref: m,
|
|
13
|
+
className: c(
|
|
14
|
+
"sticky top-0 z-[var(--z-sticky)] flex h-14 w-full items-center gap-4",
|
|
15
|
+
"border-border bg-background border-b px-4",
|
|
16
|
+
s
|
|
17
|
+
),
|
|
18
|
+
...a,
|
|
19
|
+
children: [
|
|
20
|
+
/* @__PURE__ */ l("div", { className: "flex min-w-0 items-center gap-6", children: [
|
|
21
|
+
n,
|
|
22
|
+
e && /* @__PURE__ */ i(
|
|
23
|
+
"nav",
|
|
24
|
+
{
|
|
25
|
+
"aria-label": t ?? p.topNav.label,
|
|
26
|
+
className: "hidden items-center gap-1 md:flex",
|
|
27
|
+
children: e
|
|
28
|
+
}
|
|
29
|
+
)
|
|
30
|
+
] }),
|
|
31
|
+
r && /* @__PURE__ */ i("div", { className: "mx-auto max-w-md flex-1", children: r }),
|
|
32
|
+
o && /* @__PURE__ */ i("div", { className: "ml-auto flex shrink-0 items-center gap-2", children: o })
|
|
33
|
+
]
|
|
34
|
+
}
|
|
35
|
+
);
|
|
36
|
+
});
|
|
37
|
+
v.displayName = "TopNav";
|
|
38
|
+
const x = d(function({ href: n, active: e, asChild: r, className: o, children: t, ...s }, a) {
|
|
39
|
+
return /* @__PURE__ */ i(
|
|
40
|
+
r ? u : "a",
|
|
41
|
+
{
|
|
42
|
+
ref: a,
|
|
43
|
+
href: n,
|
|
44
|
+
"aria-current": e ? "page" : void 0,
|
|
45
|
+
className: c(
|
|
46
|
+
"inline-flex h-9 items-center rounded-md px-3 text-sm font-medium outline-none",
|
|
47
|
+
"transition-colors duration-[var(--duration-fast)]",
|
|
48
|
+
e ? "text-foreground" : "text-foreground-muted hover:text-foreground",
|
|
49
|
+
"focus-visible:ring-ring focus-visible:ring-offset-background focus-visible:ring-2 focus-visible:ring-offset-2",
|
|
50
|
+
o
|
|
51
|
+
),
|
|
52
|
+
...s,
|
|
53
|
+
children: t
|
|
54
|
+
}
|
|
55
|
+
);
|
|
56
|
+
});
|
|
57
|
+
x.displayName = "TopNavLink";
|
|
58
|
+
export {
|
|
59
|
+
v as TopNav,
|
|
60
|
+
x as TopNavLink
|
|
61
|
+
};
|
|
62
|
+
//# 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 AnchorHTMLAttributes, type HTMLAttributes, type ReactNode } from 'react';\nimport { Slot } from '@radix-ui/react-slot';\nimport { useStrings } from '@/hooks/use-strings';\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 /** Accessible name of the inner `<nav>` landmark. Default \"Primary\". */\n navLabel?: string;\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, navLabel, className, ...props },\n ref,\n) {\n const strings = useStrings();\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-border bg-background border-b px-4',\n className,\n )}\n {...props}\n >\n <div className=\"flex min-w-0 items-center gap-6\">\n {logo}\n {nav && (\n <nav\n aria-label={navLabel ?? strings.topNav.label}\n className=\"hidden items-center gap-1 md:flex\"\n >\n {nav}\n </nav>\n )}\n </div>\n {search && <div className=\"mx-auto max-w-md flex-1\">{search}</div>}\n {actions && <div className=\"ml-auto flex shrink-0 items-center gap-2\">{actions}</div>}\n </header>\n );\n});\nTopNav.displayName = 'TopNav';\n\nexport interface TopNavLinkProps extends AnchorHTMLAttributes<HTMLAnchorElement> {\n href?: string;\n active?: boolean;\n /** Render the child (e.g. router `<Link>`) instead of an `<a>`. */\n asChild?: boolean;\n}\n\nexport const TopNavLink = forwardRef<HTMLAnchorElement, TopNavLinkProps>(function TopNavLink(\n { href, active, asChild, className, children, ...props },\n ref,\n) {\n const Comp = asChild ? Slot : 'a';\n return (\n <Comp\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-ring focus-visible:ring-offset-background focus-visible:ring-2 focus-visible:ring-offset-2',\n className,\n )}\n {...props}\n >\n {children}\n </Comp>\n );\n});\nTopNavLink.displayName = 'TopNavLink';\n"],"names":["TopNav","forwardRef","logo","nav","search","actions","navLabel","className","props","ref","strings","useStrings","jsxs","cn","jsx","TopNavLink","href","active","asChild","children","Slot"],"mappings":";;;;;;AAoCO,MAAMA,IAASC,EAAqC,SACzD,EAAE,MAAAC,GAAM,KAAAC,GAAK,QAAAC,GAAQ,SAAAC,GAAS,UAAAC,GAAU,WAAAC,GAAW,GAAGC,EAAA,GACtDC,GACA;AACA,QAAMC,IAAUC,EAAA;AAChB,SACE,gBAAAC;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,KAAAH;AAAA,MACA,WAAWI;AAAA,QACT;AAAA,QACA;AAAA,QACAN;AAAA,MAAA;AAAA,MAED,GAAGC;AAAA,MAEJ,UAAA;AAAA,QAAA,gBAAAI,EAAC,OAAA,EAAI,WAAU,mCACZ,UAAA;AAAA,UAAAV;AAAA,UACAC,KACC,gBAAAW;AAAA,YAAC;AAAA,YAAA;AAAA,cACC,cAAYR,KAAYI,EAAQ,OAAO;AAAA,cACvC,WAAU;AAAA,cAET,UAAAP;AAAA,YAAA;AAAA,UAAA;AAAA,QACH,GAEJ;AAAA,QACCC,KAAU,gBAAAU,EAAC,OAAA,EAAI,WAAU,2BAA2B,UAAAV,GAAO;AAAA,QAC3DC,KAAW,gBAAAS,EAAC,OAAA,EAAI,WAAU,4CAA4C,UAAAT,EAAA,CAAQ;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA;AAGrF,CAAC;AACDL,EAAO,cAAc;AASd,MAAMe,IAAad,EAA+C,SACvE,EAAE,MAAAe,GAAM,QAAAC,GAAQ,SAAAC,GAAS,WAAAX,GAAW,UAAAY,GAAU,GAAGX,EAAA,GACjDC,GACA;AAEA,SACE,gBAAAK;AAAA,IAFWI,IAAUE,IAAO;AAAA,IAE3B;AAAA,MACC,KAAAX;AAAA,MACA,MAAAO;AAAA,MACA,gBAAcC,IAAS,SAAS;AAAA,MAChC,WAAWJ;AAAA,QACT;AAAA,QACA;AAAA,QACAI,IAAS,oBAAoB;AAAA,QAC7B;AAAA,QACAV;AAAA,MAAA;AAAA,MAED,GAAGC;AAAA,MAEH,UAAAW;AAAA,IAAA;AAAA,EAAA;AAGP,CAAC;AACDJ,EAAW,cAAc;"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ReactNode } from 'react';
|
|
1
|
+
import { HTMLAttributes, ReactNode } from 'react';
|
|
2
2
|
export interface TreeNode {
|
|
3
3
|
id: string;
|
|
4
4
|
label: ReactNode;
|
|
@@ -7,18 +7,45 @@ export interface TreeNode {
|
|
|
7
7
|
/** Optional custom icon (overrides default file/folder icons). */
|
|
8
8
|
icon?: ReactNode;
|
|
9
9
|
}
|
|
10
|
-
export interface TreeProps {
|
|
10
|
+
export interface TreeProps extends Omit<HTMLAttributes<HTMLUListElement>, 'onSelect'> {
|
|
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
|
+
/**
|
|
20
|
+
* @deprecated Use `selected` + `onSelectedChange`. Kept for backward
|
|
21
|
+
* compatibility; ignored when `selected` is present.
|
|
22
|
+
*/
|
|
16
23
|
selectedId?: string;
|
|
24
|
+
/**
|
|
25
|
+
* Controlled selected id. The component is controlled whenever this key is
|
|
26
|
+
* present in props (even as `undefined`), mirroring React inputs.
|
|
27
|
+
*/
|
|
28
|
+
selected?: string;
|
|
29
|
+
/** Default selected id (uncontrolled). */
|
|
30
|
+
defaultSelected?: string;
|
|
31
|
+
/**
|
|
32
|
+
* @deprecated Use `onSelectedChange`. Still called on every activation.
|
|
33
|
+
*/
|
|
17
34
|
onSelect?: (id: string) => void;
|
|
35
|
+
/** Called when a node is activated (click / Enter / Space). */
|
|
36
|
+
onSelectedChange?: (id: string) => void;
|
|
37
|
+
/** Accessible name for the tree. */
|
|
38
|
+
'aria-label'?: string;
|
|
39
|
+
'aria-labelledby'?: string;
|
|
18
40
|
className?: string;
|
|
19
41
|
}
|
|
20
42
|
/**
|
|
21
|
-
* Expandable file-tree style list
|
|
22
|
-
*
|
|
43
|
+
* Expandable file-tree style list following the WAI-ARIA tree pattern.
|
|
44
|
+
* Single selection, roving tabindex (one tab stop), and full arrow-key
|
|
45
|
+
* navigation: Up/Down move, Right expands or moves into a folder, Left
|
|
46
|
+
* collapses or moves to the parent, Home/End jump, Enter/Space select.
|
|
47
|
+
*
|
|
48
|
+
* Expansion and selection are uncontrolled by default; pass
|
|
49
|
+
* `expanded`/`onExpandedChange` and `selected`/`onSelectedChange` to control.
|
|
23
50
|
*/
|
|
24
|
-
export declare
|
|
51
|
+
export declare const Tree: import('react').ForwardRefExoticComponent<TreeProps & import('react').RefAttributes<HTMLUListElement>>;
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as f, jsxs as j } from "react/jsx-runtime";
|
|
3
|
+
import { forwardRef as _, useState as F, useMemo as K, useRef as ee, useCallback as R } from "react";
|
|
4
|
+
import { ChevronRight as te, FolderOpen as re, Folder as ie, File as se } from "../../icons/index.js";
|
|
5
|
+
import { cn as T } from "../../lib/utils.js";
|
|
6
|
+
import { useStrings as ae } from "../../hooks/use-strings.js";
|
|
7
|
+
const ne = _(function(I, M) {
|
|
8
|
+
var P;
|
|
9
|
+
const {
|
|
10
|
+
data: m,
|
|
11
|
+
defaultExpanded: H = [],
|
|
12
|
+
expanded: u,
|
|
13
|
+
onExpandedChange: h,
|
|
14
|
+
selectedId: U,
|
|
15
|
+
selected: $,
|
|
16
|
+
defaultSelected: q,
|
|
17
|
+
onSelect: b,
|
|
18
|
+
onSelectedChange: g,
|
|
19
|
+
className: B,
|
|
20
|
+
"aria-label": G,
|
|
21
|
+
"aria-labelledby": z,
|
|
22
|
+
...J
|
|
23
|
+
} = I, D = "selected" in I || "selectedId" in I, Q = ae(), [A, V] = F(
|
|
24
|
+
() => new Set(H)
|
|
25
|
+
), c = K(
|
|
26
|
+
() => u ? new Set(u) : A,
|
|
27
|
+
[u, A]
|
|
28
|
+
), [W, X] = F(q), x = D ? $ ?? U : W, [N, y] = F(void 0), S = ee(/* @__PURE__ */ new Map()), C = R(
|
|
29
|
+
(e) => {
|
|
30
|
+
u === void 0 && V(e), h == null || h([...e]);
|
|
31
|
+
},
|
|
32
|
+
[u, h]
|
|
33
|
+
), v = R(
|
|
34
|
+
(e, t) => {
|
|
35
|
+
if (c.has(e) === t) return;
|
|
36
|
+
const r = new Set(c);
|
|
37
|
+
t ? r.add(e) : r.delete(e), C(r);
|
|
38
|
+
},
|
|
39
|
+
[c, C]
|
|
40
|
+
), L = R(
|
|
41
|
+
(e) => {
|
|
42
|
+
D || X(e), b == null || b(e), g == null || g(e);
|
|
43
|
+
},
|
|
44
|
+
[b, g, D]
|
|
45
|
+
), n = K(() => {
|
|
46
|
+
const e = [], t = (r, l, d) => {
|
|
47
|
+
r.forEach((s, a) => {
|
|
48
|
+
e.push({ node: s, level: l, setSize: r.length, posInSet: a + 1, parentId: d }), s.children && c.has(s.id) && t(s.children, l + 1, s.id);
|
|
49
|
+
});
|
|
50
|
+
};
|
|
51
|
+
return t(m, 1, null), e;
|
|
52
|
+
}, [m, c]), Y = N && n.some((e) => e.node.id === N) && N || x && n.some((e) => e.node.id === x) && x || ((P = n[0]) == null ? void 0 : P.node.id), o = (e) => {
|
|
53
|
+
var t;
|
|
54
|
+
e && (y(e), (t = S.current.get(e)) == null || t.focus());
|
|
55
|
+
}, Z = (e, t) => {
|
|
56
|
+
var a, p, k, w, i;
|
|
57
|
+
const { node: r } = t, l = !!r.children, d = c.has(r.id), s = n.findIndex((E) => E.node.id === r.id);
|
|
58
|
+
switch (e.key) {
|
|
59
|
+
case "ArrowDown":
|
|
60
|
+
e.preventDefault(), o((a = n[s + 1]) == null ? void 0 : a.node.id);
|
|
61
|
+
break;
|
|
62
|
+
case "ArrowUp":
|
|
63
|
+
e.preventDefault(), o((p = n[s - 1]) == null ? void 0 : p.node.id);
|
|
64
|
+
break;
|
|
65
|
+
case "ArrowRight":
|
|
66
|
+
e.preventDefault(), l && !d ? v(r.id, !0) : l && d && o((k = r.children[0]) == null ? void 0 : k.id);
|
|
67
|
+
break;
|
|
68
|
+
case "ArrowLeft":
|
|
69
|
+
e.preventDefault(), l && d ? v(r.id, !1) : t.parentId && o(t.parentId);
|
|
70
|
+
break;
|
|
71
|
+
case "Home":
|
|
72
|
+
e.preventDefault(), o((w = n[0]) == null ? void 0 : w.node.id);
|
|
73
|
+
break;
|
|
74
|
+
case "End":
|
|
75
|
+
e.preventDefault(), o((i = n[n.length - 1]) == null ? void 0 : i.node.id);
|
|
76
|
+
break;
|
|
77
|
+
case "Enter":
|
|
78
|
+
case " ":
|
|
79
|
+
e.preventDefault(), L(r.id), l && v(r.id, !d);
|
|
80
|
+
break;
|
|
81
|
+
default:
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
e.stopPropagation();
|
|
85
|
+
};
|
|
86
|
+
function O(e, t, r, l, d) {
|
|
87
|
+
const s = !!e.children, a = c.has(e.id), p = x === e.id, k = s ? a ? re : ie : se, w = { node: e, parentId: d };
|
|
88
|
+
return /* @__PURE__ */ j(
|
|
89
|
+
"li",
|
|
90
|
+
{
|
|
91
|
+
ref: (i) => {
|
|
92
|
+
i ? S.current.set(e.id, i) : S.current.delete(e.id);
|
|
93
|
+
},
|
|
94
|
+
role: "treeitem",
|
|
95
|
+
"aria-expanded": s ? a : void 0,
|
|
96
|
+
"aria-selected": p,
|
|
97
|
+
"aria-level": t,
|
|
98
|
+
"aria-setsize": r,
|
|
99
|
+
"aria-posinset": l,
|
|
100
|
+
tabIndex: Y === e.id ? 0 : -1,
|
|
101
|
+
onFocus: (i) => {
|
|
102
|
+
i.target === i.currentTarget && y(e.id);
|
|
103
|
+
},
|
|
104
|
+
onKeyDown: (i) => {
|
|
105
|
+
i.target === i.currentTarget && Z(i, w);
|
|
106
|
+
},
|
|
107
|
+
onClick: (i) => {
|
|
108
|
+
i.stopPropagation(), y(e.id), L(e.id), s && v(e.id, !a);
|
|
109
|
+
},
|
|
110
|
+
className: "outline-none",
|
|
111
|
+
children: [
|
|
112
|
+
/* @__PURE__ */ j(
|
|
113
|
+
"div",
|
|
114
|
+
{
|
|
115
|
+
style: { paddingLeft: `${t - 1 + 0.5}rem` },
|
|
116
|
+
className: T(
|
|
117
|
+
"flex w-full cursor-default items-center gap-1.5 rounded-md py-1 pr-2 text-left text-sm",
|
|
118
|
+
"hover:bg-background-muted",
|
|
119
|
+
"[li:focus-visible>&]:ring-ring [li:focus-visible>&]:ring-offset-background [li:focus-visible>&]:ring-2 [li:focus-visible>&]:ring-offset-1",
|
|
120
|
+
p && "bg-accent-soft text-foreground"
|
|
121
|
+
),
|
|
122
|
+
children: [
|
|
123
|
+
s ? /* @__PURE__ */ f(
|
|
124
|
+
te,
|
|
125
|
+
{
|
|
126
|
+
className: T(
|
|
127
|
+
"text-foreground-subtle size-3.5 shrink-0 transition-transform",
|
|
128
|
+
a && "rotate-90"
|
|
129
|
+
),
|
|
130
|
+
"aria-hidden": !0
|
|
131
|
+
}
|
|
132
|
+
) : /* @__PURE__ */ f("span", { className: "w-3.5 shrink-0", "aria-hidden": !0 }),
|
|
133
|
+
e.icon ?? /* @__PURE__ */ f(k, { className: "text-foreground-muted size-4 shrink-0", "aria-hidden": !0 }),
|
|
134
|
+
/* @__PURE__ */ f(
|
|
135
|
+
"span",
|
|
136
|
+
{
|
|
137
|
+
className: "truncate",
|
|
138
|
+
title: typeof e.label == "string" ? e.label : void 0,
|
|
139
|
+
children: e.label
|
|
140
|
+
}
|
|
141
|
+
)
|
|
142
|
+
]
|
|
143
|
+
}
|
|
144
|
+
),
|
|
145
|
+
s && a && /* @__PURE__ */ f("ul", { role: "group", className: "mt-0.5", children: e.children.map(
|
|
146
|
+
(i, E) => O(i, t + 1, e.children.length, E + 1, e.id)
|
|
147
|
+
) })
|
|
148
|
+
]
|
|
149
|
+
},
|
|
150
|
+
e.id
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
return /* @__PURE__ */ f(
|
|
154
|
+
"ul",
|
|
155
|
+
{
|
|
156
|
+
ref: M,
|
|
157
|
+
role: "tree",
|
|
158
|
+
"aria-label": G ?? (z ? void 0 : Q.tree.label),
|
|
159
|
+
"aria-labelledby": z,
|
|
160
|
+
className: T("flex flex-col gap-0.5", B),
|
|
161
|
+
...J,
|
|
162
|
+
children: m.map((e, t) => O(e, 1, m.length, t + 1, null))
|
|
163
|
+
}
|
|
164
|
+
);
|
|
165
|
+
});
|
|
166
|
+
ne.displayName = "Tree";
|
|
167
|
+
export {
|
|
168
|
+
ne as Tree
|
|
169
|
+
};
|
|
170
|
+
//# sourceMappingURL=Tree.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Tree.js","sources":["../../../src/components/ui/Tree.tsx"],"sourcesContent":["'use client';\n\nimport {\n forwardRef,\n useCallback,\n useMemo,\n useRef,\n useState,\n type HTMLAttributes,\n type KeyboardEvent,\n type ReactNode,\n} 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 extends Omit<HTMLAttributes<HTMLUListElement>, 'onSelect'> {\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 /**\n * @deprecated Use `selected` + `onSelectedChange`. Kept for backward\n * compatibility; ignored when `selected` is present.\n */\n selectedId?: string;\n /**\n * Controlled selected id. The component is controlled whenever this key is\n * present in props (even as `undefined`), mirroring React inputs.\n */\n selected?: string;\n /** Default selected id (uncontrolled). */\n defaultSelected?: string;\n /**\n * @deprecated Use `onSelectedChange`. Still called on every activation.\n */\n onSelect?: (id: string) => void;\n /** Called when a node is activated (click / Enter / Space). */\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 const Tree = forwardRef<HTMLUListElement, TreeProps>(function Tree(props, ref) {\n const {\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 ...rest\n } = props;\n const isControlled = 'selected' in props || 'selectedId' in props;\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 = isControlled ? (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 (!isControlled) setInternalSelected(id);\n onSelect?.(id);\n onSelectedChange?.(id);\n },\n [onSelect, onSelectedChange, isControlled],\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 + 0.5}rem` }}\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 ref={ref}\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 {...rest}\n >\n {data.map((n, i) => renderNode(n, 1, data.length, i + 1, null))}\n </ul>\n );\n});\nTree.displayName = 'Tree';\n"],"names":["Tree","forwardRef","props","ref","data","defaultExpanded","expandedProp","onExpandedChange","selectedId","selectedProp","defaultSelected","onSelect","onSelectedChange","className","ariaLabel","ariaLabelledby","rest","isControlled","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,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns `value` after it has stopped changing for `delay` ms. Use for
|
|
3
|
+
* search inputs that drive a query (URL or fetch) without firing per keystroke.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* const [q, setQ] = useState('');
|
|
7
|
+
* const debouncedQ = useDebounce(q); // 300ms
|
|
8
|
+
* useEffect(() => { fetchRows(debouncedQ); }, [debouncedQ]);
|
|
9
|
+
*/
|
|
10
|
+
export declare function useDebounce<T>(value: T, delay?: number): T;
|
|
11
|
+
/**
|
|
12
|
+
* Stable callback that runs `fn` once `delay` ms have passed since the last
|
|
13
|
+
* call. Always invokes the latest `fn`; pending calls are dropped on unmount.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* const save = useDebouncedCallback((draft: string) => api.save(draft), 500);
|
|
17
|
+
* <Textarea onChange={(e) => save(e.target.value)} />
|
|
18
|
+
*/
|
|
19
|
+
export declare function useDebouncedCallback<A extends unknown[]>(fn: (...args: A) => void, delay?: number): (...args: A) => void;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useState as i, useEffect as c, useRef as o, useCallback as s } from "react";
|
|
3
|
+
function w(t, n = 300) {
|
|
4
|
+
const [r, e] = i(t);
|
|
5
|
+
return c(() => {
|
|
6
|
+
if (n <= 0) {
|
|
7
|
+
e(t);
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
const u = window.setTimeout(() => e(t), n);
|
|
11
|
+
return () => window.clearTimeout(u);
|
|
12
|
+
}, [t, n]), r;
|
|
13
|
+
}
|
|
14
|
+
function f(t, n = 300) {
|
|
15
|
+
const r = o(t), e = o(null);
|
|
16
|
+
return c(() => {
|
|
17
|
+
r.current = t;
|
|
18
|
+
}, [t]), c(
|
|
19
|
+
() => () => {
|
|
20
|
+
e.current !== null && window.clearTimeout(e.current);
|
|
21
|
+
},
|
|
22
|
+
[]
|
|
23
|
+
), s(
|
|
24
|
+
(...u) => {
|
|
25
|
+
e.current !== null && window.clearTimeout(e.current), e.current = window.setTimeout(() => {
|
|
26
|
+
e.current = null, r.current(...u);
|
|
27
|
+
}, n);
|
|
28
|
+
},
|
|
29
|
+
[n]
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
export {
|
|
33
|
+
w as useDebounce,
|
|
34
|
+
f as useDebouncedCallback
|
|
35
|
+
};
|
|
36
|
+
//# sourceMappingURL=use-debounce.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-debounce.js","sources":["../../src/hooks/use-debounce.ts"],"sourcesContent":["'use client';\n\nimport { useCallback, useEffect, useRef, useState } from 'react';\n\n/**\n * Returns `value` after it has stopped changing for `delay` ms. Use for\n * search inputs that drive a query (URL or fetch) without firing per keystroke.\n *\n * @example\n * const [q, setQ] = useState('');\n * const debouncedQ = useDebounce(q); // 300ms\n * useEffect(() => { fetchRows(debouncedQ); }, [debouncedQ]);\n */\nexport function useDebounce<T>(value: T, delay = 300): T {\n const [debounced, setDebounced] = useState(value);\n useEffect(() => {\n if (delay <= 0) {\n setDebounced(value);\n return;\n }\n const t = window.setTimeout(() => setDebounced(value), delay);\n return () => window.clearTimeout(t);\n }, [value, delay]);\n return debounced;\n}\n\n/**\n * Stable callback that runs `fn` once `delay` ms have passed since the last\n * call. Always invokes the latest `fn`; pending calls are dropped on unmount.\n *\n * @example\n * const save = useDebouncedCallback((draft: string) => api.save(draft), 500);\n * <Textarea onChange={(e) => save(e.target.value)} />\n */\nexport function useDebouncedCallback<A extends unknown[]>(\n fn: (...args: A) => void,\n delay = 300,\n): (...args: A) => void {\n const fnRef = useRef(fn);\n const timer = useRef<number | null>(null);\n useEffect(() => {\n fnRef.current = fn;\n }, [fn]);\n useEffect(\n () => () => {\n if (timer.current !== null) window.clearTimeout(timer.current);\n },\n [],\n );\n return useCallback(\n (...args: A) => {\n if (timer.current !== null) window.clearTimeout(timer.current);\n timer.current = window.setTimeout(() => {\n timer.current = null;\n fnRef.current(...args);\n }, delay);\n },\n [delay],\n );\n}\n"],"names":["useDebounce","value","delay","debounced","setDebounced","useState","useEffect","t","useDebouncedCallback","fn","fnRef","useRef","timer","useCallback","args"],"mappings":";;AAaO,SAASA,EAAeC,GAAUC,IAAQ,KAAQ;AACvD,QAAM,CAACC,GAAWC,CAAY,IAAIC,EAASJ,CAAK;AAChD,SAAAK,EAAU,MAAM;AACd,QAAIJ,KAAS,GAAG;AACd,MAAAE,EAAaH,CAAK;AAClB;AAAA,IACF;AACA,UAAMM,IAAI,OAAO,WAAW,MAAMH,EAAaH,CAAK,GAAGC,CAAK;AAC5D,WAAO,MAAM,OAAO,aAAaK,CAAC;AAAA,EACpC,GAAG,CAACN,GAAOC,CAAK,CAAC,GACVC;AACT;AAUO,SAASK,EACdC,GACAP,IAAQ,KACc;AACtB,QAAMQ,IAAQC,EAAOF,CAAE,GACjBG,IAAQD,EAAsB,IAAI;AACxC,SAAAL,EAAU,MAAM;AACd,IAAAI,EAAM,UAAUD;AAAA,EAClB,GAAG,CAACA,CAAE,CAAC,GACPH;AAAA,IACE,MAAM,MAAM;AACV,MAAIM,EAAM,YAAY,QAAM,OAAO,aAAaA,EAAM,OAAO;AAAA,IAC/D;AAAA,IACA,CAAA;AAAA,EAAC,GAEIC;AAAA,IACL,IAAIC,MAAY;AACd,MAAIF,EAAM,YAAY,QAAM,OAAO,aAAaA,EAAM,OAAO,GAC7DA,EAAM,UAAU,OAAO,WAAW,MAAM;AACtC,QAAAA,EAAM,UAAU,MAChBF,EAAM,QAAQ,GAAGI,CAAI;AAAA,MACvB,GAAGZ,CAAK;AAAA,IACV;AAAA,IACA,CAACA,CAAK;AAAA,EAAA;AAEV;"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export interface DelayedLoadingOptions {
|
|
2
|
+
/**
|
|
3
|
+
* Once `true`, the value holds for at least this many ms even if `ms`
|
|
4
|
+
* changes, so a placeholder that did appear never flickers away. Default 500.
|
|
5
|
+
*/
|
|
6
|
+
minVisible?: number;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Returns `false` until `ms` has elapsed, then `true`. Gate skeletons and
|
|
10
|
+
* spinners behind it so sub-300ms loads never flash a placeholder.
|
|
11
|
+
* `ms <= 0` returns `true` immediately (and synchronously on first render).
|
|
12
|
+
*
|
|
13
|
+
* Once shown, the value stays `true` for `minVisible` ms (default 500). Keep
|
|
14
|
+
* the gated component mounted for that window on the consumer side too —
|
|
15
|
+
* unmounting it early defeats the hold.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* const showSkeleton = useDelayedLoading(300);
|
|
19
|
+
* if (isLoading) return showSkeleton ? <Skeleton /> : null;
|
|
20
|
+
*/
|
|
21
|
+
export declare function useDelayedLoading(ms: number, { minVisible }?: DelayedLoadingOptions): boolean;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useState as f, useRef as i, useEffect as l } from "react";
|
|
3
|
+
function d(t, { minVisible: u = 500 } = {}) {
|
|
4
|
+
const [a, n] = f(t <= 0), e = i(t <= 0 ? Date.now() : null);
|
|
5
|
+
return l(() => {
|
|
6
|
+
const c = () => {
|
|
7
|
+
e.current = Date.now(), n(!0);
|
|
8
|
+
};
|
|
9
|
+
if (t <= 0) {
|
|
10
|
+
e.current ?? (e.current = Date.now()), n(!0);
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
const r = e.current === null ? 0 : Math.max(0, u - (Date.now() - e.current)), o = [];
|
|
14
|
+
return r > 0 ? o.push(window.setTimeout(() => n(!1), r)) : n(!1), o.push(window.setTimeout(c, r + t)), () => o.forEach((s) => window.clearTimeout(s));
|
|
15
|
+
}, [t, u]), a;
|
|
16
|
+
}
|
|
17
|
+
export {
|
|
18
|
+
d as useDelayedLoading
|
|
19
|
+
};
|
|
20
|
+
//# sourceMappingURL=use-delayed-loading.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-delayed-loading.js","sources":["../../src/hooks/use-delayed-loading.ts"],"sourcesContent":["'use client';\n\nimport { useEffect, useRef, useState } from 'react';\n\nexport interface DelayedLoadingOptions {\n /**\n * Once `true`, the value holds for at least this many ms even if `ms`\n * changes, so a placeholder that did appear never flickers away. Default 500.\n */\n minVisible?: number;\n}\n\n/**\n * Returns `false` until `ms` has elapsed, then `true`. Gate skeletons and\n * spinners behind it so sub-300ms loads never flash a placeholder.\n * `ms <= 0` returns `true` immediately (and synchronously on first render).\n *\n * Once shown, the value stays `true` for `minVisible` ms (default 500). Keep\n * the gated component mounted for that window on the consumer side too —\n * unmounting it early defeats the hold.\n *\n * @example\n * const showSkeleton = useDelayedLoading(300);\n * if (isLoading) return showSkeleton ? <Skeleton /> : null;\n */\nexport function useDelayedLoading(\n ms: number,\n { minVisible = 500 }: DelayedLoadingOptions = {},\n): boolean {\n const [ready, setReady] = useState(ms <= 0);\n const shownAt = useRef<number | null>(ms <= 0 ? Date.now() : null);\n\n useEffect(() => {\n const show = () => {\n shownAt.current = Date.now();\n setReady(true);\n };\n if (ms <= 0) {\n shownAt.current ??= Date.now();\n setReady(true);\n return;\n }\n // Respect the minimum-visible window before hiding again.\n const hold =\n shownAt.current === null ? 0 : Math.max(0, minVisible - (Date.now() - shownAt.current));\n const timers: number[] = [];\n if (hold > 0) {\n timers.push(window.setTimeout(() => setReady(false), hold));\n } else {\n setReady(false);\n }\n timers.push(window.setTimeout(show, hold + ms));\n return () => timers.forEach((t) => window.clearTimeout(t));\n }, [ms, minVisible]);\n\n return ready;\n}\n"],"names":["useDelayedLoading","ms","minVisible","ready","setReady","useState","shownAt","useRef","useEffect","show","hold","timers","t"],"mappings":";;AAyBO,SAASA,EACdC,GACA,EAAE,YAAAC,IAAa,IAAA,IAA+B,CAAA,GACrC;AACT,QAAM,CAACC,GAAOC,CAAQ,IAAIC,EAASJ,KAAM,CAAC,GACpCK,IAAUC,EAAsBN,KAAM,IAAI,KAAK,IAAA,IAAQ,IAAI;AAEjE,SAAAO,EAAU,MAAM;AACd,UAAMC,IAAO,MAAM;AACjB,MAAAH,EAAQ,UAAU,KAAK,IAAA,GACvBF,EAAS,EAAI;AAAA,IACf;AACA,QAAIH,KAAM,GAAG;AACX,MAAAK,EAAQ,YAARA,EAAQ,UAAY,KAAK,IAAA,IACzBF,EAAS,EAAI;AACb;AAAA,IACF;AAEA,UAAMM,IACJJ,EAAQ,YAAY,OAAO,IAAI,KAAK,IAAI,GAAGJ,KAAc,KAAK,IAAA,IAAQI,EAAQ,QAAQ,GAClFK,IAAmB,CAAA;AACzB,WAAID,IAAO,IACTC,EAAO,KAAK,OAAO,WAAW,MAAMP,EAAS,EAAK,GAAGM,CAAI,CAAC,IAE1DN,EAAS,EAAK,GAEhBO,EAAO,KAAK,OAAO,WAAWF,GAAMC,IAAOT,CAAE,CAAC,GACvC,MAAMU,EAAO,QAAQ,CAACC,MAAM,OAAO,aAAaA,CAAC,CAAC;AAAA,EAC3D,GAAG,CAACX,GAAIC,CAAU,CAAC,GAEZC;AACT;"}
|
|
@@ -0,0 +1,30 @@
|
|
|
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 (`<fieldId>-desc`). */
|
|
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
|
+
/** Consumer-supplied `aria-describedby` ids, appended after the helper / error id. */
|
|
29
|
+
extra?: string;
|
|
30
|
+
}): FieldIds;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useId as n } from "react";
|
|
3
|
+
function l(s, o = {}) {
|
|
4
|
+
const i = n(), r = s ?? i, e = `${r}-desc`, d = `${r}-error`, t = [
|
|
5
|
+
o.hasError ? d : o.hasHelper ? e : void 0,
|
|
6
|
+
o.extra
|
|
7
|
+
].filter(Boolean);
|
|
8
|
+
return {
|
|
9
|
+
fieldId: r,
|
|
10
|
+
helperId: e,
|
|
11
|
+
errorId: d,
|
|
12
|
+
describedBy: t.length ? t.join(" ") : void 0
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
export {
|
|
16
|
+
l 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 (`<fieldId>-desc`). */\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: {\n hasHelper?: boolean;\n hasError?: boolean;\n /** Consumer-supplied `aria-describedby` ids, appended after the helper / error id. */\n extra?: string;\n } = {},\n): FieldIds {\n const autoId = useId();\n const fieldId = id ?? autoId;\n const helperId = `${fieldId}-desc`;\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,IAKI,IACM;AACV,QAAMC,IAASC,EAAA,GACTC,IAAUJ,KAAME,GAChBG,IAAW,GAAGD,CAAO,SACrBE,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;"}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Subscribe to a CSS media query. SSR-
|
|
2
|
+
* Subscribe to a CSS media query. SSR- and hydration-safe: the server snapshot
|
|
3
|
+
* is always `false`, and the client re-renders with the real value after
|
|
4
|
+
* hydration instead of producing a markup mismatch.
|
|
3
5
|
*
|
|
4
6
|
* @example
|
|
5
7
|
* const isDesktop = useMediaQuery('(min-width: 1024px)');
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useCallback as o, useSyncExternalStore as d } from "react";
|
|
3
|
+
function r(e) {
|
|
4
|
+
const i = o(
|
|
5
|
+
(n) => {
|
|
6
|
+
if (typeof window > "u" || !window.matchMedia) return () => {
|
|
7
|
+
};
|
|
8
|
+
const t = window.matchMedia(e);
|
|
9
|
+
return t.addEventListener("change", n), () => t.removeEventListener("change", n);
|
|
10
|
+
},
|
|
11
|
+
[e]
|
|
12
|
+
);
|
|
13
|
+
return d(i, () => typeof window < "u" && window.matchMedia ? window.matchMedia(e).matches : !1, () => !1);
|
|
14
|
+
}
|
|
15
|
+
function c() {
|
|
16
|
+
return r("(prefers-reduced-motion: reduce)");
|
|
17
|
+
}
|
|
18
|
+
export {
|
|
19
|
+
r as useMediaQuery,
|
|
20
|
+
c as usePrefersReducedMotion
|
|
21
|
+
};
|
|
22
|
+
//# 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 { useCallback, useSyncExternalStore } from 'react';\n\n/**\n * Subscribe to a CSS media query. SSR- and hydration-safe: the server snapshot\n * is always `false`, and the client re-renders with the real value after\n * hydration instead of producing a markup mismatch.\n *\n * @example\n * const isDesktop = useMediaQuery('(min-width: 1024px)');\n */\nexport function useMediaQuery(query: string): boolean {\n const subscribe = useCallback(\n (onChange: () => void) => {\n if (typeof window === 'undefined' || !window.matchMedia) return () => {};\n const list = window.matchMedia(query);\n list.addEventListener('change', onChange);\n return () => list.removeEventListener('change', onChange);\n },\n [query],\n );\n const getSnapshot = () =>\n typeof window !== 'undefined' && window.matchMedia ? window.matchMedia(query).matches : false;\n return useSyncExternalStore(subscribe, getSnapshot, () => false);\n}\n\n/** Convenience: respect `prefers-reduced-motion`. */\nexport function usePrefersReducedMotion(): boolean {\n return useMediaQuery('(prefers-reduced-motion: reduce)');\n}\n"],"names":["useMediaQuery","query","subscribe","useCallback","onChange","list","useSyncExternalStore","usePrefersReducedMotion"],"mappings":";;AAYO,SAASA,EAAcC,GAAwB;AACpD,QAAMC,IAAYC;AAAA,IAChB,CAACC,MAAyB;AACxB,UAAI,OAAO,SAAW,OAAe,CAAC,OAAO,mBAAmB,MAAM;AAAA,MAAC;AACvE,YAAMC,IAAO,OAAO,WAAWJ,CAAK;AACpC,aAAAI,EAAK,iBAAiB,UAAUD,CAAQ,GACjC,MAAMC,EAAK,oBAAoB,UAAUD,CAAQ;AAAA,IAC1D;AAAA,IACA,CAACH,CAAK;AAAA,EAAA;AAIR,SAAOK,EAAqBJ,GAFR,MAClB,OAAO,SAAW,OAAe,OAAO,aAAa,OAAO,WAAWD,CAAK,EAAE,UAAU,IACtC,MAAM,EAAK;AACjE;AAGO,SAASM,IAAmC;AACjD,SAAOP,EAAc,kCAAkC;AACzD;"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** True on macOS / iOS / iPadOS, where the command modifier is ⌘. */
|
|
2
|
+
export declare function isApplePlatform(): boolean;
|
|
3
|
+
export interface ModifierKey {
|
|
4
|
+
/** Glyph for `<Kbd>`: `⌘` on Apple platforms, `Ctrl` elsewhere. */
|
|
5
|
+
symbol: '⌘' | 'Ctrl';
|
|
6
|
+
/** Spoken/long form for aria-labels and tooltips: `Cmd` / `Ctrl`. */
|
|
7
|
+
label: 'Cmd' | 'Ctrl';
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Platform-correct command modifier for shortcut hints (⌘K vs Ctrl K).
|
|
11
|
+
* Defaults to Ctrl until mounted so server output is deterministic.
|
|
12
|
+
*/
|
|
13
|
+
export declare function useModifierKey(): ModifierKey;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useState as o, useEffect as r } from "react";
|
|
3
|
+
function n() {
|
|
4
|
+
var a;
|
|
5
|
+
if (typeof navigator > "u") return !1;
|
|
6
|
+
const e = navigator, t = ((a = e.userAgentData) == null ? void 0 : a.platform) ?? e.platform ?? "";
|
|
7
|
+
return /mac|iphone|ipad|ipod/i.test(t) || /Mac OS X/i.test(e.userAgent ?? "");
|
|
8
|
+
}
|
|
9
|
+
function s() {
|
|
10
|
+
const [e, t] = o(!1);
|
|
11
|
+
return r(() => {
|
|
12
|
+
t(n());
|
|
13
|
+
}, []), e ? { symbol: "⌘", label: "Cmd" } : { symbol: "Ctrl", label: "Ctrl" };
|
|
14
|
+
}
|
|
15
|
+
export {
|
|
16
|
+
n as isApplePlatform,
|
|
17
|
+
s as useModifierKey
|
|
18
|
+
};
|
|
19
|
+
//# sourceMappingURL=use-modifier-key.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-modifier-key.js","sources":["../../src/hooks/use-modifier-key.ts"],"sourcesContent":["'use client';\n\nimport { useEffect, useState } from 'react';\n\n/** True on macOS / iOS / iPadOS, where the command modifier is ⌘. */\nexport function isApplePlatform(): boolean {\n if (typeof navigator === 'undefined') return false;\n const nav = navigator as Navigator & { userAgentData?: { platform?: string } };\n const platform = nav.userAgentData?.platform ?? nav.platform ?? '';\n return /mac|iphone|ipad|ipod/i.test(platform) || /Mac OS X/i.test(nav.userAgent ?? '');\n}\n\nexport interface ModifierKey {\n /** Glyph for `<Kbd>`: `⌘` on Apple platforms, `Ctrl` elsewhere. */\n symbol: '⌘' | 'Ctrl';\n /** Spoken/long form for aria-labels and tooltips: `Cmd` / `Ctrl`. */\n label: 'Cmd' | 'Ctrl';\n}\n\n/**\n * Platform-correct command modifier for shortcut hints (⌘K vs Ctrl K).\n * Defaults to Ctrl until mounted so server output is deterministic.\n */\nexport function useModifierKey(): ModifierKey {\n const [apple, setApple] = useState(false);\n useEffect(() => {\n setApple(isApplePlatform());\n }, []);\n return apple ? { symbol: '⌘', label: 'Cmd' } : { symbol: 'Ctrl', label: 'Ctrl' };\n}\n"],"names":["isApplePlatform","nav","platform","_a","useModifierKey","apple","setApple","useState","useEffect"],"mappings":";;AAKO,SAASA,IAA2B;;AACzC,MAAI,OAAO,YAAc,IAAa,QAAO;AAC7C,QAAMC,IAAM,WACNC,MAAWC,IAAAF,EAAI,kBAAJ,gBAAAE,EAAmB,aAAYF,EAAI,YAAY;AAChE,SAAO,wBAAwB,KAAKC,CAAQ,KAAK,YAAY,KAAKD,EAAI,aAAa,EAAE;AACvF;AAaO,SAASG,IAA8B;AAC5C,QAAM,CAACC,GAAOC,CAAQ,IAAIC,EAAS,EAAK;AACxC,SAAAC,EAAU,MAAM;AACd,IAAAF,EAASN,GAAiB;AAAA,EAC5B,GAAG,CAAA,CAAE,GACEK,IAAQ,EAAE,QAAQ,KAAK,OAAO,MAAA,IAAU,EAAE,QAAQ,QAAQ,OAAO,OAAA;AAC1E;"}
|
|
@@ -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;
|