@evinvest/uikit 0.19.0 → 0.20.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 +17 -5
- package/dist/components/accordion.js +136 -0
- package/dist/components/accordion.js.map +1 -0
- package/dist/components/alert-dialog.js +175 -0
- package/dist/components/alert-dialog.js.map +1 -0
- package/dist/components/alert.js +49 -0
- package/dist/components/alert.js.map +1 -0
- package/dist/components/avatar.js +54 -0
- package/dist/components/avatar.js.map +1 -0
- package/dist/components/badge.js +25 -0
- package/dist/components/badge.js.map +1 -0
- package/dist/components/band.js +75 -0
- package/dist/components/band.js.map +1 -0
- package/dist/components/breadcrumb.js +132 -0
- package/dist/components/breadcrumb.js.map +1 -0
- package/dist/components/button-group.js +61 -0
- package/dist/components/button-group.js.map +1 -0
- package/dist/components/button.js +55 -0
- package/dist/components/button.js.map +1 -0
- package/dist/components/calendar.js +226 -0
- package/dist/components/calendar.js.map +1 -0
- package/dist/components/card.js +98 -0
- package/dist/components/card.js.map +1 -0
- package/dist/components/carousel.js +262 -0
- package/dist/components/carousel.js.map +1 -0
- package/dist/components/chart.js +151 -0
- package/dist/components/chart.js.map +1 -0
- package/dist/components/checkbox.js +68 -0
- package/dist/components/checkbox.js.map +1 -0
- package/dist/components/collapsible.js +60 -0
- package/dist/components/collapsible.js.map +1 -0
- package/dist/components/command.js +274 -0
- package/dist/components/command.js.map +1 -0
- package/dist/components/container.js +18 -0
- package/dist/components/container.js.map +1 -0
- package/dist/components/context-menu.js +337 -0
- package/dist/components/context-menu.js.map +1 -0
- package/dist/components/date-time-picker.js +301 -0
- package/dist/components/date-time-picker.js.map +1 -0
- package/dist/components/dialog.js +207 -0
- package/dist/components/dialog.js.map +1 -0
- package/dist/components/drawer.js +351 -0
- package/dist/components/drawer.js.map +1 -0
- package/dist/components/dropdown-menu.js +356 -0
- package/dist/components/dropdown-menu.js.map +1 -0
- package/dist/components/empty.js +91 -0
- package/dist/components/empty.js.map +1 -0
- package/dist/components/field-context.js +30 -0
- package/dist/components/field-context.js.map +1 -0
- package/dist/components/field.js +216 -0
- package/dist/components/field.js.map +1 -0
- package/dist/components/footer.js +89 -0
- package/dist/components/footer.js.map +1 -0
- package/dist/components/form.js +101 -0
- package/dist/components/form.js.map +1 -0
- package/dist/components/hover-card.js +90 -0
- package/dist/components/hover-card.js.map +1 -0
- package/dist/components/info-tip.js +161 -0
- package/dist/components/info-tip.js.map +1 -0
- package/dist/components/input-group.js +124 -0
- package/dist/components/input-group.js.map +1 -0
- package/dist/components/input-otp.js +120 -0
- package/dist/components/input-otp.js.map +1 -0
- package/dist/components/input.js +26 -0
- package/dist/components/input.js.map +1 -0
- package/dist/components/item.js +167 -0
- package/dist/components/item.js.map +1 -0
- package/dist/components/kbd.js +29 -0
- package/dist/components/kbd.js.map +1 -0
- package/dist/components/label.js +11 -0
- package/dist/components/label.js.map +1 -0
- package/dist/components/logo.js +41 -0
- package/dist/components/logo.js.map +1 -0
- package/dist/components/menubar.js +330 -0
- package/dist/components/menubar.js.map +1 -0
- package/dist/components/native-select.js +62 -0
- package/dist/components/native-select.js.map +1 -0
- package/dist/components/navigation-menu.js +204 -0
- package/dist/components/navigation-menu.js.map +1 -0
- package/dist/components/pagination.js +138 -0
- package/dist/components/pagination.js.map +1 -0
- package/dist/components/popover.js +104 -0
- package/dist/components/popover.js.map +1 -0
- package/dist/components/progress.js +30 -0
- package/dist/components/progress.js.map +1 -0
- package/dist/components/radio-group.js +113 -0
- package/dist/components/radio-group.js.map +1 -0
- package/dist/components/resizable.js +193 -0
- package/dist/components/resizable.js.map +1 -0
- package/dist/components/scroll-area.js +40 -0
- package/dist/components/scroll-area.js.map +1 -0
- package/dist/components/section-descriptor.js +88 -0
- package/dist/components/section-descriptor.js.map +1 -0
- package/dist/components/select-chevron.js +25 -0
- package/dist/components/select-chevron.js.map +1 -0
- package/dist/components/select.js +288 -0
- package/dist/components/select.js.map +1 -0
- package/dist/components/separator.js +31 -0
- package/dist/components/separator.js.map +1 -0
- package/dist/components/sheet.js +200 -0
- package/dist/components/sheet.js.map +1 -0
- package/dist/components/sidebar.js +326 -0
- package/dist/components/sidebar.js.map +1 -0
- package/dist/components/skeleton.js +18 -0
- package/dist/components/skeleton.js.map +1 -0
- package/dist/components/slider.js +138 -0
- package/dist/components/slider.js.map +1 -0
- package/dist/components/sonner.js +319 -0
- package/dist/components/sonner.js.map +1 -0
- package/dist/components/spinner.js +23 -0
- package/dist/components/spinner.js.map +1 -0
- package/dist/components/status-screen.js +145 -0
- package/dist/components/status-screen.js.map +1 -0
- package/dist/components/switch.js +54 -0
- package/dist/components/switch.js.map +1 -0
- package/dist/components/table.js +124 -0
- package/dist/components/table.js.map +1 -0
- package/dist/components/tabs.js +133 -0
- package/dist/components/tabs.js.map +1 -0
- package/dist/components/terminal.js +206 -0
- package/dist/components/terminal.js.map +1 -0
- package/dist/components/textarea.js +22 -0
- package/dist/components/textarea.js.map +1 -0
- package/dist/components/toggle-group.js +105 -0
- package/dist/components/toggle-group.js.map +1 -0
- package/dist/components/toggle.js +53 -0
- package/dist/components/toggle.js.map +1 -0
- package/dist/components/tooltip.js +102 -0
- package/dist/components/tooltip.js.map +1 -0
- package/dist/generated/accent.js +27 -0
- package/dist/generated/accent.js.map +1 -0
- package/dist/generated/accordion.js +13 -0
- package/dist/generated/accordion.js.map +1 -0
- package/dist/generated/alert-dialog.js +15 -0
- package/dist/generated/alert-dialog.js.map +1 -0
- package/dist/generated/alert.js +16 -0
- package/dist/generated/alert.js.map +1 -0
- package/dist/generated/avatar.js +9 -0
- package/dist/generated/avatar.js.map +1 -0
- package/dist/generated/badge.js +13 -0
- package/dist/generated/badge.js.map +1 -0
- package/dist/generated/band.js +39 -0
- package/dist/generated/band.js.map +1 -0
- package/dist/generated/breadcrumb.js +15 -0
- package/dist/generated/breadcrumb.js.map +1 -0
- package/dist/generated/button-group.js +14 -0
- package/dist/generated/button-group.js.map +1 -0
- package/dist/generated/button.js +30 -0
- package/dist/generated/button.js.map +1 -0
- package/dist/generated/calendar.js +29 -0
- package/dist/generated/calendar.js.map +1 -0
- package/dist/generated/card.js +17 -0
- package/dist/generated/card.js.map +1 -0
- package/dist/generated/carousel.js +31 -0
- package/dist/generated/carousel.js.map +1 -0
- package/dist/generated/chart.js +9 -0
- package/dist/generated/chart.js.map +1 -0
- package/dist/generated/checkbox.js +7 -0
- package/dist/generated/checkbox.js.map +1 -0
- package/dist/generated/command.js +27 -0
- package/dist/generated/command.js.map +1 -0
- package/dist/generated/container.js +5 -0
- package/dist/generated/container.js.map +1 -0
- package/dist/generated/context-menu.js +19 -0
- package/dist/generated/context-menu.js.map +1 -0
- package/dist/generated/date-time-picker.js +15 -0
- package/dist/generated/date-time-picker.js.map +1 -0
- package/dist/generated/dialog.js +17 -0
- package/dist/generated/dialog.js.map +1 -0
- package/dist/generated/drawer.js +26 -0
- package/dist/generated/drawer.js.map +1 -0
- package/dist/generated/dropdown-menu.js +21 -0
- package/dist/generated/dropdown-menu.js.map +1 -0
- package/dist/generated/empty.js +20 -0
- package/dist/generated/empty.js.map +1 -0
- package/dist/generated/field.js +33 -0
- package/dist/generated/field.js.map +1 -0
- package/dist/generated/focus.js +5 -0
- package/dist/generated/focus.js.map +1 -0
- package/dist/generated/form.js +11 -0
- package/dist/generated/form.js.map +1 -0
- package/dist/generated/hover-card.js +5 -0
- package/dist/generated/hover-card.js.map +1 -0
- package/dist/generated/input-group.js +29 -0
- package/dist/generated/input-group.js.map +1 -0
- package/dist/generated/input-otp.js +15 -0
- package/dist/generated/input-otp.js.map +1 -0
- package/dist/generated/input.js +11 -0
- package/dist/generated/input.js.map +1 -0
- package/dist/generated/item.js +40 -0
- package/dist/generated/item.js.map +1 -0
- package/dist/generated/kbd.js +7 -0
- package/dist/generated/kbd.js.map +1 -0
- package/dist/generated/label.js +5 -0
- package/dist/generated/label.js.map +1 -0
- package/dist/generated/menubar.js +27 -0
- package/dist/generated/menubar.js.map +1 -0
- package/dist/generated/navigation-menu.js +19 -0
- package/dist/generated/navigation-menu.js.map +1 -0
- package/dist/generated/popover.js +5 -0
- package/dist/generated/popover.js.map +1 -0
- package/dist/generated/progress.js +7 -0
- package/dist/generated/progress.js.map +1 -0
- package/dist/generated/radio-group.js +7 -0
- package/dist/generated/radio-group.js.map +1 -0
- package/dist/generated/resizable.js +11 -0
- package/dist/generated/resizable.js.map +1 -0
- package/dist/generated/scroll-area.js +14 -0
- package/dist/generated/scroll-area.js.map +1 -0
- package/dist/generated/select.js +15 -0
- package/dist/generated/select.js.map +1 -0
- package/dist/generated/separator.js +10 -0
- package/dist/generated/separator.js.map +1 -0
- package/dist/generated/sheet.js +25 -0
- package/dist/generated/sheet.js.map +1 -0
- package/dist/generated/sidebar.js +41 -0
- package/dist/generated/sidebar.js.map +1 -0
- package/dist/generated/skeleton.js +5 -0
- package/dist/generated/skeleton.js.map +1 -0
- package/dist/generated/slider.js +11 -0
- package/dist/generated/slider.js.map +1 -0
- package/dist/generated/sonner.js +30 -0
- package/dist/generated/sonner.js.map +1 -0
- package/dist/generated/switch.js +7 -0
- package/dist/generated/switch.js.map +1 -0
- package/dist/generated/table.js +21 -0
- package/dist/generated/table.js.map +1 -0
- package/dist/generated/tabs.js +11 -0
- package/dist/generated/tabs.js.map +1 -0
- package/dist/generated/terminal.js +70 -0
- package/dist/generated/terminal.js.map +1 -0
- package/dist/generated/textarea.js +11 -0
- package/dist/generated/textarea.js.map +1 -0
- package/dist/generated/toggle.js +18 -0
- package/dist/generated/toggle.js.map +1 -0
- package/dist/generated/tooltip.js +5 -0
- package/dist/generated/tooltip.js.map +1 -0
- package/dist/index.d.ts +43 -10
- package/dist/index.js +73 -8805
- package/dist/lib/cn.js +12 -0
- package/dist/lib/cn.js.map +1 -0
- package/dist/lib/focus.js +5 -0
- package/dist/lib/focus.js.map +1 -0
- package/dist/primitives/dismissable-layer.js +35 -0
- package/dist/primitives/dismissable-layer.js.map +1 -0
- package/dist/primitives/focus-scope.js +58 -0
- package/dist/primitives/focus-scope.js.map +1 -0
- package/dist/primitives/merge-refs.js +13 -0
- package/dist/primitives/merge-refs.js.map +1 -0
- package/dist/primitives/portal.js +19 -0
- package/dist/primitives/portal.js.map +1 -0
- package/dist/primitives/presence.js +45 -0
- package/dist/primitives/presence.js.map +1 -0
- package/dist/primitives/slot.js +39 -0
- package/dist/primitives/slot.js.map +1 -0
- package/dist/primitives/use-controllable-state.js +24 -0
- package/dist/primitives/use-controllable-state.js.map +1 -0
- package/dist/primitives/use-floating.js +128 -0
- package/dist/primitives/use-floating.js.map +1 -0
- package/dist/primitives/use-hover-intent.js +50 -0
- package/dist/primitives/use-hover-intent.js.map +1 -0
- package/dist/primitives/use-roving-focus.js +40 -0
- package/dist/primitives/use-roving-focus.js.map +1 -0
- package/dist/primitives/walk-elements.js +13 -0
- package/dist/primitives/walk-elements.js.map +1 -0
- package/package.json +5 -4
- package/dist/index.js.map +0 -1
package/dist/lib/cn.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { clsx } from "clsx";
|
|
2
|
+
import { twMerge } from "tailwind-merge";
|
|
3
|
+
function cn(...inputs) {
|
|
4
|
+
if (inputs.length === 2 && typeof inputs[0] === "string" && !inputs[1]) {
|
|
5
|
+
return inputs[0];
|
|
6
|
+
}
|
|
7
|
+
return twMerge(clsx(inputs));
|
|
8
|
+
}
|
|
9
|
+
export {
|
|
10
|
+
cn
|
|
11
|
+
};
|
|
12
|
+
//# sourceMappingURL=cn.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/lib/cn.ts"],"sourcesContent":["import { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\n/**\n * Fuses class fragments into one string with real Tailwind conflict resolution:\n * `clsx` drops falsy fragments and joins the rest, `tailwind-merge` resolves\n * conflicting utilities so the rightmost wins (`cn(\"p-4\", \"p-2\") === \"p-2\"`). A\n * caller's `className` override, passed last, therefore beats the base classes.\n *\n * `twMerge` parses every utility on every render, and the overwhelmingly common\n * call is one kit constant plus a `className` override that is absent — so that\n * call returns the constant untouched.\n *\n * The Rust mirror is the `cn!` macro in `ev_lib::uikit` (`tailwind_fuse::tw_merge!`).\n */\nexport function cn(...inputs: ClassValue[]): string {\n if (inputs.length === 2 && typeof inputs[0] === \"string\" && !inputs[1]) {\n return inputs[0];\n }\n return twMerge(clsx(inputs));\n}\n"],"mappings":"AAAA,SAAS,YAA6B;AACtC,SAAS,eAAe;AAcjB,SAAS,MAAM,QAA8B;AAClD,MAAI,OAAO,WAAW,KAAK,OAAO,OAAO,CAAC,MAAM,YAAY,CAAC,OAAO,CAAC,GAAG;AACtE,WAAO,OAAO,CAAC;AAAA,EACjB;AACA,SAAO,QAAQ,KAAK,MAAM,CAAC;AAC7B;","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/lib/focus.ts"],"sourcesContent":["/**\n * The focus indicator for a control that paints a fill (lib#129): the solid\n * ring standing 2px off the control instead of the kit's 50 % halo. The class\n * tables that paint a fill already carry it (`rust/classes/src/focus.rs` says\n * why); this is for a filled control of the caller's own.\n */\nexport { FILLED_FOCUS_RING } from \"../generated/focus\";\n"],"mappings":"AAMA,SAAS,yBAAyB;","names":[]}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
function useDismissableLayer(opts) {
|
|
4
|
+
const { enabled, onDismiss, exclude } = opts;
|
|
5
|
+
const ref = React.useRef(null);
|
|
6
|
+
const onDismissRef = React.useRef(onDismiss);
|
|
7
|
+
React.useEffect(() => {
|
|
8
|
+
onDismissRef.current = onDismiss;
|
|
9
|
+
});
|
|
10
|
+
React.useEffect(() => {
|
|
11
|
+
if (!enabled) return;
|
|
12
|
+
function onPointerDown(event) {
|
|
13
|
+
const target = event.target;
|
|
14
|
+
if (ref.current && target && ref.current.contains(target)) return;
|
|
15
|
+
for (const r of exclude ?? []) {
|
|
16
|
+
if (r.current && target && r.current.contains(target)) return;
|
|
17
|
+
}
|
|
18
|
+
onDismissRef.current();
|
|
19
|
+
}
|
|
20
|
+
function onKeyDown(event) {
|
|
21
|
+
if (event.key === "Escape") onDismissRef.current();
|
|
22
|
+
}
|
|
23
|
+
document.addEventListener("pointerdown", onPointerDown, true);
|
|
24
|
+
document.addEventListener("keydown", onKeyDown);
|
|
25
|
+
return () => {
|
|
26
|
+
document.removeEventListener("pointerdown", onPointerDown, true);
|
|
27
|
+
document.removeEventListener("keydown", onKeyDown);
|
|
28
|
+
};
|
|
29
|
+
}, [enabled, exclude]);
|
|
30
|
+
return ref;
|
|
31
|
+
}
|
|
32
|
+
export {
|
|
33
|
+
useDismissableLayer
|
|
34
|
+
};
|
|
35
|
+
//# sourceMappingURL=dismissable-layer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/primitives/dismissable-layer.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\n\n/**\n * Calls `onDismiss` on Escape or on a pointer-down outside the layer — the\n * dep-light core of every overlay's \"click away to close\". Pass refs to any\n * nodes that should NOT count as outside (e.g. the trigger), via `exclude`.\n *\n * Mirrors the dismiss behaviour Rust expresses with a full-screen backdrop.\n */\nexport function useDismissableLayer(opts: {\n enabled: boolean;\n onDismiss: () => void;\n exclude?: Array<React.RefObject<Element | null>>;\n}): React.RefObject<HTMLDivElement | null> {\n const { enabled, onDismiss, exclude } = opts;\n const ref = React.useRef<HTMLDivElement | null>(null);\n const onDismissRef = React.useRef(onDismiss);\n React.useEffect(() => {\n onDismissRef.current = onDismiss;\n });\n\n React.useEffect(() => {\n if (!enabled) return;\n\n function onPointerDown(event: PointerEvent) {\n const target = event.target as Node | null;\n if (ref.current && target && ref.current.contains(target)) return;\n for (const r of exclude ?? []) {\n if (r.current && target && r.current.contains(target)) return;\n }\n onDismissRef.current();\n }\n\n function onKeyDown(event: KeyboardEvent) {\n if (event.key === \"Escape\") onDismissRef.current();\n }\n\n document.addEventListener(\"pointerdown\", onPointerDown, true);\n document.addEventListener(\"keydown\", onKeyDown);\n return () => {\n document.removeEventListener(\"pointerdown\", onPointerDown, true);\n document.removeEventListener(\"keydown\", onKeyDown);\n };\n }, [enabled, exclude]);\n\n return ref;\n}\n"],"mappings":";AAEA,YAAY,WAAW;AAShB,SAAS,oBAAoB,MAIO;AACzC,QAAM,EAAE,SAAS,WAAW,QAAQ,IAAI;AACxC,QAAM,MAAM,MAAM,OAA8B,IAAI;AACpD,QAAM,eAAe,MAAM,OAAO,SAAS;AAC3C,QAAM,UAAU,MAAM;AACpB,iBAAa,UAAU;AAAA,EACzB,CAAC;AAED,QAAM,UAAU,MAAM;AACpB,QAAI,CAAC,QAAS;AAEd,aAAS,cAAc,OAAqB;AAC1C,YAAM,SAAS,MAAM;AACrB,UAAI,IAAI,WAAW,UAAU,IAAI,QAAQ,SAAS,MAAM,EAAG;AAC3D,iBAAW,KAAK,WAAW,CAAC,GAAG;AAC7B,YAAI,EAAE,WAAW,UAAU,EAAE,QAAQ,SAAS,MAAM,EAAG;AAAA,MACzD;AACA,mBAAa,QAAQ;AAAA,IACvB;AAEA,aAAS,UAAU,OAAsB;AACvC,UAAI,MAAM,QAAQ,SAAU,cAAa,QAAQ;AAAA,IACnD;AAEA,aAAS,iBAAiB,eAAe,eAAe,IAAI;AAC5D,aAAS,iBAAiB,WAAW,SAAS;AAC9C,WAAO,MAAM;AACX,eAAS,oBAAoB,eAAe,eAAe,IAAI;AAC/D,eAAS,oBAAoB,WAAW,SAAS;AAAA,IACnD;AAAA,EACF,GAAG,CAAC,SAAS,OAAO,CAAC;AAErB,SAAO;AACT;","names":[]}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
const FOCUSABLE = [
|
|
4
|
+
"a[href]",
|
|
5
|
+
"button:not([disabled])",
|
|
6
|
+
"textarea:not([disabled])",
|
|
7
|
+
"input:not([disabled])",
|
|
8
|
+
"select:not([disabled])",
|
|
9
|
+
"[tabindex]"
|
|
10
|
+
].map((sel) => `${sel}:not([tabindex="-1"])`).join(",");
|
|
11
|
+
function focusCandidates(root) {
|
|
12
|
+
return Array.from(root.querySelectorAll(FOCUSABLE));
|
|
13
|
+
}
|
|
14
|
+
function focusable(root) {
|
|
15
|
+
return focusCandidates(root).filter(
|
|
16
|
+
(el) => el.offsetWidth > 0 || el.offsetHeight > 0 || el === document.activeElement
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
function useFocusScope(enabled) {
|
|
20
|
+
const ref = React.useRef(null);
|
|
21
|
+
React.useEffect(() => {
|
|
22
|
+
if (!enabled) return;
|
|
23
|
+
const root = ref.current;
|
|
24
|
+
if (!root) return;
|
|
25
|
+
const previouslyFocused = document.activeElement;
|
|
26
|
+
const first = focusable(root)[0];
|
|
27
|
+
(first ?? root).focus();
|
|
28
|
+
function onKeyDown(event) {
|
|
29
|
+
if (event.key !== "Tab" || !root) return;
|
|
30
|
+
const items = focusable(root);
|
|
31
|
+
if (items.length === 0) {
|
|
32
|
+
event.preventDefault();
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
const firstEl = items[0];
|
|
36
|
+
const lastEl = items[items.length - 1];
|
|
37
|
+
const active = document.activeElement;
|
|
38
|
+
if (event.shiftKey && active === firstEl) {
|
|
39
|
+
event.preventDefault();
|
|
40
|
+
lastEl.focus();
|
|
41
|
+
} else if (!event.shiftKey && active === lastEl) {
|
|
42
|
+
event.preventDefault();
|
|
43
|
+
firstEl.focus();
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
root.addEventListener("keydown", onKeyDown);
|
|
47
|
+
return () => {
|
|
48
|
+
root.removeEventListener("keydown", onKeyDown);
|
|
49
|
+
previouslyFocused?.focus?.();
|
|
50
|
+
};
|
|
51
|
+
}, [enabled]);
|
|
52
|
+
return ref;
|
|
53
|
+
}
|
|
54
|
+
export {
|
|
55
|
+
focusCandidates,
|
|
56
|
+
useFocusScope
|
|
57
|
+
};
|
|
58
|
+
//# sourceMappingURL=focus-scope.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/primitives/focus-scope.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\n\n// `tabindex=\"-1\"` opts an element out of Tab order whatever its tag, so the\n// exclusion goes on every branch — otherwise a roving-tabindex grid's parked\n// cells would count as tab stops.\nconst FOCUSABLE = [\n \"a[href]\",\n \"button:not([disabled])\",\n \"textarea:not([disabled])\",\n \"input:not([disabled])\",\n \"select:not([disabled])\",\n \"[tabindex]\",\n]\n .map(sel => `${sel}:not([tabindex=\"-1\"])`)\n .join(\",\");\n\n/**\n * Every enabled, Tab-reachable candidate under `root` in DOM (= Tab) order,\n * before the layout-based visibility filter; `tabindex=\"-1\"` is skipped on any\n * tag. For content that is fully visible while it exists (a popover), this is\n * the tab order itself. Internal to the kit.\n */\nexport function focusCandidates(root: HTMLElement): HTMLElement[] {\n return Array.from(root.querySelectorAll<HTMLElement>(FOCUSABLE));\n}\n\nfunction focusable(root: HTMLElement): HTMLElement[] {\n return focusCandidates(root).filter(\n (el) => el.offsetWidth > 0 || el.offsetHeight > 0 || el === document.activeElement,\n );\n}\n\n/**\n * Traps Tab focus inside the returned ref's subtree and restores focus to the\n * previously-focused element on unmount — the dep-light core of dialogs/menus,\n * replacing `@radix-ui/react-focus-scope`. On mount, focuses the first\n * focusable (or the container itself).\n *\n * Rust dialogs rely on the browser's native focus order within a fixed overlay;\n * see the README \"Limitations\".\n */\nexport function useFocusScope(enabled: boolean): React.RefObject<HTMLDivElement | null> {\n const ref = React.useRef<HTMLDivElement | null>(null);\n\n React.useEffect(() => {\n if (!enabled) return;\n const root = ref.current;\n if (!root) return;\n\n const previouslyFocused = document.activeElement as HTMLElement | null;\n const first = focusable(root)[0];\n (first ?? root).focus();\n\n function onKeyDown(event: KeyboardEvent) {\n if (event.key !== \"Tab\" || !root) return;\n const items = focusable(root);\n if (items.length === 0) {\n event.preventDefault();\n return;\n }\n const firstEl = items[0]!;\n const lastEl = items[items.length - 1]!;\n const active = document.activeElement;\n if (event.shiftKey && active === firstEl) {\n event.preventDefault();\n lastEl.focus();\n } else if (!event.shiftKey && active === lastEl) {\n event.preventDefault();\n firstEl.focus();\n }\n }\n\n root.addEventListener(\"keydown\", onKeyDown);\n return () => {\n root.removeEventListener(\"keydown\", onKeyDown);\n previouslyFocused?.focus?.();\n };\n }, [enabled]);\n\n return ref;\n}\n"],"mappings":";AAEA,YAAY,WAAW;AAKvB,MAAM,YAAY;AAAA,EAChB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,EACG,IAAI,SAAO,GAAG,GAAG,uBAAuB,EACxC,KAAK,GAAG;AAQJ,SAAS,gBAAgB,MAAkC;AAChE,SAAO,MAAM,KAAK,KAAK,iBAA8B,SAAS,CAAC;AACjE;AAEA,SAAS,UAAU,MAAkC;AACnD,SAAO,gBAAgB,IAAI,EAAE;AAAA,IAC3B,CAAC,OAAO,GAAG,cAAc,KAAK,GAAG,eAAe,KAAK,OAAO,SAAS;AAAA,EACvE;AACF;AAWO,SAAS,cAAc,SAA0D;AACtF,QAAM,MAAM,MAAM,OAA8B,IAAI;AAEpD,QAAM,UAAU,MAAM;AACpB,QAAI,CAAC,QAAS;AACd,UAAM,OAAO,IAAI;AACjB,QAAI,CAAC,KAAM;AAEX,UAAM,oBAAoB,SAAS;AACnC,UAAM,QAAQ,UAAU,IAAI,EAAE,CAAC;AAC/B,KAAC,SAAS,MAAM,MAAM;AAEtB,aAAS,UAAU,OAAsB;AACvC,UAAI,MAAM,QAAQ,SAAS,CAAC,KAAM;AAClC,YAAM,QAAQ,UAAU,IAAI;AAC5B,UAAI,MAAM,WAAW,GAAG;AACtB,cAAM,eAAe;AACrB;AAAA,MACF;AACA,YAAM,UAAU,MAAM,CAAC;AACvB,YAAM,SAAS,MAAM,MAAM,SAAS,CAAC;AACrC,YAAM,SAAS,SAAS;AACxB,UAAI,MAAM,YAAY,WAAW,SAAS;AACxC,cAAM,eAAe;AACrB,eAAO,MAAM;AAAA,MACf,WAAW,CAAC,MAAM,YAAY,WAAW,QAAQ;AAC/C,cAAM,eAAe;AACrB,gBAAQ,MAAM;AAAA,MAChB;AAAA,IACF;AAEA,SAAK,iBAAiB,WAAW,SAAS;AAC1C,WAAO,MAAM;AACX,WAAK,oBAAoB,WAAW,SAAS;AAC7C,yBAAmB,QAAQ;AAAA,IAC7B;AAAA,EACF,GAAG,CAAC,OAAO,CAAC;AAEZ,SAAO;AACT;","names":[]}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
function mergeRefs(...refs) {
|
|
3
|
+
return (node) => {
|
|
4
|
+
for (const ref of refs) {
|
|
5
|
+
if (typeof ref === "function") ref(node);
|
|
6
|
+
else if (ref != null) ref.current = node;
|
|
7
|
+
}
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
export {
|
|
11
|
+
mergeRefs
|
|
12
|
+
};
|
|
13
|
+
//# sourceMappingURL=merge-refs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/primitives/merge-refs.ts"],"sourcesContent":["import * as React from \"react\";\n\n/**\n * Combines several refs into one callback ref so a node can satisfy multiple\n * consumers at once (e.g. an overlay that is both the floating element and the\n * dismissable layer). Used throughout the overlay components.\n */\nexport function mergeRefs<T>(\n ...refs: Array<React.Ref<T> | undefined>\n): React.RefCallback<T> {\n return (node) => {\n for (const ref of refs) {\n if (typeof ref === \"function\") ref(node);\n else if (ref != null) (ref as React.MutableRefObject<T | null>).current = node;\n }\n };\n}\n"],"mappings":"AAAA,YAAY,WAAW;AAOhB,SAAS,aACX,MACmB;AACtB,SAAO,CAAC,SAAS;AACf,eAAW,OAAO,MAAM;AACtB,UAAI,OAAO,QAAQ,WAAY,KAAI,IAAI;AAAA,eAC9B,OAAO,KAAM,CAAC,IAAyC,UAAU;AAAA,IAC5E;AAAA,EACF;AACF;","names":[]}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { createPortal } from "react-dom";
|
|
5
|
+
const PortalContext = React.createContext(null);
|
|
6
|
+
function PortalProvider({ container, children }) {
|
|
7
|
+
return /* @__PURE__ */ jsx(PortalContext.Provider, { value: container, children });
|
|
8
|
+
}
|
|
9
|
+
function Portal({ children, container }) {
|
|
10
|
+
const provided = React.useContext(PortalContext);
|
|
11
|
+
if (typeof document === "undefined") return null;
|
|
12
|
+
const target = container ?? provided ?? document.body;
|
|
13
|
+
return createPortal(children, target);
|
|
14
|
+
}
|
|
15
|
+
export {
|
|
16
|
+
Portal,
|
|
17
|
+
PortalProvider
|
|
18
|
+
};
|
|
19
|
+
//# sourceMappingURL=portal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/primitives/portal.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { createPortal } from \"react-dom\";\n\n/**\n * Renders its children into `container` (default: the nearest\n * {@link PortalProvider}'s, else `document.body`) via `react-dom`'s\n * `createPortal` — the dep-light stand-in for `@radix-ui/react-portal`.\n * SSR-safe: renders nothing until mounted.\n *\n * Rust has no portal counterpart (see the README \"Limitations\"): `ev_lib::uikit`\n * overlays render inline with fixed positioning instead.\n */\nexport interface PortalProps {\n children: React.ReactNode;\n container?: Element | null;\n}\n\nconst PortalContext = React.createContext<Element | null>(null);\n\nexport interface PortalProviderProps {\n /** Where every overlay beneath this provider mounts; `null` falls back to `document.body`. */\n container: Element | null;\n children?: React.ReactNode;\n}\n\n/**\n * Re-targets every overlay beneath it (Dialog, Select, Popover, Tooltip, …)\n * into `container`. Tokens are inherited custom properties, so an overlay\n * portaled to `document.body` leaves its `[data-brand]` scope and paints in\n * the root palette; mounted inside the scope, it keeps the brand's.\n */\nexport function PortalProvider({ container, children }: PortalProviderProps) {\n return <PortalContext.Provider value={container}>{children}</PortalContext.Provider>;\n}\n\nexport function Portal({ children, container }: PortalProps) {\n const provided = React.useContext(PortalContext);\n // Render the portal synchronously on the client so a consumer's ref (e.g. a\n // floating element measured in `useLayoutEffect`) is attached in the same\n // commit. Overlays only render this when open — i.e. after a client event —\n // so there is no server render to mismatch.\n if (typeof document === \"undefined\") return null;\n const target = container ?? provided ?? document.body;\n return createPortal(children, target);\n}\n"],"mappings":";AAkCS;AAhCT,YAAY,WAAW;AACvB,SAAS,oBAAoB;AAgB7B,MAAM,gBAAgB,MAAM,cAA8B,IAAI;AAcvD,SAAS,eAAe,EAAE,WAAW,SAAS,GAAwB;AAC3E,SAAO,oBAAC,cAAc,UAAd,EAAuB,OAAO,WAAY,UAAS;AAC7D;AAEO,SAAS,OAAO,EAAE,UAAU,UAAU,GAAgB;AAC3D,QAAM,WAAW,MAAM,WAAW,aAAa;AAK/C,MAAI,OAAO,aAAa,YAAa,QAAO;AAC5C,QAAM,SAAS,aAAa,YAAY,SAAS;AACjD,SAAO,aAAa,UAAU,MAAM;AACtC;","names":[]}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import { flushSync } from "react-dom";
|
|
4
|
+
const EXIT_FALLBACK_MS = 1e3;
|
|
5
|
+
function usePresence(present) {
|
|
6
|
+
const ref = React.useRef(null);
|
|
7
|
+
const [mounted, setMounted] = React.useState(present);
|
|
8
|
+
React.useLayoutEffect(() => {
|
|
9
|
+
if (present) {
|
|
10
|
+
setMounted(true);
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
const node = ref.current;
|
|
14
|
+
if (!node) {
|
|
15
|
+
setMounted(false);
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
const name = getComputedStyle(node).animationName;
|
|
19
|
+
if (name === "" || name === "none") {
|
|
20
|
+
setMounted(false);
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
const prevPointerEvents = node.style.pointerEvents;
|
|
24
|
+
node.style.pointerEvents = "none";
|
|
25
|
+
const onEnd = (e) => {
|
|
26
|
+
if (e.target !== node) return;
|
|
27
|
+
if (e.animationName && !name.includes(e.animationName)) return;
|
|
28
|
+
flushSync(() => setMounted(false));
|
|
29
|
+
};
|
|
30
|
+
node.addEventListener("animationend", onEnd);
|
|
31
|
+
node.addEventListener("animationcancel", onEnd);
|
|
32
|
+
const timer = setTimeout(() => setMounted(false), EXIT_FALLBACK_MS);
|
|
33
|
+
return () => {
|
|
34
|
+
clearTimeout(timer);
|
|
35
|
+
node.removeEventListener("animationend", onEnd);
|
|
36
|
+
node.removeEventListener("animationcancel", onEnd);
|
|
37
|
+
node.style.pointerEvents = prevPointerEvents;
|
|
38
|
+
};
|
|
39
|
+
}, [present]);
|
|
40
|
+
return { isPresent: present || mounted, ref };
|
|
41
|
+
}
|
|
42
|
+
export {
|
|
43
|
+
usePresence
|
|
44
|
+
};
|
|
45
|
+
//# sourceMappingURL=presence.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/primitives/presence.ts"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { flushSync } from \"react-dom\";\n\n// Safety net for a missed exit `animationend` (display:none ancestor, tab\n// hidden, animations disabled by a stylesheet after the check). Well past the\n// longest exit in tw-animate-css.\nconst EXIT_FALLBACK_MS = 1000;\n\n/**\n * Keeps an overlay mounted through its exit animation. Enter animations\n * (`data-[state=open]:animate-in`) play on mount; once `present` turns false\n * the consumer flips the node to `data-state=\"closed\"` in the same render and\n * the node stays until its exit animation ends (or the fallback timer fires).\n * Re-opening mid-exit just flips the state back; the node is never remounted.\n *\n * The exit was dropped once (5728b40) because closing flickered: tw-animate's\n * `animate-out` runs with `animation-fill-mode: none`, so after `animationend`\n * the node snaps back to full opacity/scale for the frame React needs to\n * process an async unmount. Hence `flushSync` inside the native listener —\n * the unmount commits before the browser paints that frame (Radix Presence\n * does the same). With no animation on the node (jsdom, overlays without an\n * exit class) unmount is immediate, as before. The Rust mirror still does not\n * defer unmount.\n *\n * Pass `ref` to the animated node — the one carrying `data-state`.\n */\nexport function usePresence(present: boolean): {\n isPresent: boolean;\n ref: React.RefObject<HTMLElement | null>;\n} {\n const ref = React.useRef<HTMLElement | null>(null);\n const [mounted, setMounted] = React.useState(present);\n\n React.useLayoutEffect(() => {\n if (present) {\n setMounted(true);\n return;\n }\n const node = ref.current;\n if (!node) {\n setMounted(false);\n return;\n }\n // `data-state=\"closed\"` is already in the DOM, so this resolves to the\n // exit animation (if any) — reading it forces the style recalc.\n const name = getComputedStyle(node).animationName;\n if (name === \"\" || name === \"none\") {\n setMounted(false);\n return;\n }\n const prevPointerEvents = node.style.pointerEvents;\n node.style.pointerEvents = \"none\";\n // Animation events bubble from descendants, and a close mid-enter cancels\n // the enter keyframe on this very node — only the exit's own end counts.\n const onEnd = (e: AnimationEvent) => {\n if (e.target !== node) return;\n if (e.animationName && !name.includes(e.animationName)) return;\n flushSync(() => setMounted(false));\n };\n node.addEventListener(\"animationend\", onEnd);\n node.addEventListener(\"animationcancel\", onEnd);\n const timer = setTimeout(() => setMounted(false), EXIT_FALLBACK_MS);\n return () => {\n clearTimeout(timer);\n node.removeEventListener(\"animationend\", onEnd);\n node.removeEventListener(\"animationcancel\", onEnd);\n node.style.pointerEvents = prevPointerEvents;\n };\n }, [present]);\n\n return { isPresent: present || mounted, ref };\n}\n"],"mappings":";AAEA,YAAY,WAAW;AACvB,SAAS,iBAAiB;AAK1B,MAAM,mBAAmB;AAoBlB,SAAS,YAAY,SAG1B;AACA,QAAM,MAAM,MAAM,OAA2B,IAAI;AACjD,QAAM,CAAC,SAAS,UAAU,IAAI,MAAM,SAAS,OAAO;AAEpD,QAAM,gBAAgB,MAAM;AAC1B,QAAI,SAAS;AACX,iBAAW,IAAI;AACf;AAAA,IACF;AACA,UAAM,OAAO,IAAI;AACjB,QAAI,CAAC,MAAM;AACT,iBAAW,KAAK;AAChB;AAAA,IACF;AAGA,UAAM,OAAO,iBAAiB,IAAI,EAAE;AACpC,QAAI,SAAS,MAAM,SAAS,QAAQ;AAClC,iBAAW,KAAK;AAChB;AAAA,IACF;AACA,UAAM,oBAAoB,KAAK,MAAM;AACrC,SAAK,MAAM,gBAAgB;AAG3B,UAAM,QAAQ,CAAC,MAAsB;AACnC,UAAI,EAAE,WAAW,KAAM;AACvB,UAAI,EAAE,iBAAiB,CAAC,KAAK,SAAS,EAAE,aAAa,EAAG;AACxD,gBAAU,MAAM,WAAW,KAAK,CAAC;AAAA,IACnC;AACA,SAAK,iBAAiB,gBAAgB,KAAK;AAC3C,SAAK,iBAAiB,mBAAmB,KAAK;AAC9C,UAAM,QAAQ,WAAW,MAAM,WAAW,KAAK,GAAG,gBAAgB;AAClE,WAAO,MAAM;AACX,mBAAa,KAAK;AAClB,WAAK,oBAAoB,gBAAgB,KAAK;AAC9C,WAAK,oBAAoB,mBAAmB,KAAK;AACjD,WAAK,MAAM,gBAAgB;AAAA,IAC7B;AAAA,EACF,GAAG,CAAC,OAAO,CAAC;AAEZ,SAAO,EAAE,WAAW,WAAW,SAAS,IAAI;AAC9C;","names":[]}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { cn } from "../lib/cn.js";
|
|
3
|
+
const Slot = React.forwardRef(function Slot2({ children, ...slotProps }, forwardedRef) {
|
|
4
|
+
if (!React.isValidElement(children)) {
|
|
5
|
+
return React.Children.count(children) > 1 ? React.Children.only(null) : null;
|
|
6
|
+
}
|
|
7
|
+
const child = children;
|
|
8
|
+
const childProps = child.props;
|
|
9
|
+
const merged = { ...slotProps, ...childProps };
|
|
10
|
+
for (const key of Object.keys(slotProps)) {
|
|
11
|
+
const slotValue = slotProps[key];
|
|
12
|
+
const childValue = childProps[key];
|
|
13
|
+
if (/^on[A-Z]/.test(key) && typeof slotValue === "function") {
|
|
14
|
+
merged[key] = typeof childValue === "function" ? (...args) => {
|
|
15
|
+
childValue(...args);
|
|
16
|
+
slotValue(...args);
|
|
17
|
+
} : slotValue;
|
|
18
|
+
} else if (key === "style") {
|
|
19
|
+
merged[key] = { ...slotValue, ...childValue };
|
|
20
|
+
} else if (key === "className") {
|
|
21
|
+
merged[key] = cn(slotValue, childValue);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
const childRef = child.ref;
|
|
25
|
+
if (forwardedRef || childRef) merged["ref"] = composeRefs(forwardedRef, childRef);
|
|
26
|
+
return React.cloneElement(child, merged);
|
|
27
|
+
});
|
|
28
|
+
function composeRefs(...refs) {
|
|
29
|
+
return (node) => {
|
|
30
|
+
for (const ref of refs) {
|
|
31
|
+
if (typeof ref === "function") ref(node);
|
|
32
|
+
else if (ref != null) ref.current = node;
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
export {
|
|
37
|
+
Slot
|
|
38
|
+
};
|
|
39
|
+
//# sourceMappingURL=slot.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/primitives/slot.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"../lib/cn\";\n\n/**\n * Minimal `asChild` mechanism — the dep-light replacement for\n * `@radix-ui/react-slot`. Renders by cloning its single child, merging the\n * Slot's own props onto it: `className` is fused with `cn`, event handlers are\n * chained (child's runs first, then ours), and `style` objects are shallow\n * merged. Refs are composed so both the caller's and the child's ref fire.\n *\n * There is no Rust counterpart: Dioxus composition is expressed with `children`\n * directly, so `asChild` is a TS-only ergonomic.\n */\nexport interface SlotProps extends React.HTMLAttributes<HTMLElement> {\n children?: React.ReactNode;\n}\n\nexport const Slot = React.forwardRef<HTMLElement, SlotProps>(function Slot(\n { children, ...slotProps },\n forwardedRef,\n) {\n if (!React.isValidElement(children)) {\n return React.Children.count(children) > 1\n ? React.Children.only(null)\n : null;\n }\n\n const child = children as React.ReactElement<Record<string, unknown>>;\n const childProps = child.props;\n\n const merged: Record<string, unknown> = { ...slotProps, ...childProps };\n\n for (const key of Object.keys(slotProps)) {\n const slotValue = (slotProps as Record<string, unknown>)[key];\n const childValue = childProps[key];\n if (/^on[A-Z]/.test(key) && typeof slotValue === \"function\") {\n merged[key] =\n typeof childValue === \"function\"\n ? (...args: unknown[]) => {\n (childValue as (...a: unknown[]) => void)(...args);\n (slotValue as (...a: unknown[]) => void)(...args);\n }\n : slotValue;\n } else if (key === \"style\") {\n merged[key] = { ...(slotValue as object), ...(childValue as object) };\n } else if (key === \"className\") {\n merged[key] = cn(slotValue as string, childValue as string);\n }\n }\n\n // Only when there is a ref to compose. `Slot` renders inside server\n // components too (`<Button asChild>` has no \"use client\"), and a function\n // ref in a Server Component's output fails the render (\"Refs cannot be used\n // in Server Components\") even when it would call nothing.\n const childRef = (child as { ref?: React.Ref<unknown> }).ref;\n if (forwardedRef || childRef) merged[\"ref\"] = composeRefs(forwardedRef, childRef);\n\n return React.cloneElement(child, merged);\n});\n\nfunction composeRefs<T>(...refs: Array<React.Ref<T> | undefined>): React.RefCallback<T> {\n return (node) => {\n for (const ref of refs) {\n if (typeof ref === \"function\") ref(node);\n else if (ref != null) (ref as React.MutableRefObject<T | null>).current = node;\n }\n };\n}\n"],"mappings":"AAAA,YAAY,WAAW;AACvB,SAAS,UAAU;AAgBZ,MAAM,OAAO,MAAM,WAAmC,SAASA,MACpE,EAAE,UAAU,GAAG,UAAU,GACzB,cACA;AACA,MAAI,CAAC,MAAM,eAAe,QAAQ,GAAG;AACnC,WAAO,MAAM,SAAS,MAAM,QAAQ,IAAI,IACpC,MAAM,SAAS,KAAK,IAAI,IACxB;AAAA,EACN;AAEA,QAAM,QAAQ;AACd,QAAM,aAAa,MAAM;AAEzB,QAAM,SAAkC,EAAE,GAAG,WAAW,GAAG,WAAW;AAEtE,aAAW,OAAO,OAAO,KAAK,SAAS,GAAG;AACxC,UAAM,YAAa,UAAsC,GAAG;AAC5D,UAAM,aAAa,WAAW,GAAG;AACjC,QAAI,WAAW,KAAK,GAAG,KAAK,OAAO,cAAc,YAAY;AAC3D,aAAO,GAAG,IACR,OAAO,eAAe,aAClB,IAAI,SAAoB;AACtB,QAAC,WAAyC,GAAG,IAAI;AACjD,QAAC,UAAwC,GAAG,IAAI;AAAA,MAClD,IACA;AAAA,IACR,WAAW,QAAQ,SAAS;AAC1B,aAAO,GAAG,IAAI,EAAE,GAAI,WAAsB,GAAI,WAAsB;AAAA,IACtE,WAAW,QAAQ,aAAa;AAC9B,aAAO,GAAG,IAAI,GAAG,WAAqB,UAAoB;AAAA,IAC5D;AAAA,EACF;AAMA,QAAM,WAAY,MAAuC;AACzD,MAAI,gBAAgB,SAAU,QAAO,KAAK,IAAI,YAAY,cAAc,QAAQ;AAEhF,SAAO,MAAM,aAAa,OAAO,MAAM;AACzC,CAAC;AAED,SAAS,eAAkB,MAA6D;AACtF,SAAO,CAAC,SAAS;AACf,eAAW,OAAO,MAAM;AACtB,UAAI,OAAO,QAAQ,WAAY,KAAI,IAAI;AAAA,eAC9B,OAAO,KAAM,CAAC,IAAyC,UAAU;AAAA,IAC5E;AAAA,EACF;AACF;","names":["Slot"]}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
function useControllableState(opts) {
|
|
4
|
+
const { value, defaultValue, onChange } = opts;
|
|
5
|
+
const isControlled = value !== void 0;
|
|
6
|
+
const [uncontrolled, setUncontrolled] = React.useState(defaultValue);
|
|
7
|
+
const current = isControlled ? value : uncontrolled;
|
|
8
|
+
const onChangeRef = React.useRef(onChange);
|
|
9
|
+
React.useEffect(() => {
|
|
10
|
+
onChangeRef.current = onChange;
|
|
11
|
+
});
|
|
12
|
+
const setValue = React.useCallback(
|
|
13
|
+
(next) => {
|
|
14
|
+
if (!isControlled) setUncontrolled(next);
|
|
15
|
+
onChangeRef.current?.(next);
|
|
16
|
+
},
|
|
17
|
+
[isControlled]
|
|
18
|
+
);
|
|
19
|
+
return [current, setValue];
|
|
20
|
+
}
|
|
21
|
+
export {
|
|
22
|
+
useControllableState
|
|
23
|
+
};
|
|
24
|
+
//# sourceMappingURL=use-controllable-state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/primitives/use-controllable-state.ts"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\n\n/**\n * Controlled/uncontrolled state, the dep-light equivalent of Radix's\n * `useControllableState`. When `value` is provided the component is controlled\n * (the hook returns it and routes writes through `onChange`); otherwise it owns\n * an internal `useState` seeded from `defaultValue`.\n *\n * The Rust mirror is `use_controllable_signal` in `ev_lib::uikit`.\n */\nexport function useControllableState<T>(opts: {\n value?: T | undefined;\n defaultValue: T;\n onChange?: ((value: T) => void) | undefined;\n}): [T, (next: T) => void] {\n const { value, defaultValue, onChange } = opts;\n const isControlled = value !== undefined;\n const [uncontrolled, setUncontrolled] = React.useState<T>(defaultValue);\n\n const current = isControlled ? value : uncontrolled;\n\n const onChangeRef = React.useRef(onChange);\n React.useEffect(() => {\n onChangeRef.current = onChange;\n });\n\n const setValue = React.useCallback(\n (next: T) => {\n if (!isControlled) setUncontrolled(next);\n onChangeRef.current?.(next);\n },\n [isControlled],\n );\n\n return [current, setValue];\n}\n"],"mappings":";AAEA,YAAY,WAAW;AAUhB,SAAS,qBAAwB,MAIb;AACzB,QAAM,EAAE,OAAO,cAAc,SAAS,IAAI;AAC1C,QAAM,eAAe,UAAU;AAC/B,QAAM,CAAC,cAAc,eAAe,IAAI,MAAM,SAAY,YAAY;AAEtE,QAAM,UAAU,eAAe,QAAQ;AAEvC,QAAM,cAAc,MAAM,OAAO,QAAQ;AACzC,QAAM,UAAU,MAAM;AACpB,gBAAY,UAAU;AAAA,EACxB,CAAC;AAED,QAAM,WAAW,MAAM;AAAA,IACrB,CAAC,SAAY;AACX,UAAI,CAAC,aAAc,iBAAgB,IAAI;AACvC,kBAAY,UAAU,IAAI;AAAA,IAC5B;AAAA,IACA,CAAC,YAAY;AAAA,EACf;AAEA,SAAO,CAAC,SAAS,QAAQ;AAC3B;","names":[]}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
const ABSOLUTE = Object.freeze({ position: "absolute", top: 0, left: 0 });
|
|
4
|
+
const POSITIONED = /* @__PURE__ */ new Set(["relative", "absolute", "fixed", "sticky"]);
|
|
5
|
+
function toContainingBlock(floating, x, y) {
|
|
6
|
+
const parent = floating.offsetParent;
|
|
7
|
+
const doc = floating.ownerDocument;
|
|
8
|
+
const root = parent === doc.body || parent === doc.documentElement || parent === null;
|
|
9
|
+
const positioned = parent instanceof HTMLElement && (!root || POSITIONED.has(doc.defaultView?.getComputedStyle(parent).position ?? ""));
|
|
10
|
+
if (!positioned) {
|
|
11
|
+
const win = doc.defaultView;
|
|
12
|
+
return { x: x + (win?.scrollX ?? 0), y: y + (win?.scrollY ?? 0), root: true };
|
|
13
|
+
}
|
|
14
|
+
const p = parent.getBoundingClientRect();
|
|
15
|
+
return {
|
|
16
|
+
x: x - p.left - parent.clientLeft + parent.scrollLeft,
|
|
17
|
+
y: y - p.top - parent.clientTop + parent.scrollTop,
|
|
18
|
+
root: false
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
function layoutSize(el) {
|
|
22
|
+
return { width: el.offsetWidth, height: el.offsetHeight };
|
|
23
|
+
}
|
|
24
|
+
function viewport() {
|
|
25
|
+
const el = document.documentElement;
|
|
26
|
+
return {
|
|
27
|
+
vw: el.clientWidth || window.innerWidth,
|
|
28
|
+
vh: el.clientHeight || window.innerHeight
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
function useFloating(opts) {
|
|
32
|
+
const { anchorRef, open, side = "bottom", align = "center", offset = 4 } = opts;
|
|
33
|
+
const floatingRef = React.useRef(null);
|
|
34
|
+
const [placedSide, setPlacedSide] = React.useState(side);
|
|
35
|
+
const placement = React.useRef({
|
|
36
|
+
side,
|
|
37
|
+
shiftX: 0,
|
|
38
|
+
shiftY: 0
|
|
39
|
+
});
|
|
40
|
+
React.useLayoutEffect(() => {
|
|
41
|
+
if (!open) return;
|
|
42
|
+
function ideal(a, f, placed) {
|
|
43
|
+
let top = 0;
|
|
44
|
+
let left = 0;
|
|
45
|
+
const vertical = placed === "top" || placed === "bottom";
|
|
46
|
+
if (placed === "bottom") top = a.bottom + offset;
|
|
47
|
+
else if (placed === "top") top = a.top - offset - f.height;
|
|
48
|
+
else if (placed === "right") left = a.right + offset;
|
|
49
|
+
else left = a.left - offset - f.width;
|
|
50
|
+
if (vertical) {
|
|
51
|
+
if (align === "start") left = a.left;
|
|
52
|
+
else if (align === "end") left = a.right - f.width;
|
|
53
|
+
else left = a.left + a.width / 2 - f.width / 2;
|
|
54
|
+
} else {
|
|
55
|
+
if (align === "start") top = a.top;
|
|
56
|
+
else if (align === "end") top = a.bottom - f.height;
|
|
57
|
+
else top = a.top + a.height / 2 - f.height / 2;
|
|
58
|
+
}
|
|
59
|
+
return { top, left };
|
|
60
|
+
}
|
|
61
|
+
function write(floating, f, top, left, contain) {
|
|
62
|
+
const p = toContainingBlock(floating, left, top);
|
|
63
|
+
let { x, y } = p;
|
|
64
|
+
if (contain && p.root) {
|
|
65
|
+
const icbWidth = floating.ownerDocument.documentElement.clientWidth || window.innerWidth;
|
|
66
|
+
x = Math.max(0, Math.min(x, icbWidth - f.width));
|
|
67
|
+
y = Math.max(0, y);
|
|
68
|
+
}
|
|
69
|
+
floating.style.translate = `${Math.round(x)}px ${Math.round(y)}px`;
|
|
70
|
+
}
|
|
71
|
+
function place() {
|
|
72
|
+
const anchor = anchorRef.current;
|
|
73
|
+
const floating = floatingRef.current;
|
|
74
|
+
if (!anchor || !floating) return;
|
|
75
|
+
const a = anchor.getBoundingClientRect();
|
|
76
|
+
const f = layoutSize(floating);
|
|
77
|
+
const { vw, vh } = viewport();
|
|
78
|
+
let placed = side;
|
|
79
|
+
if (side === "bottom" && a.bottom + offset + f.height > vh && a.top - offset - f.height > 0)
|
|
80
|
+
placed = "top";
|
|
81
|
+
else if (side === "top" && a.top - offset - f.height < 0 && a.bottom + offset + f.height < vh)
|
|
82
|
+
placed = "bottom";
|
|
83
|
+
else if (side === "right" && a.right + offset + f.width > vw && a.left - offset - f.width > 0)
|
|
84
|
+
placed = "left";
|
|
85
|
+
else if (side === "left" && a.left - offset - f.width < 0 && a.right + offset + f.width < vw)
|
|
86
|
+
placed = "right";
|
|
87
|
+
const { top, left } = ideal(a, f, placed);
|
|
88
|
+
const clampedLeft = Math.max(offset, Math.min(left, vw - f.width - offset));
|
|
89
|
+
const clampedTop = Math.max(offset, Math.min(top, vh - f.height - offset));
|
|
90
|
+
placement.current = {
|
|
91
|
+
side: placed,
|
|
92
|
+
shiftX: clampedLeft - left,
|
|
93
|
+
shiftY: clampedTop - top
|
|
94
|
+
};
|
|
95
|
+
write(floating, f, clampedTop, clampedLeft, false);
|
|
96
|
+
setPlacedSide((prev) => prev === placed ? prev : placed);
|
|
97
|
+
}
|
|
98
|
+
function sync() {
|
|
99
|
+
const anchor = anchorRef.current;
|
|
100
|
+
const floating = floatingRef.current;
|
|
101
|
+
if (!anchor || !floating) return;
|
|
102
|
+
const a = anchor.getBoundingClientRect();
|
|
103
|
+
const f = layoutSize(floating);
|
|
104
|
+
const { side: placed, shiftX, shiftY } = placement.current;
|
|
105
|
+
const { top, left } = ideal(a, f, placed);
|
|
106
|
+
write(floating, f, top + shiftY, left + shiftX, true);
|
|
107
|
+
}
|
|
108
|
+
place();
|
|
109
|
+
window.addEventListener("scroll", sync, true);
|
|
110
|
+
window.addEventListener("resize", place);
|
|
111
|
+
let observer;
|
|
112
|
+
if (typeof ResizeObserver !== "undefined") {
|
|
113
|
+
observer = new ResizeObserver(place);
|
|
114
|
+
if (anchorRef.current) observer.observe(anchorRef.current);
|
|
115
|
+
if (floatingRef.current) observer.observe(floatingRef.current);
|
|
116
|
+
}
|
|
117
|
+
return () => {
|
|
118
|
+
window.removeEventListener("scroll", sync, true);
|
|
119
|
+
window.removeEventListener("resize", place);
|
|
120
|
+
observer?.disconnect();
|
|
121
|
+
};
|
|
122
|
+
}, [open, side, align, offset, anchorRef]);
|
|
123
|
+
return { floatingRef, style: ABSOLUTE, side: placedSide, align };
|
|
124
|
+
}
|
|
125
|
+
export {
|
|
126
|
+
useFloating
|
|
127
|
+
};
|
|
128
|
+
//# sourceMappingURL=use-floating.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/primitives/use-floating.ts"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\n\nexport type Side = \"top\" | \"right\" | \"bottom\" | \"left\";\nexport type Align = \"start\" | \"center\" | \"end\";\n\nexport interface FloatingResult {\n floatingRef: React.RefObject<HTMLElement | null>;\n style: React.CSSProperties;\n side: Side;\n align: Align;\n}\n\n// The element sits at its containing block's origin and is moved with the\n// `translate` property, never with `top`/`left`: an absolute box's\n// shrink-to-fit width is \"containing block minus `left`\", so a `w-fit` surface\n// pushed past the viewport's right edge (horizontal scroll, a nested scroller)\n// would collapse to one word per line. `translate` composes with `transform`,\n// so the enter animations (which animate `transform`) are unaffected. The\n// offset never goes through React: `place()` / `sync()` write it straight to\n// the element, so this style prop stays one frozen object.\nconst ABSOLUTE: React.CSSProperties = Object.freeze({ position: \"absolute\", top: 0, left: 0 });\n\nconst POSITIONED = new Set([\"relative\", \"absolute\", \"fixed\", \"sticky\"]);\n\n/**\n * Converts a viewport-relative point into the coordinate space of the\n * floating element's containing block, i.e. the numbers `position: absolute`\n * expects. floating-ui's `strategy: \"absolute\"` does the same walk.\n */\nfunction toContainingBlock(\n floating: HTMLElement,\n x: number,\n y: number,\n): { x: number; y: number; root: boolean } {\n const parent = floating.offsetParent;\n const doc = floating.ownerDocument;\n const root = parent === doc.body || parent === doc.documentElement || parent === null;\n // `offsetParent` reports `body` even when nothing is positioned; only a\n // positioned body/html really forms a containing block.\n const positioned =\n parent instanceof HTMLElement &&\n (!root || POSITIONED.has(doc.defaultView?.getComputedStyle(parent).position ?? \"\"));\n if (!positioned) {\n const win = doc.defaultView;\n return { x: x + (win?.scrollX ?? 0), y: y + (win?.scrollY ?? 0), root: true };\n }\n const p = parent.getBoundingClientRect();\n return {\n x: x - p.left - parent.clientLeft + parent.scrollLeft,\n y: y - p.top - parent.clientTop + parent.scrollTop,\n root: false,\n };\n}\n\ninterface Size2D {\n width: number;\n height: number;\n}\n\n// The layout box, not `getBoundingClientRect()`: the latter reports the box\n// mid-animation (`zoom-in-95` scales it to 0.95 on the first frame), which\n// placed a top-side popover 5 % of its height too low, over the anchor.\nfunction layoutSize(el: HTMLElement): Size2D {\n return { width: el.offsetWidth, height: el.offsetHeight };\n}\n\nfunction viewport(): { vw: number; vh: number } {\n // `clientWidth` excludes the scrollbar, which the floating element cannot\n // sit under anyway; jsdom reports 0 there, hence the fallback.\n const el = document.documentElement;\n return {\n vw: el.clientWidth || window.innerWidth,\n vh: el.clientHeight || window.innerHeight,\n };\n}\n\n/**\n * Positions a floating element against an anchor with `position: absolute` in\n * the coordinate space of its containing block (the `Portal` puts it in\n * `document.body`, so normally document coordinates) — the dep-light stand-in\n * for `@radix-ui/react-popper` / floating-ui. Supports a preferred\n * `side`/`align`, an `offset`, and a single collision flip onto the opposite\n * side when the preferred side overflows the viewport.\n *\n * Two update modes:\n * - `place()` — full pass (flip + clamp into the viewport) on open, on window\n * `resize`, and whenever the anchor or the floating element changes size.\n * - `sync()` — re-derives the coordinates from the anchor with the side and\n * the clamp shift already chosen; runs on every `scroll` (captured on\n * `window`, so nested scroll containers count too).\n *\n * Because the element is absolutely positioned in the document, a document\n * scroll moves it natively with the page — `sync()` then computes the same\n * numbers and is a no-op — so the overlay never lags behind the compositor.\n * Only a nested scroll container changes the coordinates, and there a frame\n * of lag is acceptable. The side is never revisited on scroll: no jumping.\n * A page that scrolls `body` instead of the document (`html { overflow:\n * hidden } body { overflow: auto }`) is such a nested scroller: correct, but\n * with the frame of lag the `fixed` strategy had.\n *\n * Rust's overlays use CSS-only placement (`data-side`) and do not measure; see\n * the README \"Limitations\".\n */\nexport function useFloating(opts: {\n anchorRef: React.RefObject<Element | null>;\n open: boolean;\n side?: Side;\n align?: Align;\n offset?: number;\n}): FloatingResult {\n const { anchorRef, open, side = \"bottom\", align = \"center\", offset = 4 } = opts;\n const floatingRef = React.useRef<HTMLElement | null>(null);\n const [placedSide, setPlacedSide] = React.useState<Side>(side);\n // The last full placement: the side that won the flip and how far the\n // viewport clamp pushed the element off its ideal spot. `sync()` reuses both\n // instead of measuring against the viewport again.\n const placement = React.useRef<{ side: Side; shiftX: number; shiftY: number }>({\n side,\n shiftX: 0,\n shiftY: 0,\n });\n\n React.useLayoutEffect(() => {\n if (!open) return;\n\n function ideal(a: DOMRect, f: Size2D, placed: Side): { top: number; left: number } {\n let top = 0;\n let left = 0;\n const vertical = placed === \"top\" || placed === \"bottom\";\n\n if (placed === \"bottom\") top = a.bottom + offset;\n else if (placed === \"top\") top = a.top - offset - f.height;\n else if (placed === \"right\") left = a.right + offset;\n else left = a.left - offset - f.width;\n\n if (vertical) {\n if (align === \"start\") left = a.left;\n else if (align === \"end\") left = a.right - f.width;\n else left = a.left + a.width / 2 - f.width / 2;\n } else {\n if (align === \"start\") top = a.top;\n else if (align === \"end\") top = a.bottom - f.height;\n else top = a.top + a.height / 2 - f.height / 2;\n }\n return { top, left };\n }\n\n // `contain` keeps the box inside the initial containing block: a box past\n // the document's edge grows the page (a scrollbar shows for as long as the\n // overlay is open). `place()` already clamps against the viewport;\n // `sync()` follows a nested scroller's anchor and only needs this bound.\n function write(floating: HTMLElement, f: Size2D, top: number, left: number, contain: boolean) {\n const p = toContainingBlock(floating, left, top);\n let { x, y } = p;\n if (contain && p.root) {\n const icbWidth = floating.ownerDocument.documentElement.clientWidth || window.innerWidth;\n x = Math.max(0, Math.min(x, icbWidth - f.width));\n y = Math.max(0, y);\n }\n floating.style.translate = `${Math.round(x)}px ${Math.round(y)}px`;\n }\n\n function place() {\n const anchor = anchorRef.current;\n const floating = floatingRef.current;\n if (!anchor || !floating) return;\n\n const a = anchor.getBoundingClientRect();\n const f = layoutSize(floating);\n const { vw, vh } = viewport();\n\n let placed: Side = side;\n if (side === \"bottom\" && a.bottom + offset + f.height > vh && a.top - offset - f.height > 0)\n placed = \"top\";\n else if (side === \"top\" && a.top - offset - f.height < 0 && a.bottom + offset + f.height < vh)\n placed = \"bottom\";\n else if (side === \"right\" && a.right + offset + f.width > vw && a.left - offset - f.width > 0)\n placed = \"left\";\n else if (side === \"left\" && a.left - offset - f.width < 0 && a.right + offset + f.width < vw)\n placed = \"right\";\n\n const { top, left } = ideal(a, f, placed);\n const clampedLeft = Math.max(offset, Math.min(left, vw - f.width - offset));\n const clampedTop = Math.max(offset, Math.min(top, vh - f.height - offset));\n\n placement.current = {\n side: placed,\n shiftX: clampedLeft - left,\n shiftY: clampedTop - top,\n };\n write(floating, f, clampedTop, clampedLeft, false);\n // Only a real change may re-render: this runs on every resize tick.\n setPlacedSide(prev => (prev === placed ? prev : placed));\n }\n\n function sync() {\n const anchor = anchorRef.current;\n const floating = floatingRef.current;\n if (!anchor || !floating) return;\n\n const a = anchor.getBoundingClientRect();\n const f = layoutSize(floating);\n const { side: placed, shiftX, shiftY } = placement.current;\n const { top, left } = ideal(a, f, placed);\n write(floating, f, top + shiftY, left + shiftX, true);\n }\n\n place();\n // No rAF: `scroll` already fires at most once per frame, and a synchronous\n // DOM write from the handler lands in that same frame — rAF would cost one.\n window.addEventListener(\"scroll\", sync, true);\n window.addEventListener(\"resize\", place);\n\n let observer: ResizeObserver | undefined;\n if (typeof ResizeObserver !== \"undefined\") {\n observer = new ResizeObserver(place);\n if (anchorRef.current) observer.observe(anchorRef.current);\n if (floatingRef.current) observer.observe(floatingRef.current);\n }\n\n return () => {\n window.removeEventListener(\"scroll\", sync, true);\n window.removeEventListener(\"resize\", place);\n observer?.disconnect();\n };\n }, [open, side, align, offset, anchorRef]);\n\n return { floatingRef, style: ABSOLUTE, side: placedSide, align };\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
function useHoverIntent(opts) {
|
|
4
|
+
const { onOpen, onClose, openDelay = 500, closeDelay = 0 } = opts;
|
|
5
|
+
const openRef = React.useRef(onOpen);
|
|
6
|
+
const closeRef = React.useRef(onClose);
|
|
7
|
+
React.useEffect(() => {
|
|
8
|
+
openRef.current = onOpen;
|
|
9
|
+
closeRef.current = onClose;
|
|
10
|
+
});
|
|
11
|
+
const timer = React.useRef(null);
|
|
12
|
+
const clear = React.useCallback(() => {
|
|
13
|
+
if (timer.current != null) {
|
|
14
|
+
clearTimeout(timer.current);
|
|
15
|
+
timer.current = null;
|
|
16
|
+
}
|
|
17
|
+
}, []);
|
|
18
|
+
React.useEffect(() => clear, [clear]);
|
|
19
|
+
const onPointerEnter = React.useCallback(
|
|
20
|
+
(event) => {
|
|
21
|
+
if (event.pointerType !== "mouse") return;
|
|
22
|
+
clear();
|
|
23
|
+
timer.current = setTimeout(() => {
|
|
24
|
+
timer.current = null;
|
|
25
|
+
openRef.current();
|
|
26
|
+
}, openDelay);
|
|
27
|
+
},
|
|
28
|
+
[clear, openDelay]
|
|
29
|
+
);
|
|
30
|
+
const onPointerLeave = React.useCallback(
|
|
31
|
+
(event) => {
|
|
32
|
+
if (event.pointerType !== "mouse") return;
|
|
33
|
+
clear();
|
|
34
|
+
if (closeDelay > 0) {
|
|
35
|
+
timer.current = setTimeout(() => {
|
|
36
|
+
timer.current = null;
|
|
37
|
+
closeRef.current();
|
|
38
|
+
}, closeDelay);
|
|
39
|
+
} else {
|
|
40
|
+
closeRef.current();
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
[clear, closeDelay]
|
|
44
|
+
);
|
|
45
|
+
return { onPointerEnter, onPointerLeave };
|
|
46
|
+
}
|
|
47
|
+
export {
|
|
48
|
+
useHoverIntent
|
|
49
|
+
};
|
|
50
|
+
//# sourceMappingURL=use-hover-intent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/primitives/use-hover-intent.ts"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\n\nexport interface HoverIntentHandlers {\n onPointerEnter: (event: React.PointerEvent) => void;\n onPointerLeave: (event: React.PointerEvent) => void;\n}\n\n/**\n * Mouse-only hover-with-intent for the {@link InfoTip} toggletip: opens after the\n * cursor dwells `openDelay`ms over the trigger, closes after `closeDelay`ms once\n * it leaves. Touch and pen pointers are ignored — they have no hover, so a\n * coarse-pointer device never fires this and the toggletip's click/tap path takes\n * over. This timer is the one piece of behaviour with no Rust counterpart: the\n * Dioxus kit has no host timers (the same reason it has no Portal or floating\n * engine — see the README \"Limitations\"), so its trigger opens on hover instantly.\n */\nexport function useHoverIntent(opts: {\n onOpen: () => void;\n onClose: () => void;\n openDelay?: number;\n closeDelay?: number;\n}): HoverIntentHandlers {\n const { onOpen, onClose, openDelay = 500, closeDelay = 0 } = opts;\n\n const openRef = React.useRef(onOpen);\n const closeRef = React.useRef(onClose);\n React.useEffect(() => {\n openRef.current = onOpen;\n closeRef.current = onClose;\n });\n\n const timer = React.useRef<ReturnType<typeof setTimeout> | null>(null);\n const clear = React.useCallback(() => {\n if (timer.current != null) {\n clearTimeout(timer.current);\n timer.current = null;\n }\n }, []);\n React.useEffect(() => clear, [clear]);\n\n const onPointerEnter = React.useCallback(\n (event: React.PointerEvent) => {\n if (event.pointerType !== \"mouse\") return;\n clear();\n timer.current = setTimeout(() => {\n timer.current = null;\n openRef.current();\n }, openDelay);\n },\n [clear, openDelay],\n );\n\n const onPointerLeave = React.useCallback(\n (event: React.PointerEvent) => {\n if (event.pointerType !== \"mouse\") return;\n clear();\n if (closeDelay > 0) {\n timer.current = setTimeout(() => {\n timer.current = null;\n closeRef.current();\n }, closeDelay);\n } else {\n closeRef.current();\n }\n },\n [clear, closeDelay],\n );\n\n return { onPointerEnter, onPointerLeave };\n}\n"],"mappings":";AAEA,YAAY,WAAW;AAgBhB,SAAS,eAAe,MAKP;AACtB,QAAM,EAAE,QAAQ,SAAS,YAAY,KAAK,aAAa,EAAE,IAAI;AAE7D,QAAM,UAAU,MAAM,OAAO,MAAM;AACnC,QAAM,WAAW,MAAM,OAAO,OAAO;AACrC,QAAM,UAAU,MAAM;AACpB,YAAQ,UAAU;AAClB,aAAS,UAAU;AAAA,EACrB,CAAC;AAED,QAAM,QAAQ,MAAM,OAA6C,IAAI;AACrE,QAAM,QAAQ,MAAM,YAAY,MAAM;AACpC,QAAI,MAAM,WAAW,MAAM;AACzB,mBAAa,MAAM,OAAO;AAC1B,YAAM,UAAU;AAAA,IAClB;AAAA,EACF,GAAG,CAAC,CAAC;AACL,QAAM,UAAU,MAAM,OAAO,CAAC,KAAK,CAAC;AAEpC,QAAM,iBAAiB,MAAM;AAAA,IAC3B,CAAC,UAA8B;AAC7B,UAAI,MAAM,gBAAgB,QAAS;AACnC,YAAM;AACN,YAAM,UAAU,WAAW,MAAM;AAC/B,cAAM,UAAU;AAChB,gBAAQ,QAAQ;AAAA,MAClB,GAAG,SAAS;AAAA,IACd;AAAA,IACA,CAAC,OAAO,SAAS;AAAA,EACnB;AAEA,QAAM,iBAAiB,MAAM;AAAA,IAC3B,CAAC,UAA8B;AAC7B,UAAI,MAAM,gBAAgB,QAAS;AACnC,YAAM;AACN,UAAI,aAAa,GAAG;AAClB,cAAM,UAAU,WAAW,MAAM;AAC/B,gBAAM,UAAU;AAChB,mBAAS,QAAQ;AAAA,QACnB,GAAG,UAAU;AAAA,MACf,OAAO;AACL,iBAAS,QAAQ;AAAA,MACnB;AAAA,IACF;AAAA,IACA,CAAC,OAAO,UAAU;AAAA,EACpB;AAEA,SAAO,EAAE,gBAAgB,eAAe;AAC1C;","names":[]}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
function useRovingFocus(opts) {
|
|
4
|
+
const { count, orientation = "vertical", loop = true, initial = 0 } = opts;
|
|
5
|
+
const [activeIndex, setActiveIndex] = React.useState(initial);
|
|
6
|
+
const next = (dir) => setActiveIndex((i) => {
|
|
7
|
+
let n = i + dir;
|
|
8
|
+
if (n < 0) n = loop ? count - 1 : 0;
|
|
9
|
+
else if (n >= count) n = loop ? 0 : count - 1;
|
|
10
|
+
return n;
|
|
11
|
+
});
|
|
12
|
+
const onKeyDown = (event) => {
|
|
13
|
+
const horiz = orientation === "horizontal" || orientation === "both";
|
|
14
|
+
const vert = orientation === "vertical" || orientation === "both";
|
|
15
|
+
if (vert && event.key === "ArrowDown") {
|
|
16
|
+
event.preventDefault();
|
|
17
|
+
next(1);
|
|
18
|
+
} else if (vert && event.key === "ArrowUp") {
|
|
19
|
+
event.preventDefault();
|
|
20
|
+
next(-1);
|
|
21
|
+
} else if (horiz && event.key === "ArrowRight") {
|
|
22
|
+
event.preventDefault();
|
|
23
|
+
next(1);
|
|
24
|
+
} else if (horiz && event.key === "ArrowLeft") {
|
|
25
|
+
event.preventDefault();
|
|
26
|
+
next(-1);
|
|
27
|
+
} else if (event.key === "Home") {
|
|
28
|
+
event.preventDefault();
|
|
29
|
+
setActiveIndex(0);
|
|
30
|
+
} else if (event.key === "End") {
|
|
31
|
+
event.preventDefault();
|
|
32
|
+
setActiveIndex(count - 1);
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
return { activeIndex, setActiveIndex, onKeyDown };
|
|
36
|
+
}
|
|
37
|
+
export {
|
|
38
|
+
useRovingFocus
|
|
39
|
+
};
|
|
40
|
+
//# sourceMappingURL=use-roving-focus.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/primitives/use-roving-focus.ts"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\n\n/**\n * Arrow-key roving focus over a set of items — the dep-light core of menus,\n * tabs, radio-groups and toolbars (replacing `@radix-ui/react-roving-focus`).\n * Returns an `onKeyDown` to spread on the container and the active index; items\n * should set `tabIndex={index === activeIndex ? 0 : -1}`.\n *\n * Mirrors the keyboard navigation Rust drives with a focused-index signal.\n */\nexport function useRovingFocus(opts: {\n count: number;\n orientation?: \"horizontal\" | \"vertical\" | \"both\";\n loop?: boolean;\n initial?: number;\n}): {\n activeIndex: number;\n setActiveIndex: (i: number) => void;\n onKeyDown: (event: React.KeyboardEvent) => void;\n} {\n const { count, orientation = \"vertical\", loop = true, initial = 0 } = opts;\n const [activeIndex, setActiveIndex] = React.useState(initial);\n\n const next = (dir: 1 | -1) =>\n setActiveIndex((i) => {\n let n = i + dir;\n if (n < 0) n = loop ? count - 1 : 0;\n else if (n >= count) n = loop ? 0 : count - 1;\n return n;\n });\n\n const onKeyDown = (event: React.KeyboardEvent) => {\n const horiz = orientation === \"horizontal\" || orientation === \"both\";\n const vert = orientation === \"vertical\" || orientation === \"both\";\n if (vert && event.key === \"ArrowDown\") {\n event.preventDefault();\n next(1);\n } else if (vert && event.key === \"ArrowUp\") {\n event.preventDefault();\n next(-1);\n } else if (horiz && event.key === \"ArrowRight\") {\n event.preventDefault();\n next(1);\n } else if (horiz && event.key === \"ArrowLeft\") {\n event.preventDefault();\n next(-1);\n } else if (event.key === \"Home\") {\n event.preventDefault();\n setActiveIndex(0);\n } else if (event.key === \"End\") {\n event.preventDefault();\n setActiveIndex(count - 1);\n }\n };\n\n return { activeIndex, setActiveIndex, onKeyDown };\n}\n"],"mappings":";AAEA,YAAY,WAAW;AAUhB,SAAS,eAAe,MAS7B;AACA,QAAM,EAAE,OAAO,cAAc,YAAY,OAAO,MAAM,UAAU,EAAE,IAAI;AACtE,QAAM,CAAC,aAAa,cAAc,IAAI,MAAM,SAAS,OAAO;AAE5D,QAAM,OAAO,CAAC,QACZ,eAAe,CAAC,MAAM;AACpB,QAAI,IAAI,IAAI;AACZ,QAAI,IAAI,EAAG,KAAI,OAAO,QAAQ,IAAI;AAAA,aACzB,KAAK,MAAO,KAAI,OAAO,IAAI,QAAQ;AAC5C,WAAO;AAAA,EACT,CAAC;AAEH,QAAM,YAAY,CAAC,UAA+B;AAChD,UAAM,QAAQ,gBAAgB,gBAAgB,gBAAgB;AAC9D,UAAM,OAAO,gBAAgB,cAAc,gBAAgB;AAC3D,QAAI,QAAQ,MAAM,QAAQ,aAAa;AACrC,YAAM,eAAe;AACrB,WAAK,CAAC;AAAA,IACR,WAAW,QAAQ,MAAM,QAAQ,WAAW;AAC1C,YAAM,eAAe;AACrB,WAAK,EAAE;AAAA,IACT,WAAW,SAAS,MAAM,QAAQ,cAAc;AAC9C,YAAM,eAAe;AACrB,WAAK,CAAC;AAAA,IACR,WAAW,SAAS,MAAM,QAAQ,aAAa;AAC7C,YAAM,eAAe;AACrB,WAAK,EAAE;AAAA,IACT,WAAW,MAAM,QAAQ,QAAQ;AAC/B,YAAM,eAAe;AACrB,qBAAe,CAAC;AAAA,IAClB,WAAW,MAAM,QAAQ,OAAO;AAC9B,YAAM,eAAe;AACrB,qBAAe,QAAQ,CAAC;AAAA,IAC1B;AAAA,EACF;AAEA,SAAO,EAAE,aAAa,gBAAgB,UAAU;AAClD;","names":[]}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
function walkElements(node, visit) {
|
|
3
|
+
if (Array.isArray(node)) {
|
|
4
|
+
for (const child of node) walkElements(child, visit);
|
|
5
|
+
return;
|
|
6
|
+
}
|
|
7
|
+
if (!React.isValidElement(node)) return;
|
|
8
|
+
if (node.type === React.Fragment || visit(node)) walkElements(node.props.children, visit);
|
|
9
|
+
}
|
|
10
|
+
export {
|
|
11
|
+
walkElements
|
|
12
|
+
};
|
|
13
|
+
//# sourceMappingURL=walk-elements.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/primitives/walk-elements.ts"],"sourcesContent":["import * as React from \"react\";\n\nexport type ElementWithChildren = React.ReactElement<{ children?: unknown }>;\n\n/**\n * Visits the elements of a children tree, looking through arrays and\n * fragments; `visit` returns whether to descend into that element's children.\n *\n * Only elements, arrays and fragments are walked. `children` is whatever the\n * caller passed — a render function, or a plain object an i18n component\n * interpolates (`{ count }` under Trans) — and `React.Children` throws on an\n * object, so anything else is skipped rather than handed to it.\n */\nexport function walkElements(node: unknown, visit: (element: ElementWithChildren) => boolean): void {\n if (Array.isArray(node)) {\n for (const child of node) walkElements(child, visit);\n return;\n }\n if (!React.isValidElement<{ children?: unknown }>(node)) return;\n if (node.type === React.Fragment || visit(node)) walkElements(node.props.children, visit);\n}\n"],"mappings":"AAAA,YAAY,WAAW;AAahB,SAAS,aAAa,MAAe,OAAwD;AAClG,MAAI,MAAM,QAAQ,IAAI,GAAG;AACvB,eAAW,SAAS,KAAM,cAAa,OAAO,KAAK;AACnD;AAAA,EACF;AACA,MAAI,CAAC,MAAM,eAAuC,IAAI,EAAG;AACzD,MAAI,KAAK,SAAS,MAAM,YAAY,MAAM,IAAI,EAAG,cAAa,KAAK,MAAM,UAAU,KAAK;AAC1F;","names":[]}
|