@nsaa/core 0.1.0-alpha.1 → 0.1.0-alpha.3
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 +30 -8
- package/dist/_virtual/_rolldown/runtime.js +13 -0
- package/dist/accordion/accordion.d.ts +7 -0
- package/dist/accordion/accordion.js +54 -0
- package/dist/accordion/index.d.ts +3 -0
- package/dist/alert/alert.d.ts +19 -0
- package/dist/alert/alert.js +58 -0
- package/dist/alert/index.d.ts +3 -0
- package/dist/alert-dialog/alert-dialog.d.ts +19 -0
- package/dist/alert-dialog/alert-dialog.js +106 -0
- package/dist/alert-dialog/index.d.ts +3 -0
- package/dist/avatar/avatar.d.ts +12 -0
- package/dist/avatar/avatar.js +50 -0
- package/dist/avatar/index.d.ts +3 -0
- package/dist/badge/badge.d.ts +8 -0
- package/dist/badge/badge.js +29 -0
- package/dist/badge/index.d.ts +3 -0
- package/dist/button/button.d.ts +25 -0
- package/dist/button/button.js +136 -0
- package/dist/button/index.d.ts +3 -0
- package/dist/calendar/calendar.d.ts +11 -0
- package/dist/calendar/calendar.js +131 -0
- package/dist/calendar/index.d.ts +3 -0
- package/dist/card/card.d.ts +12 -0
- package/dist/card/card.js +55 -0
- package/dist/card/index.d.ts +3 -0
- package/dist/checkbox/checkbox.d.ts +4 -0
- package/dist/checkbox/checkbox.js +35 -0
- package/dist/checkbox/index.d.ts +3 -0
- package/dist/collapsible/collapsible.d.ts +6 -0
- package/dist/collapsible/collapsible.js +24 -0
- package/dist/collapsible/index.d.ts +3 -0
- package/dist/combobox/combobox.d.ts +27 -0
- package/dist/combobox/combobox.js +226 -0
- package/dist/combobox/index.d.ts +3 -0
- package/dist/command/command.d.ts +20 -0
- package/dist/command/command.js +116 -0
- package/dist/command/index.d.ts +3 -0
- package/dist/context-menu/context-menu.d.ts +30 -0
- package/dist/context-menu/context-menu.js +173 -0
- package/dist/context-menu/index.d.ts +3 -0
- package/dist/dialog/dialog.d.ts +18 -0
- package/dist/dialog/dialog.js +112 -0
- package/dist/dialog/index.d.ts +3 -0
- package/dist/direction.d.ts +3 -0
- package/dist/direction.js +2 -0
- package/dist/drawer/drawer.d.ts +17 -0
- package/dist/drawer/drawer.js +136 -0
- package/dist/drawer/index.d.ts +3 -0
- package/dist/dropdown-menu/dropdown-menu.d.ts +30 -0
- package/dist/dropdown-menu/dropdown-menu.js +177 -0
- package/dist/dropdown-menu/index.d.ts +3 -0
- package/dist/empty/empty.d.ts +35 -0
- package/dist/empty/empty.js +93 -0
- package/dist/empty/index.d.ts +3 -0
- package/dist/field/field.d.ts +26 -0
- package/dist/field/field.js +109 -0
- package/dist/field/index.d.ts +3 -0
- package/dist/index.d.ts +90 -674
- package/dist/index.js +50 -3877
- package/dist/input/index.d.ts +3 -0
- package/dist/input/input.d.ts +4 -0
- package/dist/input/input.js +14 -0
- package/dist/input-group/index.d.ts +3 -0
- package/dist/input-group/input-group.d.ts +19 -0
- package/dist/input-group/input-group.js +78 -0
- package/dist/input-otp/index.d.ts +3 -0
- package/dist/input-otp/input-otp.d.ts +12 -0
- package/dist/input-otp/input-otp.js +73 -0
- package/dist/kbd/index.d.ts +3 -0
- package/dist/kbd/kbd.d.ts +5 -0
- package/dist/kbd/kbd.js +19 -0
- package/dist/label/index.d.ts +3 -0
- package/dist/label/label.d.ts +4 -0
- package/dist/label/label.js +13 -0
- package/dist/legacy/icon-button/icon-swap.js +58 -0
- package/dist/lib/cn.d.ts +3 -0
- package/dist/{cn-DxFBFdwT.js → lib/cn.js} +1 -1
- package/dist/lib/control-defaults.js +8 -0
- package/dist/lib/icon-swap.d.ts +15 -0
- package/dist/lib/icon-swap.js +26 -0
- package/dist/lib/input-modality.js +39 -0
- package/dist/lib/merge-refs.js +21 -0
- package/dist/lib/opener.js +29 -0
- package/dist/lib/shortcut.js +50 -0
- package/dist/lib/track-input-modality.d.ts +2 -0
- package/dist/lib/track-input-modality.js +4 -0
- package/dist/lib/tv.js +7 -0
- package/dist/lib/use-press.js +48 -0
- package/dist/lib/user-action.js +13 -0
- package/dist/loaders/arc.d.ts +6 -0
- package/dist/loaders/arc.js +31 -0
- package/dist/loaders/atom.d.ts +5 -0
- package/dist/loaders/atom.js +70 -0
- package/dist/loaders/blocks.d.ts +8 -0
- package/dist/loaders/blocks.js +74 -0
- package/dist/loaders/bouncing-dots.d.ts +3 -0
- package/dist/loaders/bouncing-dots.js +45 -0
- package/dist/loaders/cap.d.ts +6 -0
- package/dist/loaders/cap.js +7 -0
- package/dist/loaders/cascade.d.ts +5 -0
- package/dist/loaders/cascade.js +50 -0
- package/dist/loaders/circular-dots.d.ts +3 -0
- package/dist/loaders/circular-dots.js +40 -0
- package/dist/loaders/classic-v2.d.ts +3 -0
- package/dist/loaders/classic-v2.js +41 -0
- package/dist/loaders/classic.d.ts +3 -0
- package/dist/loaders/classic.js +48 -0
- package/dist/loaders/clock.d.ts +5 -0
- package/dist/loaders/clock.js +31 -0
- package/dist/loaders/comet.d.ts +5 -0
- package/dist/loaders/comet.js +52 -0
- package/dist/loaders/compass.d.ts +3 -0
- package/dist/loaders/compass.js +39 -0
- package/dist/loaders/dual.d.ts +6 -0
- package/dist/loaders/dual.js +47 -0
- package/dist/loaders/easing.d.ts +6 -0
- package/dist/loaders/easing.js +34 -0
- package/dist/loaders/eclipse.d.ts +3 -0
- package/dist/loaders/eclipse.js +69 -0
- package/dist/loaders/fade.js +28 -0
- package/dist/loaders/flip.d.ts +3 -0
- package/dist/loaders/flip.js +41 -0
- package/dist/{frame-CK9IBOZB.js → loaders/frame.js} +2 -50
- package/dist/loaders/gather.d.ts +3 -0
- package/dist/loaders/gather.js +85 -0
- package/dist/loaders/leap.d.ts +3 -0
- package/dist/loaders/leap.js +67 -0
- package/dist/loaders/linear-dots.d.ts +3 -0
- package/dist/loaders/linear-dots.js +54 -0
- package/dist/loaders/loading.d.ts +3 -0
- package/dist/loaders/loading.js +64 -0
- package/dist/loaders/morph.d.ts +3 -0
- package/dist/loaders/morph.js +40 -0
- package/dist/loaders/motion.d.ts +35 -0
- package/dist/loaders/motion.js +51 -0
- package/dist/loaders/orbit.d.ts +5 -0
- package/dist/loaders/orbit.js +44 -0
- package/dist/loaders/pulse.d.ts +3 -0
- package/dist/loaders/pulse.js +52 -0
- package/dist/loaders/radar.d.ts +5 -0
- package/dist/loaders/radar.js +64 -0
- package/dist/loaders/ring.d.ts +6 -0
- package/dist/loaders/ring.js +38 -0
- package/dist/loaders/ripple.d.ts +8 -0
- package/dist/loaders/ripple.js +57 -0
- package/dist/loaders/slide.d.ts +3 -0
- package/dist/loaders/slide.js +74 -0
- package/dist/loaders/snake.d.ts +6 -0
- package/dist/loaders/snake.js +59 -0
- package/dist/loaders/spinners.d.ts +61 -0
- package/dist/loaders/spinners.js +63 -0
- package/dist/loaders/swirl.d.ts +3 -0
- package/dist/loaders/swirl.js +50 -0
- package/dist/loaders/text/shared.js +25 -0
- package/dist/loaders/text/text-loaders.d.ts +48 -0
- package/dist/loaders/text/text-loaders.js +723 -0
- package/dist/loaders/trace.d.ts +6 -0
- package/dist/loaders/trace.js +46 -0
- package/dist/loaders/types.d.ts +10 -0
- package/dist/loaders/wave.d.ts +8 -0
- package/dist/loaders/wave.js +59 -0
- package/dist/loaders-text.d.ts +2 -47
- package/dist/loaders-text.js +1 -743
- package/dist/loaders.d.ts +36 -132
- package/dist/loaders.js +35 -1096
- package/dist/native-select/index.d.ts +3 -0
- package/dist/native-select/native-select.d.ts +9 -0
- package/dist/native-select/native-select.js +44 -0
- package/dist/pagination/index.d.ts +3 -0
- package/dist/pagination/pagination.d.ts +18 -0
- package/dist/pagination/pagination.js +134 -0
- package/dist/popover/index.d.ts +3 -0
- package/dist/popover/popover.d.ts +10 -0
- package/dist/popover/popover.js +54 -0
- package/dist/radio-group/index.d.ts +3 -0
- package/dist/radio-group/radio-group.d.ts +6 -0
- package/dist/radio-group/radio-group.js +33 -0
- package/dist/select/index.d.ts +3 -0
- package/dist/select/select.d.ts +16 -0
- package/dist/select/select.js +148 -0
- package/dist/separator/index.d.ts +3 -0
- package/dist/separator/separator.d.ts +4 -0
- package/dist/separator/separator.js +15 -0
- package/dist/skeleton/index.d.ts +3 -0
- package/dist/skeleton/skeleton.d.ts +10 -0
- package/dist/skeleton/skeleton.js +24 -0
- package/dist/slider/index.d.ts +3 -0
- package/dist/slider/slider.d.ts +4 -0
- package/dist/slider/slider.js +39 -0
- package/dist/sound/lazy-uisfx.d.ts +20 -0
- package/dist/sound/lazy-uisfx.js +68 -0
- package/dist/sound/sound-map.d.ts +72 -0
- package/dist/sound/sound-map.js +76 -0
- package/dist/sound/sound.d.ts +38 -0
- package/dist/sound/sound.js +136 -0
- package/dist/sound/uisfx.d.ts +41 -0
- package/dist/sound/uisfx.js +59 -0
- package/dist/sound-uisfx.d.ts +2 -37
- package/dist/sound-uisfx.js +1 -58
- package/dist/sources/accordion.css +3 -0
- package/dist/sources/alert-dialog.css +26 -0
- package/dist/sources/alert.css +8 -0
- package/dist/sources/avatar.css +3 -0
- package/dist/sources/badge.css +3 -0
- package/dist/sources/button.css +24 -0
- package/dist/sources/calendar.css +25 -0
- package/dist/sources/card.css +3 -0
- package/dist/sources/checkbox.css +6 -0
- package/dist/sources/collapsible.css +2 -0
- package/dist/sources/combobox.css +28 -0
- package/dist/sources/command.css +30 -0
- package/dist/sources/context-menu.css +6 -0
- package/dist/sources/dialog.css +26 -0
- package/dist/sources/drawer.css +6 -0
- package/dist/sources/dropdown-menu.css +6 -0
- package/dist/sources/empty.css +3 -0
- package/dist/sources/field.css +5 -0
- package/dist/sources/input-group.css +27 -0
- package/dist/sources/input-otp.css +8 -0
- package/dist/sources/input.css +3 -0
- package/dist/sources/kbd.css +3 -0
- package/dist/sources/label.css +3 -0
- package/dist/sources/loaders-text.css +8 -0
- package/dist/sources/loaders.css +40 -0
- package/dist/sources/native-select.css +3 -0
- package/dist/sources/pagination.css +25 -0
- package/dist/sources/popover.css +3 -0
- package/dist/sources/radio-group.css +6 -0
- package/dist/sources/select.css +6 -0
- package/dist/sources/separator.css +3 -0
- package/dist/sources/skeleton.css +3 -0
- package/dist/sources/slider.css +6 -0
- package/dist/sources/spinner.css +20 -0
- package/dist/sources/switch.css +6 -0
- package/dist/sources/table.css +3 -0
- package/dist/sources/tabs.css +3 -0
- package/dist/sources/textarea.css +3 -0
- package/dist/sources/toast.css +25 -0
- package/dist/sources/toggle-group.css +32 -0
- package/dist/sources/toolbar.css +34 -0
- package/dist/sources/tooltip.css +3 -0
- package/dist/spinner/index.d.ts +5 -0
- package/dist/spinner/loader-provider.d.ts +16 -0
- package/dist/spinner/loader-provider.js +28 -0
- package/dist/spinner/registry.d.ts +9 -0
- package/dist/spinner/registry.js +15 -0
- package/dist/spinner/spinner.d.ts +26 -0
- package/dist/spinner/spinner.js +143 -0
- package/dist/switch/index.d.ts +3 -0
- package/dist/switch/switch.d.ts +6 -0
- package/dist/switch/switch.js +25 -0
- package/dist/table/index.d.ts +3 -0
- package/dist/table/table.d.ts +17 -0
- package/dist/table/table.js +66 -0
- package/dist/tabs/index.d.ts +3 -0
- package/dist/tabs/tabs.d.ts +11 -0
- package/dist/tabs/tabs.js +46 -0
- package/dist/textarea/index.d.ts +3 -0
- package/dist/textarea/textarea.d.ts +4 -0
- package/dist/textarea/textarea.js +12 -0
- package/dist/toast/index.d.ts +3 -0
- package/dist/toast/toast.d.ts +17 -0
- package/dist/toast/toast.js +211 -0
- package/dist/toggle-group/index.d.ts +3 -0
- package/dist/toggle-group/toggle-group.d.ts +64 -0
- package/dist/toggle-group/toggle-group.js +95 -0
- package/dist/tokens/dist/motion.d.ts +24 -0
- package/dist/{motion-NQq2BA78.js → tokens/dist/motion.js} +1 -1
- package/dist/toolbar/index.d.ts +3 -0
- package/dist/toolbar/overflow.d.ts +4 -0
- package/dist/toolbar/overflow.js +242 -0
- package/dist/toolbar/toolbar-item.d.ts +29 -0
- package/dist/toolbar/toolbar-item.js +71 -0
- package/dist/toolbar/toolbar.d.ts +73 -0
- package/dist/toolbar/toolbar.js +250 -0
- package/dist/tooltip/index.d.ts +3 -0
- package/dist/tooltip/tooltip.d.ts +7 -0
- package/dist/tooltip/tooltip.js +41 -0
- package/package.json +9 -5
- package/src/feel.css +52 -0
- package/src/styles-base.css +142 -0
- package/src/styles.css +3 -133
- package/dist/registry-2PyPW9Xw.js +0 -390
- package/dist/registry-DjfWNw3R.d.ts +0 -18
- package/dist/sound-DiYNAnR_.d.ts +0 -61
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cn } from "../lib/cn.js";
|
|
3
|
+
import { ControlDefaultsContext, useControlDefaults } from "../lib/control-defaults.js";
|
|
4
|
+
import { buttonVariants } from "../button/button.js";
|
|
5
|
+
import { Input } from "../input/input.js";
|
|
6
|
+
import { DropdownMenuCheckboxItem, DropdownMenuItem, DropdownMenuShortcut } from "../dropdown-menu/dropdown-menu.js";
|
|
7
|
+
import { tv } from "../lib/tv.js";
|
|
8
|
+
import { ariaShortcut, formatShortcut } from "../lib/shortcut.js";
|
|
9
|
+
import { TooltipProvider } from "../tooltip/tooltip.js";
|
|
10
|
+
import { ToolbarItem } from "./toolbar-item.js";
|
|
11
|
+
import { OverflowContext, OverflowMore, OverflowStore, useOverflowUnit } from "./overflow.js";
|
|
12
|
+
import { mergeRefs } from "../lib/merge-refs.js";
|
|
13
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
14
|
+
import { useCallback, useEffect, useLayoutEffect, useRef, useState } from "react";
|
|
15
|
+
import { Toolbar } from "@base-ui/react/toolbar";
|
|
16
|
+
// src/toolbar/toolbar.tsx
|
|
17
|
+
const toolbarStyles = tv({
|
|
18
|
+
base: "flex items-center gap-0.5 data-[orientation=vertical]:flex-col",
|
|
19
|
+
variants: {
|
|
20
|
+
surface: {
|
|
21
|
+
none: "",
|
|
22
|
+
floating: "nsaa-surface w-fit rounded-surface p-1 shadow-default"
|
|
23
|
+
},
|
|
24
|
+
overflow: {
|
|
25
|
+
menu: "flex-nowrap",
|
|
26
|
+
none: "flex-wrap",
|
|
27
|
+
scroll: [
|
|
28
|
+
"snap-x snap-proximity scroll-px-4 overflow-x-auto overscroll-x-contain p-1 pointer-coarse:py-1.5 [scrollbar-width:none] [&>*]:snap-start",
|
|
29
|
+
"[--fade-l:0px] [--fade-r:0px] data-fade-left:[--fade-l:--spacing(4)] data-fade-right:[--fade-r:--spacing(4)]",
|
|
30
|
+
"[mask-image:linear-gradient(to_right,transparent,black_var(--fade-l),black_calc(100%-var(--fade-r)),transparent)]"
|
|
31
|
+
]
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
defaultVariants: {
|
|
35
|
+
surface: "none",
|
|
36
|
+
overflow: "menu"
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
/** Marks which edges of a scrolling toolbar hide items (`data-fade-left` / `data-fade-right`). */
|
|
40
|
+
function useScrollEdges(enabled) {
|
|
41
|
+
const node = useRef(null);
|
|
42
|
+
useEffect(() => {
|
|
43
|
+
const el = node.current;
|
|
44
|
+
if (!enabled || !el) return;
|
|
45
|
+
const update = () => {
|
|
46
|
+
const max = el.scrollWidth - el.clientWidth;
|
|
47
|
+
const fromLeft = getComputedStyle(el).direction === "rtl" ? max + el.scrollLeft : el.scrollLeft;
|
|
48
|
+
el.toggleAttribute("data-fade-left", fromLeft > 1);
|
|
49
|
+
el.toggleAttribute("data-fade-right", max - fromLeft > 1);
|
|
50
|
+
};
|
|
51
|
+
update();
|
|
52
|
+
el.addEventListener("scroll", update, { passive: true });
|
|
53
|
+
const observer = typeof ResizeObserver === "undefined" ? void 0 : new ResizeObserver(update);
|
|
54
|
+
observer?.observe(el);
|
|
55
|
+
return () => {
|
|
56
|
+
el.removeEventListener("scroll", update);
|
|
57
|
+
observer?.disconnect();
|
|
58
|
+
};
|
|
59
|
+
}, [enabled]);
|
|
60
|
+
return node;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Groups related controls behind one Tab stop, with arrow keys, Home and End moving between items
|
|
64
|
+
* (Base UI `Toolbar`). Items get the toolbar's size, swap icons instantly, change colour without
|
|
65
|
+
* transitions and stay silent unless `sound` is set: a toolbar is high-frequency.
|
|
66
|
+
*/
|
|
67
|
+
function ToolbarRoot({ size = "sm", surface = "none", overflow = "menu", overflowLabel = "More", sound = false, orientation = "horizontal", className, children, onKeyDown, ref, ...props }) {
|
|
68
|
+
const edges = useScrollEdges(overflow === "scroll");
|
|
69
|
+
const root = useRef(null);
|
|
70
|
+
const setRef = useCallback((el) => mergeRefs(root, edges, ref)(el), [edges, ref]);
|
|
71
|
+
const [store] = useState(() => new OverflowStore());
|
|
72
|
+
const collapses = overflow === "menu" && orientation === "horizontal";
|
|
73
|
+
useLayoutEffect(() => {
|
|
74
|
+
if (collapses && root.current) store.layout(root.current);
|
|
75
|
+
});
|
|
76
|
+
useEffect(() => {
|
|
77
|
+
const el = root.current;
|
|
78
|
+
if (!collapses || !el || typeof ResizeObserver === "undefined") return;
|
|
79
|
+
const observer = new ResizeObserver(() => store.layout(el));
|
|
80
|
+
observer.observe(el);
|
|
81
|
+
if (el.parentElement) observer.observe(el.parentElement);
|
|
82
|
+
return () => observer.disconnect();
|
|
83
|
+
}, [collapses, store]);
|
|
84
|
+
const handleKeyDown = (event) => {
|
|
85
|
+
onKeyDown?.(event);
|
|
86
|
+
if (event.defaultPrevented || event.key !== "Home" && event.key !== "End") return;
|
|
87
|
+
if (event.target.closest("input, textarea, [contenteditable]")) return;
|
|
88
|
+
const items = [...event.currentTarget.querySelectorAll("button:not(:disabled), a[href], input:not(:disabled)")];
|
|
89
|
+
const target = event.key === "Home" ? items[0] : items.at(-1);
|
|
90
|
+
if (!target) return;
|
|
91
|
+
event.preventDefault();
|
|
92
|
+
target.focus();
|
|
93
|
+
};
|
|
94
|
+
return /* @__PURE__ */ jsx(ControlDefaultsContext.Provider, {
|
|
95
|
+
value: {
|
|
96
|
+
size,
|
|
97
|
+
sound,
|
|
98
|
+
orientation,
|
|
99
|
+
iconTransition: "instant",
|
|
100
|
+
instantColors: true
|
|
101
|
+
},
|
|
102
|
+
children: /* @__PURE__ */ jsx(TooltipProvider, { children: /* @__PURE__ */ jsxs(Toolbar.Root, {
|
|
103
|
+
ref: setRef,
|
|
104
|
+
"data-slot": "toolbar",
|
|
105
|
+
"data-size": size,
|
|
106
|
+
"data-surface": surface,
|
|
107
|
+
orientation,
|
|
108
|
+
className: cn(toolbarStyles({
|
|
109
|
+
surface,
|
|
110
|
+
overflow
|
|
111
|
+
}), "data-[orientation=horizontal]:pointer-coarse:[&_:is(button,a)]:before:min-w-0", "data-[orientation=vertical]:pointer-coarse:[&_:is(button,a)]:before:min-h-0", className),
|
|
112
|
+
onKeyDown: handleKeyDown,
|
|
113
|
+
...props,
|
|
114
|
+
children: [/* @__PURE__ */ jsx(OverflowContext.Provider, {
|
|
115
|
+
value: collapses ? store : null,
|
|
116
|
+
children
|
|
117
|
+
}), collapses && /* @__PURE__ */ jsx(OverflowMore, {
|
|
118
|
+
store,
|
|
119
|
+
label: overflowLabel
|
|
120
|
+
})]
|
|
121
|
+
}) })
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
/** A toolbar button's form in the More menu: a menu item, or a checkbox item for a toggle. */
|
|
125
|
+
function buttonMenuItem(p, disabled) {
|
|
126
|
+
const label = p["aria-label"] ?? p.children;
|
|
127
|
+
const shortcut = typeof p.tooltip === "object" ? p.tooltip.shortcut : void 0;
|
|
128
|
+
if (p.pressed !== void 0 || p.onPressedChange !== void 0) return /* @__PURE__ */ jsxs(DropdownMenuCheckboxItem, {
|
|
129
|
+
"aria-keyshortcuts": shortcut ? ariaShortcut(shortcut) : void 0,
|
|
130
|
+
checked: p.pressed ?? false,
|
|
131
|
+
disabled,
|
|
132
|
+
onCheckedChange: (on, details) => p.onPressedChange?.(on, details),
|
|
133
|
+
children: [
|
|
134
|
+
p.icon,
|
|
135
|
+
label,
|
|
136
|
+
shortcut && /* @__PURE__ */ jsx(DropdownMenuShortcut, { children: formatShortcut(shortcut) })
|
|
137
|
+
]
|
|
138
|
+
});
|
|
139
|
+
return /* @__PURE__ */ jsxs(DropdownMenuItem, {
|
|
140
|
+
"aria-keyshortcuts": shortcut ? ariaShortcut(shortcut) : void 0,
|
|
141
|
+
disabled,
|
|
142
|
+
onClick: p.onClick,
|
|
143
|
+
children: [
|
|
144
|
+
p.icon,
|
|
145
|
+
label,
|
|
146
|
+
shortcut && /* @__PURE__ */ jsx(DropdownMenuShortcut, { children: formatShortcut(shortcut) })
|
|
147
|
+
]
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* A ghost Button (#84): icon-only (with a tooltip) when it has only an `icon`, labelled otherwise;
|
|
152
|
+
* a toggle with `pressed`. Disabled items stay focusable so people can discover them.
|
|
153
|
+
*/
|
|
154
|
+
function ToolbarButton({ disabled, overflowPriority, ...props }) {
|
|
155
|
+
const trigger = props["aria-haspopup"] !== void 0;
|
|
156
|
+
const { hidden, unitProps, placeholder } = useOverflowUnit({
|
|
157
|
+
kind: "item",
|
|
158
|
+
priority: trigger ? "never" : overflowPriority,
|
|
159
|
+
menu: buttonMenuItem(props, disabled)
|
|
160
|
+
});
|
|
161
|
+
if (hidden) return placeholder;
|
|
162
|
+
return /* @__PURE__ */ jsx(Toolbar.Button, {
|
|
163
|
+
...unitProps,
|
|
164
|
+
"data-slot": "toolbar-button",
|
|
165
|
+
disabled,
|
|
166
|
+
focusableWhenDisabled: true,
|
|
167
|
+
render: /* @__PURE__ */ jsx(ToolbarItem, { ...props })
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
const LINK_SIZE = {
|
|
171
|
+
xs: "xs",
|
|
172
|
+
sm: "sm",
|
|
173
|
+
md: "default",
|
|
174
|
+
lg: "lg",
|
|
175
|
+
xl: "lg"
|
|
176
|
+
};
|
|
177
|
+
/** A link styled like a ghost toolbar item. */
|
|
178
|
+
function ToolbarLink({ className, overflowPriority, ...props }) {
|
|
179
|
+
const { size } = useControlDefaults();
|
|
180
|
+
const { hidden, unitProps, placeholder } = useOverflowUnit({
|
|
181
|
+
kind: "item",
|
|
182
|
+
priority: overflowPriority,
|
|
183
|
+
menu: /* @__PURE__ */ jsx(DropdownMenuItem, {
|
|
184
|
+
render: /* @__PURE__ */ jsx("a", { href: props.href }),
|
|
185
|
+
children: props.children
|
|
186
|
+
})
|
|
187
|
+
});
|
|
188
|
+
if (hidden) return placeholder;
|
|
189
|
+
return /* @__PURE__ */ jsx(Toolbar.Link, {
|
|
190
|
+
...unitProps,
|
|
191
|
+
"data-slot": "toolbar-link",
|
|
192
|
+
className: cn(buttonVariants({
|
|
193
|
+
variant: "ghost",
|
|
194
|
+
size: LINK_SIZE[size ?? "md"]
|
|
195
|
+
}), "transition-none", className),
|
|
196
|
+
...props
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
/** A labelled cluster of items. */
|
|
200
|
+
function ToolbarGroup({ className, ...props }) {
|
|
201
|
+
return /* @__PURE__ */ jsx(Toolbar.Group, {
|
|
202
|
+
"data-slot": "toolbar-group",
|
|
203
|
+
role: "group",
|
|
204
|
+
className: cn("flex items-center gap-0.5 data-[orientation=vertical]:flex-col", className),
|
|
205
|
+
...props
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
/** 1 px `border-default`, 60% of the item height, perpendicular to the toolbar. */
|
|
209
|
+
function ToolbarSeparator({ className, ...props }) {
|
|
210
|
+
const { hidden, unitProps, placeholder } = useOverflowUnit({
|
|
211
|
+
kind: "separator",
|
|
212
|
+
menu: null
|
|
213
|
+
});
|
|
214
|
+
if (hidden) return placeholder;
|
|
215
|
+
return /* @__PURE__ */ jsx(Toolbar.Separator, {
|
|
216
|
+
...unitProps,
|
|
217
|
+
"data-slot": "toolbar-separator",
|
|
218
|
+
className: cn("shrink-0 self-stretch bg-border-default", "data-[orientation=vertical]:mx-0.5 data-[orientation=vertical]:w-px data-[orientation=vertical]:scale-y-60", "data-[orientation=horizontal]:my-0.5 data-[orientation=horizontal]:h-px data-[orientation=horizontal]:scale-x-60", className),
|
|
219
|
+
...props
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* A text input in the toolbar (search, find, a URL). At most one, placed last: arrow keys move
|
|
224
|
+
* the caret inside it (Base UI). The base-nova Input (#84) without its resting border; never
|
|
225
|
+
* collapses.
|
|
226
|
+
*/
|
|
227
|
+
function ToolbarInput({ className, ...props }) {
|
|
228
|
+
const { unitProps } = useOverflowUnit({
|
|
229
|
+
kind: "item",
|
|
230
|
+
priority: "never",
|
|
231
|
+
menu: null
|
|
232
|
+
});
|
|
233
|
+
return /* @__PURE__ */ jsx("span", {
|
|
234
|
+
...unitProps,
|
|
235
|
+
"data-slot": "toolbar-input",
|
|
236
|
+
className: "inline-flex min-w-0 shrink",
|
|
237
|
+
children: /* @__PURE__ */ jsx(Toolbar.Input, { render: /* @__PURE__ */ jsx(Input, {
|
|
238
|
+
className: cn("border-transparent dark:bg-transparent", className),
|
|
239
|
+
...props
|
|
240
|
+
}) })
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
const Toolbar$1 = Object.assign(ToolbarRoot, {
|
|
244
|
+
Button: ToolbarButton,
|
|
245
|
+
Input: ToolbarInput,
|
|
246
|
+
Link: ToolbarLink,
|
|
247
|
+
Group: ToolbarGroup,
|
|
248
|
+
Separator: ToolbarSeparator
|
|
249
|
+
});
|
|
250
|
+
export { Toolbar$1 as Toolbar };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
|
|
2
|
+
import { Tooltip } from "@base-ui/react/tooltip";
|
|
3
|
+
declare function TooltipProvider({ delay, ...props }: Tooltip.Provider.Props): import("react").JSX.Element;
|
|
4
|
+
declare function Tooltip$1({ ...props }: Tooltip.Root.Props): import("react").JSX.Element;
|
|
5
|
+
declare function TooltipTrigger({ ...props }: Tooltip.Trigger.Props): import("react").JSX.Element;
|
|
6
|
+
declare function TooltipContent({ className, side, sideOffset, align, alignOffset, children, ...props }: Tooltip.Popup.Props & Pick<Tooltip.Positioner.Props, "align" | "alignOffset" | "side" | "sideOffset">): import("react").JSX.Element;
|
|
7
|
+
export { Tooltip$1 as Tooltip, TooltipContent, TooltipProvider, TooltipTrigger };
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
"use client";
|
|
3
|
+
import { cn } from "../lib/cn.js";
|
|
4
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
import { Tooltip } from "@base-ui/react/tooltip";
|
|
6
|
+
// src/tooltip/tooltip.tsx
|
|
7
|
+
function TooltipProvider({ delay = 600, ...props }) {
|
|
8
|
+
return /* @__PURE__ */ jsx(Tooltip.Provider, {
|
|
9
|
+
"data-slot": "tooltip-provider",
|
|
10
|
+
delay,
|
|
11
|
+
...props
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
function Tooltip$1({ ...props }) {
|
|
15
|
+
return /* @__PURE__ */ jsx(Tooltip.Root, {
|
|
16
|
+
"data-slot": "tooltip",
|
|
17
|
+
...props
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
function TooltipTrigger({ ...props }) {
|
|
21
|
+
return /* @__PURE__ */ jsx(Tooltip.Trigger, {
|
|
22
|
+
"data-slot": "tooltip-trigger",
|
|
23
|
+
...props
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
function TooltipContent({ className, side = "top", sideOffset = 4, align = "center", alignOffset = 0, children, ...props }) {
|
|
27
|
+
return /* @__PURE__ */ jsx(Tooltip.Portal, { children: /* @__PURE__ */ jsx(Tooltip.Positioner, {
|
|
28
|
+
align,
|
|
29
|
+
alignOffset,
|
|
30
|
+
side,
|
|
31
|
+
sideOffset,
|
|
32
|
+
className: "isolate z-50",
|
|
33
|
+
children: /* @__PURE__ */ jsxs(Tooltip.Popup, {
|
|
34
|
+
"data-slot": "tooltip-content",
|
|
35
|
+
className: cn("z-50 inline-flex w-fit max-w-xs origin-(--transform-origin) items-center gap-1.5 rounded-md bg-foreground px-3 py-1.5 text-xs text-background has-data-[slot=kbd]:pr-1.5 data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 **:data-[slot=kbd]:relative **:data-[slot=kbd]:isolate **:data-[slot=kbd]:z-50 **:data-[slot=kbd]:rounded-sm data-[state=delayed-open]:animate-in data-[state=delayed-open]:fade-in-0 data-[state=delayed-open]:zoom-in-95 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", className),
|
|
36
|
+
...props,
|
|
37
|
+
children: [children, /* @__PURE__ */ jsx(Tooltip.Arrow, { className: "z-50 size-2.5 translate-y-[calc(-50%-2px)] rotate-45 rounded-[2px] bg-foreground fill-foreground data-[side=bottom]:top-1 data-[side=inline-end]:top-1/2! data-[side=inline-end]:-left-1 data-[side=inline-end]:-translate-y-1/2 data-[side=inline-start]:top-1/2! data-[side=inline-start]:-right-1 data-[side=inline-start]:-translate-y-1/2 data-[side=left]:top-1/2! data-[side=left]:-right-1 data-[side=left]:-translate-y-1/2 data-[side=right]:top-1/2! data-[side=right]:-left-1 data-[side=right]:-translate-y-1/2 data-[side=top]:-bottom-2.5" })]
|
|
38
|
+
})
|
|
39
|
+
}) });
|
|
40
|
+
}
|
|
41
|
+
export { Tooltip$1 as Tooltip, TooltipContent, TooltipProvider, TooltipTrigger };
|
package/package.json
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nsaa/core",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.3",
|
|
4
4
|
"description": "Nsaa core components: accessible, token-driven, animated to the Nsaa feel spec.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"sideEffects": [
|
|
8
8
|
"**/*.css",
|
|
9
|
-
"./dist/
|
|
9
|
+
"./dist/lib/track-input-modality.js",
|
|
10
|
+
"./dist/spinner/spinner.js"
|
|
10
11
|
],
|
|
11
12
|
"files": [
|
|
12
13
|
"dist",
|
|
13
14
|
"src/styles.css",
|
|
15
|
+
"src/styles-base.css",
|
|
14
16
|
"src/feel.css",
|
|
15
17
|
"README.md",
|
|
16
18
|
"LICENSE",
|
|
@@ -33,7 +35,9 @@
|
|
|
33
35
|
"types": "./dist/sound-uisfx.d.ts",
|
|
34
36
|
"import": "./dist/sound-uisfx.js"
|
|
35
37
|
},
|
|
36
|
-
"./styles.css": "./src/styles.css"
|
|
38
|
+
"./styles.css": "./src/styles.css",
|
|
39
|
+
"./styles-base.css": "./src/styles-base.css",
|
|
40
|
+
"./sources/*": "./dist/sources/*"
|
|
37
41
|
},
|
|
38
42
|
"dependencies": {
|
|
39
43
|
"@base-ui/react": "^1.9.0",
|
|
@@ -46,7 +50,7 @@
|
|
|
46
50
|
"tailwind-merge": "^3.7.0",
|
|
47
51
|
"tailwind-variants": "^3.3.1",
|
|
48
52
|
"uisfx": "0.4.0",
|
|
49
|
-
"@nsaa/tokens": "^0.1.0-alpha.
|
|
53
|
+
"@nsaa/tokens": "^0.1.0-alpha.3"
|
|
50
54
|
},
|
|
51
55
|
"devDependencies": {
|
|
52
56
|
"@testing-library/jest-dom": "^7.0.1",
|
|
@@ -77,7 +81,7 @@
|
|
|
77
81
|
"access": "public"
|
|
78
82
|
},
|
|
79
83
|
"scripts": {
|
|
80
|
-
"build": "tsdown",
|
|
84
|
+
"build": "tsdown && node ../../scripts/core-sources.mjs",
|
|
81
85
|
"dev": "tsdown --watch",
|
|
82
86
|
"typecheck": "tsc --noEmit",
|
|
83
87
|
"test": "vitest run",
|
package/src/feel.css
CHANGED
|
@@ -19,6 +19,58 @@
|
|
|
19
19
|
* own unlayered CSS still wins.
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
|
+
/* ------------------------------------------------------------------ coarse-pointer hit areas (#144)
|
|
23
|
+
DESIGN.md: 44 px targets on coarse pointers. An invisible ::before, centred, at least 44 × 44 and
|
|
24
|
+
never smaller than the control; layout is unchanged. ::before because base-nova already uses
|
|
25
|
+
::after (Tabs' line indicator, the Checkbox / Radio / Switch / Slider thumb hit expanders).
|
|
26
|
+
Tailwind's components layer, below utilities: the `position: relative` here never overrides a
|
|
27
|
+
control's own `absolute` (the Dialog close) or a Toolbar / Toggle Group override.
|
|
28
|
+
Inputs are replaced elements and take no pseudo-element, so on a coarse pointer they grow to
|
|
29
|
+
44 px tall instead; an Input Group grows as a whole. */
|
|
30
|
+
@layer components {
|
|
31
|
+
@media (pointer: coarse) {
|
|
32
|
+
:is(
|
|
33
|
+
.group\/button,
|
|
34
|
+
[data-slot="select-trigger"],
|
|
35
|
+
[data-slot="checkbox"],
|
|
36
|
+
[data-slot="radio-group-item"],
|
|
37
|
+
[data-slot="switch"],
|
|
38
|
+
[data-slot="slider-thumb"],
|
|
39
|
+
[data-slot="tabs-trigger"]
|
|
40
|
+
) {
|
|
41
|
+
position: relative;
|
|
42
|
+
|
|
43
|
+
&::before {
|
|
44
|
+
content: "";
|
|
45
|
+
position: absolute;
|
|
46
|
+
top: 50%;
|
|
47
|
+
left: 50%;
|
|
48
|
+
width: 100%;
|
|
49
|
+
height: 100%;
|
|
50
|
+
min-width: calc(var(--spacing) * 11);
|
|
51
|
+
min-height: calc(var(--spacing) * 11);
|
|
52
|
+
translate: -50% -50%;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
:is([data-slot="input"], [data-slot="input-group"]) {
|
|
57
|
+
min-height: calc(var(--spacing) * 11);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/* Neighbours closer than 44 px would share a hit area. Pagination links sit 2 px apart, so
|
|
61
|
+
along the row they keep their own width. Calendar days tile the grid edge to edge (every
|
|
62
|
+
point is some day's), so a day's hit area is its cell; the month arrows keep 44 px.
|
|
63
|
+
Toolbar and Toggle Group do the same along their axis (#75). */
|
|
64
|
+
[data-slot="pagination-content"] .group\/button::before {
|
|
65
|
+
min-width: 0;
|
|
66
|
+
}
|
|
67
|
+
[data-slot="calendar"] button[data-day]::before {
|
|
68
|
+
min-width: 0;
|
|
69
|
+
min-height: 0;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
22
74
|
@layer nsaa-feel {
|
|
23
75
|
:root {
|
|
24
76
|
--nsaa-time-scale: 1;
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* @nsaa/core styles without Tailwind sources (#144): tokens, the feel layer and the variants.
|
|
3
|
+
* For the smallest CSS, import this plus one sources file per component you use:
|
|
4
|
+
* @import "tailwindcss";
|
|
5
|
+
* @import "@nsaa/core/styles-base.css";
|
|
6
|
+
* @import "@nsaa/core/sources/button.css";
|
|
7
|
+
* @import "@nsaa/core/sources/dialog.css";
|
|
8
|
+
* `styles.css` is this file plus every component's sources.
|
|
9
|
+
*/
|
|
10
|
+
@import "@nsaa/tokens/css";
|
|
11
|
+
|
|
12
|
+
/* The feel layer (#108): motion for the base-nova components, keyed to data-slot. */
|
|
13
|
+
@import "./feel.css";
|
|
14
|
+
|
|
15
|
+
/* `dark:` follows the mode axis (`.dark` or `data-mode="dark"` on an ancestor), not the OS, so the
|
|
16
|
+
shadcn classes (#84) switch with the rest of the tokens. The nearest mode wins (#120): a light
|
|
17
|
+
panel inside a dark page is light, and a dark panel inside that light panel is dark again. */
|
|
18
|
+
@custom-variant dark (
|
|
19
|
+
&:where(
|
|
20
|
+
.dark,
|
|
21
|
+
[data-mode="dark"],
|
|
22
|
+
:is(.dark, [data-mode="dark"]) *:not([data-mode="light"], [data-mode="light"] *),
|
|
23
|
+
[data-mode="light"] [data-mode="dark"] *
|
|
24
|
+
)
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
/* shadcn's base layer (apps/v4 globals.css, `* { @apply border-border }`): a bare `border` class takes
|
|
28
|
+
the border token, not currentColor. Dialog's footer and the Drawer's edge rely on it (#84). */
|
|
29
|
+
@layer base {
|
|
30
|
+
*,
|
|
31
|
+
::after,
|
|
32
|
+
::before {
|
|
33
|
+
border-color: var(--border);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/* shadcn's orientation variants (shadcn/tailwind.css, which the shadcn CLI imports for its users).
|
|
38
|
+
Base UI sets `data-orientation`, not `data-horizontal`; Tabs, Separator and Field use these (#84).
|
|
39
|
+
shadcn's other data-* variants match Base UI's attributes as Tailwind's defaults already. */
|
|
40
|
+
@custom-variant data-horizontal {
|
|
41
|
+
&:where([data-orientation="horizontal"]) {
|
|
42
|
+
@slot;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
@custom-variant data-vertical {
|
|
47
|
+
&:where([data-orientation="vertical"]) {
|
|
48
|
+
@slot;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/* shadcn's `data-selected` (same file): only `data-selected="true"`. cmdk writes "true" / "false", so
|
|
53
|
+
Tailwind's default (attribute present) would highlight every Command item (#84). */
|
|
54
|
+
@custom-variant data-selected {
|
|
55
|
+
&:where([data-selected="true"]) {
|
|
56
|
+
@slot;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/* shadcn style hooks: `cn-rtl-flip` marks icons the shadcn CLI mirrors for right-to-left (the submenu
|
|
61
|
+
chevrons); `cn-input-otp` and two `cn-calendar-*` classes are styled by base-nova's style sheet, not
|
|
62
|
+
by the item's classes. */
|
|
63
|
+
@layer components {
|
|
64
|
+
.cn-rtl-flip:where(:dir(rtl)) {
|
|
65
|
+
rotate: 180deg;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/* Input OTP's root: shadcn's style-nova.css gives `cn-input-otp` a `gap-2` between the groups. */
|
|
69
|
+
.cn-input-otp {
|
|
70
|
+
gap: calc(var(--spacing) * 2);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/* Calendar with captionLayout="dropdown" (#136): style-nova.css draws the month and year
|
|
74
|
+
dropdowns' border and focus ring, and pads the caption label; the item's classes don't. */
|
|
75
|
+
.cn-calendar-dropdown-root {
|
|
76
|
+
border: 1px solid var(--input);
|
|
77
|
+
|
|
78
|
+
&:has(:focus) {
|
|
79
|
+
border-color: var(--ring);
|
|
80
|
+
box-shadow: 0 0 0 3px color-mix(in oklab, var(--ring) 50%, transparent);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.cn-calendar-caption-label {
|
|
85
|
+
height: calc(var(--spacing) * 6);
|
|
86
|
+
padding-right: calc(var(--spacing) * 1);
|
|
87
|
+
padding-left: calc(var(--spacing) * 1.5);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/* Hover only where hovering is real (fine pointers), never on disabled, busy or read-only controls. */
|
|
92
|
+
@custom-variant hover-fine {
|
|
93
|
+
@media (hover: hover) and (pointer: fine) {
|
|
94
|
+
&:hover:not([data-disabled], [data-loading], [data-readonly]) {
|
|
95
|
+
@slot;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/* Pressed look, never on disabled or busy controls. */
|
|
101
|
+
@custom-variant pressed {
|
|
102
|
+
&:active:not([data-disabled], [data-loading]) {
|
|
103
|
+
@slot;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/* shadcn's `no-scrollbar` (apps/v4 globals.css), used by the Combobox list (#84). `cn-menu-target`
|
|
108
|
+
and `cn-menu-translucent` on Select and Combobox popups are inert markers the shadcn CLI keeps for
|
|
109
|
+
its optional translucent-menu setting; nothing styles them by default, so neither do we. */
|
|
110
|
+
@utility no-scrollbar {
|
|
111
|
+
-ms-overflow-style: none;
|
|
112
|
+
scrollbar-width: none;
|
|
113
|
+
|
|
114
|
+
&::-webkit-scrollbar {
|
|
115
|
+
display: none;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/* Input OTP's fake caret (`animate-caret-blink`, #84). shadcn's apps/v4 globals.css defines it; the
|
|
120
|
+
base-nova registry item doesn't ship it, so core does (and the `input-otp` registry item adds it).
|
|
121
|
+
One blink is `duration.caret` (1.25 s, as shadcn's; #125). Under reduced motion the caret stays solid. */
|
|
122
|
+
@theme {
|
|
123
|
+
--animate-caret-blink: caret-blink var(--nsaa-duration-caret) ease-out infinite;
|
|
124
|
+
|
|
125
|
+
@keyframes caret-blink {
|
|
126
|
+
0%,
|
|
127
|
+
70%,
|
|
128
|
+
100% {
|
|
129
|
+
opacity: 1;
|
|
130
|
+
}
|
|
131
|
+
20%,
|
|
132
|
+
50% {
|
|
133
|
+
opacity: 0;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
@media (prefers-reduced-motion: reduce) {
|
|
139
|
+
.animate-caret-blink {
|
|
140
|
+
animation: none;
|
|
141
|
+
}
|
|
142
|
+
}
|