@music-vine/cadence 2.5.0 → 2.6.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.
@@ -94,6 +94,11 @@ import {
94
94
  import { PriceTag } from "./price-tag";
95
95
  import { RadioGroup, RadioGroupItem } from "./radio-group";
96
96
  import { FadeAway, ScrollArea, ScrollBar } from "./scroll-area";
97
+ import {
98
+ ScrollDrum,
99
+ ScrollDrumColumn,
100
+ ScrollDrumGroup
101
+ } from "./scroll-drum";
97
102
  import {
98
103
  Select,
99
104
  SelectContent,
@@ -233,6 +238,9 @@ export {
233
238
  RadioGroupItem,
234
239
  ScrollArea,
235
240
  ScrollBar,
241
+ ScrollDrum,
242
+ ScrollDrumColumn,
243
+ ScrollDrumGroup,
236
244
  Select,
237
245
  SelectContent,
238
246
  SelectContentPopper,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/components/index.ts"],
4
- "sourcesContent": ["/**\n * Cadence UI Components\n *\n * Import components from this module:\n * @example\n * import { Button, Input } from '@music-vine/cadence/ui';\n */\n\n// Accordion\nexport {\n Accordion,\n AccordionContent,\n type AccordionContentProps,\n AccordionItem,\n type AccordionItemProps,\n type AccordionProps,\n AccordionTrigger,\n type AccordionTriggerProps,\n type AccordionVariant,\n} from \"./accordion\";\n\n// Badge\nexport { Badge, type BadgeProps, badgeVariants } from \"./badge\";\n\n// Breadcrumb\nexport {\n Breadcrumb,\n BreadcrumbEllipsis,\n BreadcrumbItem,\n BreadcrumbLink,\n BreadcrumbList,\n BreadcrumbPage,\n BreadcrumbSeparator,\n} from \"./breadcrumb\";\n\n// Button\nexport {\n Button,\n type ButtonFontSize,\n type ButtonProps,\n type ButtonSize,\n buttonVariants,\n Loading,\n loadingVariants,\n type ResponsiveButtonFontSize,\n type ResponsiveButtonSize,\n} from \"./button\";\n\n// Card\nexport {\n Card,\n CardContent,\n CardDescription,\n CardFooter,\n CardHeader,\n CardTitle,\n} from \"./card\";\n\n// Carousel\nexport {\n Carousel,\n type CarouselApi,\n CarouselContent,\n CarouselItem,\n CarouselNext,\n CarouselPrevious,\n useCarousel,\n} from \"./carousel\";\nexport { CarouselDots } from \"./carousel-dots\";\n\n// Checkbox\nexport { Checkbox, DummyCheckbox } from \"./checkbox\";\n\n// Context Menu\nexport {\n ContextMenu,\n ContextMenuCheckboxItem,\n ContextMenuContent,\n ContextMenuGroup,\n ContextMenuItem,\n ContextMenuLabel,\n ContextMenuPortal,\n ContextMenuRadioGroup,\n ContextMenuRadioItem,\n ContextMenuSeparator,\n ContextMenuShortcut,\n ContextMenuSub,\n ContextMenuSubContent,\n ContextMenuSubTrigger,\n ContextMenuTrigger,\n} from \"./context-menu\";\n\n// Dialog\nexport {\n Dialog,\n DialogClose,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogOverlay,\n DialogPortal,\n DialogTitle,\n DialogTrigger,\n} from \"./dialog\";\n\n// Drawer\nexport {\n Drawer,\n DrawerClose,\n DrawerContent,\n DrawerDescription,\n DrawerFooter,\n DrawerHeader,\n DrawerOverlay,\n DrawerPortal,\n DrawerTitle,\n DrawerTrigger,\n} from \"./drawer\";\n\n// Input\nexport {\n ClearInputButton,\n Input,\n type InputProps,\n inputVariants,\n} from \"./input\";\n\n// Label\nexport { Label } from \"./label\";\n\n// Popover\nexport {\n Popover,\n PopoverAnchor,\n PopoverContent,\n PopoverTrigger,\n} from \"./popover\";\n\n// Price Tag\nexport { PriceTag, type PriceTagProps } from \"./price-tag\";\n\n// Radio Group\nexport { RadioGroup, RadioGroupItem } from \"./radio-group\";\n\n// Scroll Area\nexport { FadeAway, ScrollArea, ScrollBar } from \"./scroll-area\";\n\n// Select\nexport {\n Select,\n SelectContent,\n SelectContentPopper,\n SelectGroup,\n SelectItem,\n SelectLabel,\n SelectScrollDownButton,\n SelectScrollUpButton,\n SelectSeparator,\n SelectTrigger,\n SelectValue,\n} from \"./select\";\n\n// Separator\nexport { Separator } from \"./separator\";\n\n// Skeleton\nexport { Skeleton, SkeletonFragment } from \"./skeleton\";\n\n// Slider\nexport { Slider, type SliderProps, sliderVariants } from \"./slider\";\n// Stacking Card\nexport {\n StackingCard,\n StackingCardCheck,\n StackingCardContent,\n StackingCardDescription,\n StackingCardGroup,\n StackingCardHeader,\n StackingCardList,\n StackingCardListItem,\n StackingCardTitle,\n} from \"./stacking-card\";\n// Tabs\nexport { Tabs, TabsContent, TabsList, TabsTrigger } from \"./tabs\";\n// Textarea\nexport { Textarea, type TextareaProps, textareaVariants } from \"./textarea\";\n// Toast\nexport {\n type GlobalToastOptions,\n Toaster,\n type ToasterProps,\n toast,\n} from \"./toast\";\n// Toggle Button\nexport { ToggleButton, type ToggleButtonProps } from \"./toggle-button\";\n\n// Typography\nexport {\n Alpha,\n Bravo,\n Charlie,\n Delta,\n Echo,\n Foxtrot,\n Heading,\n type HeadingProps,\n headingVariants,\n List,\n ListItem,\n type ListItemProps,\n ListItemTick,\n type ListProps,\n listVariants,\n Prose,\n Text,\n type TextProps,\n textVariants,\n} from \"./typography\";\n"],
5
- "mappings": "AASA;AAAA,EACE;AAAA,EACA;AAAA,EAEA;AAAA,EAGA;AAAA,OAGK;AAGP,SAAS,OAAwB,qBAAqB;AAGtD;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAGP;AAAA,EACE;AAAA,EAIA;AAAA,EACA;AAAA,EACA;AAAA,OAGK;AAGP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAGP;AAAA,EACE;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,oBAAoB;AAG7B,SAAS,UAAU,qBAAqB;AAGxC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAGP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAGP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAGP;AAAA,EACE;AAAA,EACA;AAAA,EAEA;AAAA,OACK;AAGP,SAAS,aAAa;AAGtB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAGP,SAAS,gBAAoC;AAG7C,SAAS,YAAY,sBAAsB;AAG3C,SAAS,UAAU,YAAY,iBAAiB;AAGhD;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAGP,SAAS,iBAAiB;AAG1B,SAAS,UAAU,wBAAwB;AAG3C,SAAS,QAA0B,sBAAsB;AAEzD;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAEP,SAAS,MAAM,aAAa,UAAU,mBAAmB;AAEzD,SAAS,UAA8B,wBAAwB;AAE/D;AAAA,EAEE;AAAA,EAEA;AAAA,OACK;AAEP,SAAS,oBAA4C;AAGrD;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EAEA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EAEA;AAAA,OACK;",
4
+ "sourcesContent": ["/**\n * Cadence UI Components\n *\n * Import components from this module:\n * @example\n * import { Button, Input } from '@music-vine/cadence/ui';\n */\n\n// Accordion\nexport {\n Accordion,\n AccordionContent,\n type AccordionContentProps,\n AccordionItem,\n type AccordionItemProps,\n type AccordionProps,\n AccordionTrigger,\n type AccordionTriggerProps,\n type AccordionVariant,\n} from \"./accordion\";\n\n// Badge\nexport { Badge, type BadgeProps, badgeVariants } from \"./badge\";\n\n// Breadcrumb\nexport {\n Breadcrumb,\n BreadcrumbEllipsis,\n BreadcrumbItem,\n BreadcrumbLink,\n BreadcrumbList,\n BreadcrumbPage,\n BreadcrumbSeparator,\n} from \"./breadcrumb\";\n\n// Button\nexport {\n Button,\n type ButtonFontSize,\n type ButtonProps,\n type ButtonSize,\n buttonVariants,\n Loading,\n loadingVariants,\n type ResponsiveButtonFontSize,\n type ResponsiveButtonSize,\n} from \"./button\";\n\n// Card\nexport {\n Card,\n CardContent,\n CardDescription,\n CardFooter,\n CardHeader,\n CardTitle,\n} from \"./card\";\n\n// Carousel\nexport {\n Carousel,\n type CarouselApi,\n CarouselContent,\n CarouselItem,\n CarouselNext,\n CarouselPrevious,\n useCarousel,\n} from \"./carousel\";\nexport { CarouselDots } from \"./carousel-dots\";\n\n// Checkbox\nexport { Checkbox, DummyCheckbox } from \"./checkbox\";\n\n// Context Menu\nexport {\n ContextMenu,\n ContextMenuCheckboxItem,\n ContextMenuContent,\n ContextMenuGroup,\n ContextMenuItem,\n ContextMenuLabel,\n ContextMenuPortal,\n ContextMenuRadioGroup,\n ContextMenuRadioItem,\n ContextMenuSeparator,\n ContextMenuShortcut,\n ContextMenuSub,\n ContextMenuSubContent,\n ContextMenuSubTrigger,\n ContextMenuTrigger,\n} from \"./context-menu\";\n\n// Dialog\nexport {\n Dialog,\n DialogClose,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogOverlay,\n DialogPortal,\n DialogTitle,\n DialogTrigger,\n} from \"./dialog\";\n\n// Drawer\nexport {\n Drawer,\n DrawerClose,\n DrawerContent,\n DrawerDescription,\n DrawerFooter,\n DrawerHeader,\n DrawerOverlay,\n DrawerPortal,\n DrawerTitle,\n DrawerTrigger,\n} from \"./drawer\";\n\n// Input\nexport {\n ClearInputButton,\n Input,\n type InputProps,\n inputVariants,\n} from \"./input\";\n\n// Label\nexport { Label } from \"./label\";\n\n// Popover\nexport {\n Popover,\n PopoverAnchor,\n PopoverContent,\n PopoverTrigger,\n} from \"./popover\";\n\n// Price Tag\nexport { PriceTag, type PriceTagProps } from \"./price-tag\";\n\n// Radio Group\nexport { RadioGroup, RadioGroupItem } from \"./radio-group\";\n\n// Scroll Area\nexport { FadeAway, ScrollArea, ScrollBar } from \"./scroll-area\";\n\n// Scroll Drum\nexport {\n ScrollDrum,\n ScrollDrumColumn,\n type ScrollDrumColumnProps,\n ScrollDrumGroup,\n type ScrollDrumGroupProps,\n type ScrollDrumProps,\n} from \"./scroll-drum\";\n\n// Select\nexport {\n Select,\n SelectContent,\n SelectContentPopper,\n SelectGroup,\n SelectItem,\n SelectLabel,\n SelectScrollDownButton,\n SelectScrollUpButton,\n SelectSeparator,\n SelectTrigger,\n SelectValue,\n} from \"./select\";\n\n// Separator\nexport { Separator } from \"./separator\";\n\n// Skeleton\nexport { Skeleton, SkeletonFragment } from \"./skeleton\";\n\n// Slider\nexport { Slider, type SliderProps, sliderVariants } from \"./slider\";\n// Stacking Card\nexport {\n StackingCard,\n StackingCardCheck,\n StackingCardContent,\n StackingCardDescription,\n StackingCardGroup,\n StackingCardHeader,\n StackingCardList,\n StackingCardListItem,\n StackingCardTitle,\n} from \"./stacking-card\";\n// Tabs\nexport { Tabs, TabsContent, TabsList, TabsTrigger } from \"./tabs\";\n// Textarea\nexport { Textarea, type TextareaProps, textareaVariants } from \"./textarea\";\n// Toast\nexport {\n type GlobalToastOptions,\n Toaster,\n type ToasterProps,\n toast,\n} from \"./toast\";\n// Toggle Button\nexport { ToggleButton, type ToggleButtonProps } from \"./toggle-button\";\n\n// Typography\nexport {\n Alpha,\n Bravo,\n Charlie,\n Delta,\n Echo,\n Foxtrot,\n Heading,\n type HeadingProps,\n headingVariants,\n List,\n ListItem,\n type ListItemProps,\n ListItemTick,\n type ListProps,\n listVariants,\n Prose,\n Text,\n type TextProps,\n textVariants,\n} from \"./typography\";\n"],
5
+ "mappings": "AASA;AAAA,EACE;AAAA,EACA;AAAA,EAEA;AAAA,EAGA;AAAA,OAGK;AAGP,SAAS,OAAwB,qBAAqB;AAGtD;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAGP;AAAA,EACE;AAAA,EAIA;AAAA,EACA;AAAA,EACA;AAAA,OAGK;AAGP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAGP;AAAA,EACE;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,oBAAoB;AAG7B,SAAS,UAAU,qBAAqB;AAGxC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAGP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAGP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAGP;AAAA,EACE;AAAA,EACA;AAAA,EAEA;AAAA,OACK;AAGP,SAAS,aAAa;AAGtB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAGP,SAAS,gBAAoC;AAG7C,SAAS,YAAY,sBAAsB;AAG3C,SAAS,UAAU,YAAY,iBAAiB;AAGhD;AAAA,EACE;AAAA,EACA;AAAA,EAEA;AAAA,OAGK;AAGP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAGP,SAAS,iBAAiB;AAG1B,SAAS,UAAU,wBAAwB;AAG3C,SAAS,QAA0B,sBAAsB;AAEzD;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAEP,SAAS,MAAM,aAAa,UAAU,mBAAmB;AAEzD,SAAS,UAA8B,wBAAwB;AAE/D;AAAA,EAEE;AAAA,EAEA;AAAA,OACK;AAEP,SAAS,oBAA4C;AAGrD;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EAEA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EAEA;AAAA,OACK;",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,355 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import {
3
+ Children,
4
+ Fragment,
5
+ useCallback,
6
+ useEffect,
7
+ useId,
8
+ useRef,
9
+ useState
10
+ } from "react";
11
+ import { cn } from "../lib/utils";
12
+ const DEFAULT_ITEM_HEIGHT = 52;
13
+ const DEFAULT_VISIBLE = 3;
14
+ const DEFAULT_MAX_CHARS = 4;
15
+ const SNAP_DURATION_MS = 280;
16
+ const WHEEL_SETTLE_MS = 110;
17
+ const COAST_FACTOR = 220 * 0.55;
18
+ const clamp = (n, lo, hi) => Math.max(lo, Math.min(hi, n));
19
+ const pmod = (n, m) => (n % m + m) % m;
20
+ const ScrollDrum = ({
21
+ items,
22
+ value,
23
+ onChange,
24
+ itemHeight = DEFAULT_ITEM_HEIGHT,
25
+ visibleCount = DEFAULT_VISIBLE,
26
+ loop: loopProp = true,
27
+ maxChars = DEFAULT_MAX_CHARS,
28
+ ariaLabel,
29
+ className
30
+ }) => {
31
+ const uid = useId();
32
+ const optionId = (idx) => `${uid}-opt-${idx}`;
33
+ const containerRef = useRef(null);
34
+ const animRef = useRef(0);
35
+ const wheelTimer = useRef(null);
36
+ const dragRef = useRef({
37
+ active: false,
38
+ startY: 0,
39
+ startIndex: 0,
40
+ lastY: 0,
41
+ lastT: 0,
42
+ vy: 0,
43
+ moved: false
44
+ });
45
+ const N = items.length;
46
+ const loop = loopProp && N > 1;
47
+ const truncate = useCallback(
48
+ (label) => label.length > maxChars ? label.slice(0, maxChars) : label,
49
+ [maxChars]
50
+ );
51
+ const initialIndex = Math.max(0, items.indexOf(value));
52
+ const [floatIndex, setFloatIndex] = useState(initialIndex);
53
+ const floatIndexRef = useRef(floatIndex);
54
+ useEffect(() => {
55
+ floatIndexRef.current = floatIndex;
56
+ }, [floatIndex]);
57
+ const resolveIndex = useCallback(
58
+ (f) => {
59
+ if (loop) return pmod(Math.round(f), N);
60
+ return clamp(Math.round(f), 0, N - 1);
61
+ },
62
+ [loop, N]
63
+ );
64
+ const animateTo = useCallback(
65
+ (targetFloat) => {
66
+ cancelAnimationFrame(animRef.current);
67
+ const start = performance.now();
68
+ const from = floatIndexRef.current;
69
+ const ease = (t) => 1 - (1 - t) ** 3;
70
+ const step = () => {
71
+ const t = clamp((performance.now() - start) / SNAP_DURATION_MS, 0, 1);
72
+ const v = from + (targetFloat - from) * ease(t);
73
+ setFloatIndex(v);
74
+ if (t < 1) {
75
+ animRef.current = requestAnimationFrame(step);
76
+ } else {
77
+ const final = loop ? pmod(targetFloat, N) : clamp(targetFloat, 0, N - 1);
78
+ setFloatIndex(final);
79
+ const idx = resolveIndex(final);
80
+ const next = items[idx];
81
+ if (next !== void 0 && next !== value) onChange?.(next, idx);
82
+ }
83
+ };
84
+ animRef.current = requestAnimationFrame(step);
85
+ },
86
+ [items, loop, N, onChange, resolveIndex, value]
87
+ );
88
+ const targetIdx = N > 0 ? Math.max(0, items.indexOf(value)) : 0;
89
+ const lastSyncedTargetIdx = useRef(targetIdx);
90
+ useEffect(() => {
91
+ if (targetIdx === lastSyncedTargetIdx.current) return;
92
+ lastSyncedTargetIdx.current = targetIdx;
93
+ if (N === 0) return;
94
+ const cur = floatIndexRef.current;
95
+ let target = targetIdx;
96
+ if (loop) {
97
+ const cycles = Math.round((cur - targetIdx) / N);
98
+ target = targetIdx + cycles * N;
99
+ }
100
+ if (Math.abs(target - cur) > 1e-3) animateTo(target);
101
+ }, [targetIdx]);
102
+ useEffect(() => {
103
+ const el = containerRef.current;
104
+ if (!el) return;
105
+ const onWheel = (e) => {
106
+ e.preventDefault();
107
+ cancelAnimationFrame(animRef.current);
108
+ const delta = e.deltaY / itemHeight;
109
+ const raw = floatIndexRef.current + delta;
110
+ setFloatIndex(loop ? raw : clamp(raw, 0, N - 1));
111
+ if (wheelTimer.current) clearTimeout(wheelTimer.current);
112
+ wheelTimer.current = setTimeout(() => {
113
+ const cur = floatIndexRef.current;
114
+ const target = loop ? Math.round(cur) : clamp(Math.round(cur), 0, N - 1);
115
+ animateTo(target);
116
+ }, WHEEL_SETTLE_MS);
117
+ };
118
+ el.addEventListener("wheel", onWheel, { passive: false });
119
+ return () => {
120
+ el.removeEventListener("wheel", onWheel);
121
+ if (wheelTimer.current) clearTimeout(wheelTimer.current);
122
+ };
123
+ }, [animateTo, N, itemHeight, loop]);
124
+ useEffect(
125
+ () => () => {
126
+ cancelAnimationFrame(animRef.current);
127
+ },
128
+ []
129
+ );
130
+ const onPointerDown = (e) => {
131
+ if (e.pointerType === "mouse" && e.button !== 0) return;
132
+ cancelAnimationFrame(animRef.current);
133
+ containerRef.current?.setPointerCapture(e.pointerId);
134
+ dragRef.current = {
135
+ active: true,
136
+ startY: e.clientY,
137
+ startIndex: floatIndexRef.current,
138
+ lastY: e.clientY,
139
+ lastT: performance.now(),
140
+ vy: 0,
141
+ moved: false
142
+ };
143
+ };
144
+ const onPointerMove = (e) => {
145
+ const d = dragRef.current;
146
+ if (!d.active) return;
147
+ const dy = e.clientY - d.startY;
148
+ if (Math.abs(dy) > 3) d.moved = true;
149
+ const raw = d.startIndex - dy / itemHeight;
150
+ setFloatIndex(loop ? raw : clamp(raw, 0, N - 1));
151
+ const now = performance.now();
152
+ const dt = Math.max(1, now - d.lastT);
153
+ d.vy = (e.clientY - d.lastY) / dt;
154
+ d.lastY = e.clientY;
155
+ d.lastT = now;
156
+ };
157
+ const onPointerUp = (e) => {
158
+ const d = dragRef.current;
159
+ if (!d.active) return;
160
+ d.active = false;
161
+ try {
162
+ containerRef.current?.releasePointerCapture(e.pointerId);
163
+ } catch {
164
+ }
165
+ const coastPx = d.vy * COAST_FACTOR;
166
+ const projected = floatIndexRef.current - coastPx / itemHeight;
167
+ const target = loop ? Math.round(projected) : clamp(Math.round(projected), 0, N - 1);
168
+ animateTo(target);
169
+ };
170
+ const onKeyDown = (e) => {
171
+ let target = Math.round(floatIndexRef.current);
172
+ if (e.key === "ArrowUp") target -= 1;
173
+ else if (e.key === "ArrowDown") target += 1;
174
+ else if (e.key === "PageUp") target -= 5;
175
+ else if (e.key === "PageDown") target += 5;
176
+ else if (e.key === "Home") {
177
+ const cur = Math.round(floatIndexRef.current);
178
+ target = loop ? cur - pmod(cur, N) : 0;
179
+ } else if (e.key === "End") {
180
+ const cur = Math.round(floatIndexRef.current);
181
+ target = loop ? cur - pmod(cur, N) + N - 1 : N - 1;
182
+ } else return;
183
+ e.preventDefault();
184
+ if (!loop) target = clamp(target, 0, N - 1);
185
+ animateTo(target);
186
+ };
187
+ const halfVisible = Math.floor(visibleCount / 2);
188
+ const windowRadius = halfVisible + 2;
189
+ const centerInt = Math.round(floatIndex);
190
+ const visibleItems = [];
191
+ for (let off = -windowRadius; off <= windowRadius; off++) {
192
+ const rawIdx = centerInt + off;
193
+ if (!loop && (rawIdx < 0 || rawIdx >= N)) continue;
194
+ const itemIdx = pmod(rawIdx, N);
195
+ const label = items[itemIdx];
196
+ if (label === void 0) continue;
197
+ visibleItems.push({
198
+ key: String(rawIdx),
199
+ itemIdx,
200
+ offsetSlot: rawIdx,
201
+ label
202
+ });
203
+ }
204
+ const rowOffset = (slot) => (slot - floatIndex) * itemHeight;
205
+ const selectedIndex = N > 0 ? resolveIndex(floatIndex) : 0;
206
+ if (N === 0) {
207
+ return /* @__PURE__ */ jsx(
208
+ "div",
209
+ {
210
+ "aria-label": ariaLabel,
211
+ className: cn(
212
+ "relative h-full w-full overflow-hidden bg-white dark:bg-gray-900",
213
+ className
214
+ ),
215
+ role: "listbox"
216
+ }
217
+ );
218
+ }
219
+ return /* @__PURE__ */ jsxs(
220
+ "div",
221
+ {
222
+ "aria-activedescendant": optionId(selectedIndex),
223
+ "aria-label": ariaLabel,
224
+ "aria-orientation": "vertical",
225
+ className: cn(
226
+ "relative h-full w-full cursor-grab touch-none select-none overflow-hidden bg-white outline-none active:cursor-grabbing focus-visible:[box-shadow:inset_0_0_0_2px_var(--brand-primary)] dark:bg-gray-900",
227
+ className
228
+ ),
229
+ onKeyDown,
230
+ onPointerCancel: onPointerUp,
231
+ onPointerDown,
232
+ onPointerMove,
233
+ onPointerUp,
234
+ ref: containerRef,
235
+ role: "listbox",
236
+ style: { "--sd-item-h": `${itemHeight}px` },
237
+ tabIndex: 0,
238
+ children: [
239
+ /* @__PURE__ */ jsx(
240
+ "div",
241
+ {
242
+ "aria-hidden": "true",
243
+ className: "pointer-events-none absolute inset-x-0 top-[-1px] z-[2] h-9 bg-gradient-to-b from-white from-[12%] to-transparent dark:from-gray-900"
244
+ }
245
+ ),
246
+ /* @__PURE__ */ jsx(
247
+ "div",
248
+ {
249
+ "aria-hidden": "true",
250
+ className: "pointer-events-none absolute inset-x-0 bottom-[-1px] z-[2] h-9 bg-gradient-to-t from-white from-[12%] to-transparent dark:from-gray-900"
251
+ }
252
+ ),
253
+ /* @__PURE__ */ jsx(
254
+ "div",
255
+ {
256
+ "aria-hidden": "true",
257
+ className: "pointer-events-none absolute inset-x-2 top-1/2 z-0 h-[41px] -translate-y-1/2 rounded-sm border border-brand-primary bg-brand-secondary"
258
+ }
259
+ ),
260
+ /* @__PURE__ */ jsx("div", { className: "pointer-events-none absolute inset-0", children: visibleItems.map(({ key, itemIdx, offsetSlot, label }) => {
261
+ const isSelected = itemIdx === selectedIndex && Math.abs(offsetSlot - floatIndex) <= 0.5;
262
+ return /* @__PURE__ */ jsx(
263
+ "button",
264
+ {
265
+ "aria-selected": isSelected,
266
+ className: cn(
267
+ "pointer-events-auto absolute top-1/2 left-1/2 z-[1] m-0 box-border flex h-[var(--sd-item-h,52px)] w-full cursor-pointer items-center justify-center overflow-hidden border-0 bg-transparent px-1.5 py-0 transition-colors duration-150",
268
+ isSelected ? "text-brand-primary-hover dark:text-white" : "text-gray-600 dark:text-gray-300"
269
+ ),
270
+ id: isSelected ? optionId(itemIdx) : void 0,
271
+ onClick: () => {
272
+ if (dragRef.current.moved) return;
273
+ animateTo(offsetSlot);
274
+ },
275
+ role: "option",
276
+ style: {
277
+ transform: `translate3d(-50%, calc(-50% + ${rowOffset(offsetSlot)}px), 0)`
278
+ },
279
+ tabIndex: -1,
280
+ type: "button",
281
+ children: /* @__PURE__ */ jsx("span", { className: "inline-block whitespace-nowrap text-center font-sans font-semibold text-[30px] leading-9 tracking-[-1px] [font-feature-settings:'ss03']", children: truncate(label) })
282
+ },
283
+ key
284
+ );
285
+ }) })
286
+ ]
287
+ }
288
+ );
289
+ };
290
+ const ScrollDrumGroup = ({
291
+ children,
292
+ separator = ":",
293
+ showSeparators = true,
294
+ className
295
+ }) => {
296
+ const cols = Children.toArray(children);
297
+ return /* @__PURE__ */ jsx(
298
+ "div",
299
+ {
300
+ className: cn(
301
+ "inline-flex flex-row items-center justify-center rounded-lg bg-gray-100 px-8 pt-6 pb-4 dark:bg-gray-800",
302
+ className
303
+ ),
304
+ children: cols.map((col, idx) => (
305
+ // biome-ignore lint/suspicious/noArrayIndexKey: columns are static and not reordered
306
+ /* @__PURE__ */ jsxs(Fragment, { children: [
307
+ col,
308
+ idx < cols.length - 1 && /* @__PURE__ */ jsx(
309
+ "div",
310
+ {
311
+ "aria-hidden": "true",
312
+ className: "flex h-[120px] w-8 flex-none items-center justify-center self-start",
313
+ children: showSeparators && /* @__PURE__ */ jsx("span", { className: "text-center font-sans font-semibold text-[36px] text-gray-600 leading-10 tracking-[-1.5px] [font-feature-settings:'ss03'] dark:text-gray-400", children: separator })
314
+ }
315
+ )
316
+ ] }, idx)
317
+ ))
318
+ }
319
+ );
320
+ };
321
+ const ScrollDrumColumn = ({
322
+ label,
323
+ width,
324
+ maxChars = DEFAULT_MAX_CHARS,
325
+ children,
326
+ className
327
+ }) => {
328
+ const computed = width ?? Math.max(72, 24 + maxChars * 16);
329
+ return /* @__PURE__ */ jsxs(
330
+ "div",
331
+ {
332
+ className: cn(
333
+ "flex flex-none flex-col gap-2 p-0",
334
+ className
335
+ ),
336
+ style: { width: computed },
337
+ children: [
338
+ /* @__PURE__ */ jsx("div", { className: "flex h-[120px] flex-none items-center justify-center bg-white dark:bg-gray-900", children }),
339
+ label !== void 0 && /* @__PURE__ */ jsx(
340
+ "div",
341
+ {
342
+ className: "h-6 text-center font-sans font-semibold text-base text-gray-600 leading-6 [font-feature-settings:'ss03'] dark:text-gray-300",
343
+ children: label || "\xA0"
344
+ }
345
+ )
346
+ ]
347
+ }
348
+ );
349
+ };
350
+ export {
351
+ ScrollDrum,
352
+ ScrollDrumColumn,
353
+ ScrollDrumGroup
354
+ };
355
+ //# sourceMappingURL=scroll-drum.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/components/scroll-drum.tsx"],
4
+ "sourcesContent": ["/**\n * @module ScrollDrum\n *\n * iOS-style picker columns for selecting a value from a constrained list \u2014\n * durations, time, dates, BPM, etc. Supports wheel/trackpad scrolling, mouse\n * and touch drag with momentum, click-to-center, full keyboard control, and\n * optional infinite looping.\n *\n * @example\n * // Single drum: count-in bars\n * const [val, setVal] = useState(\"8\");\n * const items = Array.from({ length: 31 }, (_, i) => String(i));\n *\n * <ScrollDrumGroup>\n * <ScrollDrumColumn label=\"bars\">\n * <ScrollDrum items={items} value={val} onChange={setVal} ariaLabel=\"Bars\" />\n * </ScrollDrumColumn>\n * </ScrollDrumGroup>\n *\n * @example\n * // 12-hour time picker with separator\n * <ScrollDrumGroup separator=\":\">\n * <ScrollDrumColumn label=\"hour\">\n * <ScrollDrum items={hours} value={h} onChange={setH} ariaLabel=\"Hour\" />\n * </ScrollDrumColumn>\n * <ScrollDrumColumn label=\"min\">\n * <ScrollDrum items={minutes} value={m} onChange={setM} ariaLabel=\"Minute\" />\n * </ScrollDrumColumn>\n * <ScrollDrumColumn label=\"\">\n * <ScrollDrum items={[\"AM\", \"PM\"]} value={p} onChange={setP} loop={false} />\n * </ScrollDrumColumn>\n * </ScrollDrumGroup>\n */\nimport {\n Children,\n Fragment,\n type PointerEvent as ReactPointerEvent,\n type KeyboardEvent as ReactKeyboardEvent,\n type ReactNode,\n useCallback,\n useEffect,\n useId,\n useRef,\n useState,\n} from \"react\";\n\nimport { cn } from \"../lib/utils\";\n\nconst DEFAULT_ITEM_HEIGHT = 52;\nconst DEFAULT_VISIBLE = 3;\nconst DEFAULT_MAX_CHARS = 4;\n\nconst SNAP_DURATION_MS = 280;\nconst WHEEL_SETTLE_MS = 110;\nconst COAST_FACTOR = 220 * 0.55;\n\nconst clamp = (n: number, lo: number, hi: number): number =>\n Math.max(lo, Math.min(hi, n));\n\n// Positive modulo (handles negatives) \u2014 required for stable wrap math.\nconst pmod = (n: number, m: number): number => ((n % m) + m) % m;\n\ninterface ScrollDrumProps {\n /** Ordered list of selectable strings. Order is the visual order. */\n items: string[];\n /** Currently selected item. Must exist in `items`. */\n value: string;\n /** Fires with `(newValue, newIndex)` when the user lands on a new row. */\n onChange?: (value: string, index: number) => void;\n /** Vertical pixel size of one row slot. Defaults to 52. */\n itemHeight?: number;\n /** How many rows are visible at once (odd numbers center cleanly). */\n visibleCount?: number;\n /** When `true` (default), scrolling past the end wraps to the start. */\n loop?: boolean;\n /** Hard character cap per item. Long labels are truncated for layout. */\n maxChars?: number;\n /** Accessible name for the listbox. */\n ariaLabel?: string;\n /** Additional class names merged onto the drum element. */\n className?: string;\n}\n\n/**\n * A single scrollable column. Designed to be wrapped in a {@link ScrollDrumColumn}\n * and grouped via {@link ScrollDrumGroup}.\n */\nconst ScrollDrum = ({\n items,\n value,\n onChange,\n itemHeight = DEFAULT_ITEM_HEIGHT,\n visibleCount = DEFAULT_VISIBLE,\n loop: loopProp = true,\n maxChars = DEFAULT_MAX_CHARS,\n ariaLabel,\n className,\n}: ScrollDrumProps): ReactNode => {\n const uid = useId();\n const optionId = (idx: number): string => `${uid}-opt-${idx}`;\n const containerRef = useRef<HTMLDivElement | null>(null);\n const animRef = useRef<number>(0);\n const wheelTimer = useRef<ReturnType<typeof setTimeout> | null>(null);\n const dragRef = useRef({\n active: false,\n startY: 0,\n startIndex: 0,\n lastY: 0,\n lastT: 0,\n vy: 0,\n moved: false,\n });\n\n const N = items.length;\n // Looping past zero items would call pmod(n, 0) and yield NaN, breaking the\n // render math. Force non-looping internally when there's nothing to loop\n // through; the render bails on N === 0.\n const loop = loopProp && N > 1;\n\n const truncate = useCallback(\n (label: string): string =>\n label.length > maxChars ? label.slice(0, maxChars) : label,\n [maxChars]\n );\n\n const initialIndex = Math.max(0, items.indexOf(value));\n const [floatIndex, setFloatIndex] = useState<number>(initialIndex);\n const floatIndexRef = useRef<number>(floatIndex);\n useEffect(() => {\n floatIndexRef.current = floatIndex;\n }, [floatIndex]);\n\n const resolveIndex = useCallback(\n (f: number): number => {\n if (loop) return pmod(Math.round(f), N);\n return clamp(Math.round(f), 0, N - 1);\n },\n [loop, N]\n );\n\n const animateTo = useCallback(\n (targetFloat: number): void => {\n cancelAnimationFrame(animRef.current);\n const start = performance.now();\n const from = floatIndexRef.current;\n const ease = (t: number): number => 1 - (1 - t) ** 3;\n const step = (): void => {\n const t = clamp((performance.now() - start) / SNAP_DURATION_MS, 0, 1);\n const v = from + (targetFloat - from) * ease(t);\n setFloatIndex(v);\n if (t < 1) {\n animRef.current = requestAnimationFrame(step);\n } else {\n const final = loop\n ? pmod(targetFloat, N)\n : clamp(targetFloat, 0, N - 1);\n setFloatIndex(final);\n const idx = resolveIndex(final);\n const next = items[idx];\n if (next !== undefined && next !== value) onChange?.(next, idx);\n }\n };\n animRef.current = requestAnimationFrame(step);\n },\n [items, loop, N, onChange, resolveIndex, value]\n );\n\n // External value -> internal float, animated on the shortest path. Tracked\n // by value's resolved index rather than the items reference, so an unstable\n // `items` array (fresh ref every render) doesn't cancel an in-flight\n // animation, but a real reorder (where `value` lands at a different index)\n // still resyncs the drum.\n const targetIdx = N > 0 ? Math.max(0, items.indexOf(value)) : 0;\n const lastSyncedTargetIdx = useRef<number>(targetIdx);\n useEffect(() => {\n if (targetIdx === lastSyncedTargetIdx.current) return;\n lastSyncedTargetIdx.current = targetIdx;\n if (N === 0) return;\n const cur = floatIndexRef.current;\n let target = targetIdx;\n if (loop) {\n const cycles = Math.round((cur - targetIdx) / N);\n target = targetIdx + cycles * N;\n }\n if (Math.abs(target - cur) > 0.001) animateTo(target);\n // animateTo intentionally omitted to avoid retriggering on every render.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [targetIdx]);\n\n // Wheel: native non-passive listener so we can preventDefault.\n useEffect(() => {\n const el = containerRef.current;\n if (!el) return;\n const onWheel = (e: WheelEvent): void => {\n e.preventDefault();\n cancelAnimationFrame(animRef.current);\n const delta = e.deltaY / itemHeight;\n const raw = floatIndexRef.current + delta;\n setFloatIndex(loop ? raw : clamp(raw, 0, N - 1));\n if (wheelTimer.current) clearTimeout(wheelTimer.current);\n wheelTimer.current = setTimeout(() => {\n const cur = floatIndexRef.current;\n const target = loop\n ? Math.round(cur)\n : clamp(Math.round(cur), 0, N - 1);\n animateTo(target);\n }, WHEEL_SETTLE_MS);\n };\n el.addEventListener(\"wheel\", onWheel, { passive: false });\n return () => {\n el.removeEventListener(\"wheel\", onWheel);\n if (wheelTimer.current) clearTimeout(wheelTimer.current);\n };\n }, [animateTo, N, itemHeight, loop]);\n\n useEffect(\n () => () => {\n cancelAnimationFrame(animRef.current);\n },\n []\n );\n\n const onPointerDown = (e: ReactPointerEvent<HTMLDivElement>): void => {\n if (e.pointerType === \"mouse\" && e.button !== 0) return;\n cancelAnimationFrame(animRef.current);\n containerRef.current?.setPointerCapture(e.pointerId);\n dragRef.current = {\n active: true,\n startY: e.clientY,\n startIndex: floatIndexRef.current,\n lastY: e.clientY,\n lastT: performance.now(),\n vy: 0,\n moved: false,\n };\n };\n\n const onPointerMove = (e: ReactPointerEvent<HTMLDivElement>): void => {\n const d = dragRef.current;\n if (!d.active) return;\n const dy = e.clientY - d.startY;\n if (Math.abs(dy) > 3) d.moved = true;\n const raw = d.startIndex - dy / itemHeight;\n setFloatIndex(loop ? raw : clamp(raw, 0, N - 1));\n const now = performance.now();\n const dt = Math.max(1, now - d.lastT);\n d.vy = (e.clientY - d.lastY) / dt;\n d.lastY = e.clientY;\n d.lastT = now;\n };\n\n const onPointerUp = (e: ReactPointerEvent<HTMLDivElement>): void => {\n const d = dragRef.current;\n if (!d.active) return;\n d.active = false;\n try {\n containerRef.current?.releasePointerCapture(e.pointerId);\n } catch {\n /* pointer may already be released */\n }\n const coastPx = d.vy * COAST_FACTOR;\n const projected = floatIndexRef.current - coastPx / itemHeight;\n const target = loop\n ? Math.round(projected)\n : clamp(Math.round(projected), 0, N - 1);\n animateTo(target);\n };\n\n const onKeyDown = (e: ReactKeyboardEvent<HTMLDivElement>): void => {\n let target = Math.round(floatIndexRef.current);\n if (e.key === \"ArrowUp\") target -= 1;\n else if (e.key === \"ArrowDown\") target += 1;\n else if (e.key === \"PageUp\") target -= 5;\n else if (e.key === \"PageDown\") target += 5;\n else if (e.key === \"Home\") {\n const cur = Math.round(floatIndexRef.current);\n target = loop ? cur - pmod(cur, N) : 0;\n } else if (e.key === \"End\") {\n const cur = Math.round(floatIndexRef.current);\n target = loop ? cur - pmod(cur, N) + N - 1 : N - 1;\n } else return;\n e.preventDefault();\n if (!loop) target = clamp(target, 0, N - 1);\n animateTo(target);\n };\n\n const halfVisible = Math.floor(visibleCount / 2);\n const windowRadius = halfVisible + 2;\n const centerInt = Math.round(floatIndex);\n\n const visibleItems: Array<{\n key: string;\n itemIdx: number;\n offsetSlot: number;\n label: string;\n }> = [];\n for (let off = -windowRadius; off <= windowRadius; off++) {\n const rawIdx = centerInt + off;\n if (!loop && (rawIdx < 0 || rawIdx >= N)) continue;\n const itemIdx = pmod(rawIdx, N);\n const label = items[itemIdx];\n if (label === undefined) continue;\n visibleItems.push({\n key: String(rawIdx),\n itemIdx,\n offsetSlot: rawIdx,\n label,\n });\n }\n\n const rowOffset = (slot: number): number => (slot - floatIndex) * itemHeight;\n const selectedIndex = N > 0 ? resolveIndex(floatIndex) : 0;\n\n if (N === 0) {\n return (\n <div\n aria-label={ariaLabel}\n className={cn(\n \"relative h-full w-full overflow-hidden bg-white dark:bg-gray-900\",\n className\n )}\n role=\"listbox\"\n />\n );\n }\n\n return (\n <div\n aria-activedescendant={optionId(selectedIndex)}\n aria-label={ariaLabel}\n aria-orientation=\"vertical\"\n className={cn(\n \"relative h-full w-full cursor-grab touch-none select-none overflow-hidden bg-white outline-none active:cursor-grabbing focus-visible:[box-shadow:inset_0_0_0_2px_var(--brand-primary)] dark:bg-gray-900\",\n className\n )}\n onKeyDown={onKeyDown}\n onPointerCancel={onPointerUp}\n onPointerDown={onPointerDown}\n onPointerMove={onPointerMove}\n onPointerUp={onPointerUp}\n ref={containerRef}\n role=\"listbox\"\n style={{ \"--sd-item-h\": `${itemHeight}px` } as React.CSSProperties}\n tabIndex={0}\n >\n <div\n aria-hidden=\"true\"\n className=\"pointer-events-none absolute inset-x-0 top-[-1px] z-[2] h-9 bg-gradient-to-b from-white from-[12%] to-transparent dark:from-gray-900\"\n />\n <div\n aria-hidden=\"true\"\n className=\"pointer-events-none absolute inset-x-0 bottom-[-1px] z-[2] h-9 bg-gradient-to-t from-white from-[12%] to-transparent dark:from-gray-900\"\n />\n <div\n aria-hidden=\"true\"\n className=\"pointer-events-none absolute inset-x-2 top-1/2 z-0 h-[41px] -translate-y-1/2 rounded-sm border border-brand-primary bg-brand-secondary\"\n />\n\n <div className=\"pointer-events-none absolute inset-0\">\n {visibleItems.map(({ key, itemIdx, offsetSlot, label }) => {\n const isSelected =\n itemIdx === selectedIndex &&\n Math.abs(offsetSlot - floatIndex) <= 0.5;\n return (\n <button\n aria-selected={isSelected}\n className={cn(\n \"pointer-events-auto absolute top-1/2 left-1/2 z-[1] m-0 box-border flex h-[var(--sd-item-h,52px)] w-full cursor-pointer items-center justify-center overflow-hidden border-0 bg-transparent px-1.5 py-0 transition-colors duration-150\",\n isSelected\n ? \"text-brand-primary-hover dark:text-white\"\n : \"text-gray-600 dark:text-gray-300\"\n )}\n id={isSelected ? optionId(itemIdx) : undefined}\n key={key}\n onClick={() => {\n if (dragRef.current.moved) return;\n animateTo(offsetSlot);\n }}\n role=\"option\"\n style={{\n transform: `translate3d(-50%, calc(-50% + ${rowOffset(offsetSlot)}px), 0)`,\n }}\n tabIndex={-1}\n type=\"button\"\n >\n <span className=\"inline-block whitespace-nowrap text-center font-sans font-semibold text-[30px] leading-9 tracking-[-1px] [font-feature-settings:'ss03']\">\n {truncate(label)}\n </span>\n </button>\n );\n })}\n </div>\n </div>\n );\n};\n\ninterface ScrollDrumGroupProps {\n /** One or more {@link ScrollDrumColumn} children. */\n children: ReactNode;\n /** Glyph rendered between adjacent columns. Ignored when `showSeparators` is false. */\n separator?: string;\n /** When `false`, the gutter between columns is preserved but no glyph is rendered. */\n showSeparators?: boolean;\n /** Additional class names merged onto the group container. */\n className?: string;\n}\n\n/**\n * Gray container that holds one or more {@link ScrollDrumColumn} children.\n * Optionally renders a glyph between columns (e.g. `:` for time, `/` for date).\n */\nconst ScrollDrumGroup = ({\n children,\n separator = \":\",\n showSeparators = true,\n className,\n}: ScrollDrumGroupProps): ReactNode => {\n const cols = Children.toArray(children);\n return (\n <div\n className={cn(\n \"inline-flex flex-row items-center justify-center rounded-lg bg-gray-100 px-8 pt-6 pb-4 dark:bg-gray-800\",\n className\n )}\n >\n {cols.map((col, idx) => (\n // biome-ignore lint/suspicious/noArrayIndexKey: columns are static and not reordered\n <Fragment key={idx}>\n {col}\n {idx < cols.length - 1 && (\n <div\n aria-hidden=\"true\"\n className=\"flex h-[120px] w-8 flex-none items-center justify-center self-start\"\n >\n {showSeparators && (\n <span className=\"text-center font-sans font-semibold text-[36px] text-gray-600 leading-10 tracking-[-1.5px] [font-feature-settings:'ss03'] dark:text-gray-400\">\n {separator}\n </span>\n )}\n </div>\n )}\n </Fragment>\n ))}\n </div>\n );\n};\n\ninterface ScrollDrumColumnProps {\n /** Caption rendered below the drum slab. Pass `\"\"` for an invisible spacer. */\n label?: string;\n /** Explicit pixel width override. When omitted, scales with `maxChars`. */\n width?: number;\n /** Hard character cap that drives the auto-computed column width. */\n maxChars?: number;\n /** Should be a single {@link ScrollDrum}. */\n children: ReactNode;\n /** Additional class names merged onto the column wrapper. */\n className?: string;\n}\n\n/**\n * Pairs a {@link ScrollDrum} with an optional caption underneath. When `width`\n * is omitted the column auto-sizes via `Math.max(72, 24 + maxChars * 16)`, so\n * a 4-char drum (e.g. `\"2026\"`) lands at 88px and a 2-char drum stays at the\n * 72px floor.\n */\nconst ScrollDrumColumn = ({\n label,\n width,\n maxChars = DEFAULT_MAX_CHARS,\n children,\n className,\n}: ScrollDrumColumnProps): ReactNode => {\n const computed = width ?? Math.max(72, 24 + maxChars * 16);\n return (\n <div\n className={cn(\n \"flex flex-none flex-col gap-2 p-0\",\n className\n )}\n style={{ width: computed }}\n >\n <div className=\"flex h-[120px] flex-none items-center justify-center bg-white dark:bg-gray-900\">\n {children}\n </div>\n {label !== undefined && (\n <div\n className=\"h-6 text-center font-sans font-semibold text-base text-gray-600 leading-6 [font-feature-settings:'ss03'] dark:text-gray-300\"\n >\n {label || \"\u00A0\"}\n </div>\n )}\n </div>\n );\n};\n\nexport { ScrollDrum, ScrollDrumGroup, ScrollDrumColumn };\nexport type { ScrollDrumProps, ScrollDrumGroupProps, ScrollDrumColumnProps };\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -15,8 +15,8 @@ const SelectTrigger = ({
15
15
  SelectPrimitive.Trigger,
16
16
  {
17
17
  className: cn(
18
- "group flex h-8 w-full items-center justify-between gap-2 border border-gray-200 border-solid bg-white px-4 py-2 text-base focus-visible:ring-2 focus-visible:ring-[var(--focus-ring)] focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
19
- "dark:border-gray-800 dark:bg-gray-950 dark:text-white dark:focus-visible:ring-0 dark:focus-visible:ring-offset-gray-700",
18
+ "group flex h-10 w-full min-w-44 items-center justify-between gap-2 rounded-lg border border-gray-200 border-solid bg-white px-4 py-2 text-base transition-colors focus-visible:border-gray-400 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--focus-ring)] focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
19
+ "dark:border-gray-800 dark:bg-gray-950 dark:text-white dark:focus-visible:border-gray-600 dark:focus-visible:ring-[var(--focus-ring)] dark:focus-visible:ring-offset-gray-950",
20
20
  className
21
21
  ),
22
22
  ref,
@@ -69,7 +69,7 @@ const SelectContent = ({
69
69
  SelectPrimitive.Content,
70
70
  {
71
71
  className: cn(
72
- "data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-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 relative z-50 max-h-96 min-w-[8rem] rounded-md border border-gray-200 border-solid bg-white text-gray-950 shadow-[0px_2px_4px_0px_rgba(0,_0,_0,_0.15)] data-[state=closed]:animate-out data-[state=open]:animate-in dark:border-gray-800 dark:bg-gray-950 dark:text-white",
72
+ "data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-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 relative z-50 max-h-96 min-w-[8rem] rounded-lg border border-gray-200 border-solid bg-white text-gray-950 shadow-[0px_2px_4px_0px_rgba(0,_0,_0,_0.15)] data-[state=closed]:animate-out data-[state=open]:animate-in dark:border-gray-800 dark:bg-gray-950 dark:text-white",
73
73
  position === "popper" && "data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=bottom]:translate-y-1 data-[side=top]:-translate-y-1",
74
74
  className
75
75
  ),
@@ -102,7 +102,7 @@ const SelectContentPopper = ({
102
102
  SelectPrimitive.Content,
103
103
  {
104
104
  className: cn(
105
- "data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-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 relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border border-gray-200 border-solid bg-white text-gray-950 shadow-100 data-[state=closed]:animate-out data-[state=open]:animate-in dark:border-gray-800 dark:bg-gray-950 dark:text-white",
105
+ "data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-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 relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-lg border border-gray-200 border-solid bg-white text-gray-950 shadow-100 data-[state=closed]:animate-out data-[state=open]:animate-in dark:border-gray-800 dark:bg-gray-950 dark:text-white",
106
106
  position === "popper" && "data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=bottom]:translate-y-1 data-[side=top]:-translate-y-1",
107
107
  className
108
108
  ),
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/components/select.tsx"],
4
- "sourcesContent": ["/**\n * @module Select\n *\n * Dropdown select menu for single value selection. Built on Radix UI Select primitive.\n * Includes keyboard navigation, typeahead, and customizable styling.\n *\n * @see {@link https://ui.shadcn.com/docs/components/select Shadcn Select}\n * @see {@link https://www.radix-ui.com/primitives/docs/components/select Radix Select}\n *\n * @example\n * // Basic select\n * <Select>\n * <SelectTrigger className=\"w-48\">\n * <SelectValue placeholder=\"Select option\" />\n * </SelectTrigger>\n * <SelectContent>\n * <SelectItem value=\"a\">Option A</SelectItem>\n * <SelectItem value=\"b\">Option B</SelectItem>\n * </SelectContent>\n * </Select>\n *\n * @example\n * // Grouped options\n * <SelectContent>\n * <SelectGroup>\n * <SelectLabel>Fruits</SelectLabel>\n * <SelectItem value=\"apple\">Apple</SelectItem>\n * <SelectItem value=\"banana\">Banana</SelectItem>\n * </SelectGroup>\n * <SelectSeparator />\n * <SelectGroup>\n * <SelectLabel>Vegetables</SelectLabel>\n * <SelectItem value=\"carrot\">Carrot</SelectItem>\n * </SelectGroup>\n * </SelectContent>\n */\nimport { Check, ChevronDown, ChevronUp } from \"lucide-react\";\nimport { Select as SelectPrimitive } from \"radix-ui\";\nimport type * as React from \"react\";\nimport type { Ref } from \"react\";\n\nimport { cn } from \"../lib/utils\";\n\n/** Root component that manages select state. */\nconst Select = SelectPrimitive.Root;\n\n/** Container for grouping related select items with a label. */\nconst SelectGroup = SelectPrimitive.Group;\n\n/** Displays the selected value or placeholder text. */\nconst SelectValue = SelectPrimitive.Value;\n\n/**\n * Button that opens the select dropdown.\n * @param icon - Custom dropdown icon (defaults to ChevronDown)\n */\ninterface SelectTriggerProps\n extends Omit<\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>,\n \"ref\"\n > {\n icon?: React.ReactNode;\n ref?: Ref<React.ElementRef<typeof SelectPrimitive.Trigger>>;\n}\n\nconst SelectTrigger = ({\n className,\n children,\n icon,\n ref,\n ...props\n}: SelectTriggerProps) => (\n <SelectPrimitive.Trigger\n className={cn(\n \"group flex h-8 w-full items-center justify-between gap-2 border border-gray-200 border-solid bg-white px-4 py-2 text-base focus-visible:ring-2 focus-visible:ring-[var(--focus-ring)] focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1\",\n \"dark:border-gray-800 dark:bg-gray-950 dark:text-white dark:focus-visible:ring-0 dark:focus-visible:ring-offset-gray-700\",\n className\n )}\n ref={ref}\n {...props}\n >\n {children}\n <SelectPrimitive.Icon asChild>\n {icon ?? <ChevronDown className=\"h-5 w-5\" />}\n </SelectPrimitive.Icon>\n </SelectPrimitive.Trigger>\n);\n\ninterface SelectScrollUpButtonProps\n extends Omit<\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollUpButton>,\n \"ref\"\n > {\n ref?: Ref<React.ElementRef<typeof SelectPrimitive.ScrollUpButton>>;\n}\n\nconst SelectScrollUpButton = ({\n className,\n ref,\n ...props\n}: SelectScrollUpButtonProps) => (\n <SelectPrimitive.ScrollUpButton\n className={cn(\n \"flex cursor-default items-center justify-center py-1\",\n className\n )}\n ref={ref}\n {...props}\n >\n <ChevronUp className=\"h-5 w-5\" />\n </SelectPrimitive.ScrollUpButton>\n);\n\ninterface SelectScrollDownButtonProps\n extends Omit<\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollDownButton>,\n \"ref\"\n > {\n ref?: Ref<React.ElementRef<typeof SelectPrimitive.ScrollDownButton>>;\n}\n\nconst SelectScrollDownButton = ({\n className,\n ref,\n ...props\n}: SelectScrollDownButtonProps) => (\n <SelectPrimitive.ScrollDownButton\n className={cn(\n \"flex cursor-default items-center justify-center py-1\",\n className\n )}\n ref={ref}\n {...props}\n >\n <ChevronDown className=\"h-4 w-4\" />\n </SelectPrimitive.ScrollDownButton>\n);\n\ninterface SelectContentProps\n extends Omit<\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>,\n \"ref\"\n > {\n ref?: Ref<React.ElementRef<typeof SelectPrimitive.Content>>;\n}\n\nconst SelectContent = ({\n className,\n children,\n position,\n ref,\n ...props\n}: SelectContentProps) => (\n <SelectPrimitive.Portal>\n <SelectPrimitive.Content\n className={cn(\n \"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-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 relative z-50 max-h-96 min-w-[8rem] rounded-md border border-gray-200 border-solid bg-white text-gray-950 shadow-[0px_2px_4px_0px_rgba(0,_0,_0,_0.15)] data-[state=closed]:animate-out data-[state=open]:animate-in dark:border-gray-800 dark:bg-gray-950 dark:text-white\",\n position === \"popper\" &&\n \"data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=bottom]:translate-y-1 data-[side=top]:-translate-y-1\",\n className\n )}\n position={position}\n ref={ref}\n {...props}\n >\n <SelectScrollUpButton />\n <SelectPrimitive.Viewport\n className={cn(\n \"p-1\",\n position === \"popper\" &&\n \"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]\"\n )}\n >\n {children}\n </SelectPrimitive.Viewport>\n <SelectScrollDownButton />\n </SelectPrimitive.Content>\n </SelectPrimitive.Portal>\n);\n\ninterface SelectContentPopperProps\n extends Omit<\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>,\n \"ref\"\n > {\n ref?: Ref<React.ElementRef<typeof SelectPrimitive.Content>>;\n}\n\nconst SelectContentPopper = ({\n className,\n children,\n position = \"popper\",\n ref,\n ...props\n}: SelectContentPopperProps) => (\n <SelectPrimitive.Portal>\n <SelectPrimitive.Content\n className={cn(\n \"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-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 relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border border-gray-200 border-solid bg-white text-gray-950 shadow-100 data-[state=closed]:animate-out data-[state=open]:animate-in dark:border-gray-800 dark:bg-gray-950 dark:text-white\",\n position === \"popper\" &&\n \"data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=bottom]:translate-y-1 data-[side=top]:-translate-y-1\",\n className\n )}\n position={position}\n ref={ref}\n {...props}\n >\n <SelectScrollUpButton />\n <SelectPrimitive.Viewport\n className={cn(\n \"p-1\",\n position === \"popper\" &&\n \"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]\"\n )}\n >\n {children}\n </SelectPrimitive.Viewport>\n <SelectScrollDownButton />\n </SelectPrimitive.Content>\n </SelectPrimitive.Portal>\n);\n\ninterface SelectLabelProps\n extends Omit<\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>,\n \"ref\"\n > {\n ref?: Ref<React.ElementRef<typeof SelectPrimitive.Label>>;\n}\n\nconst SelectLabel = ({ className, ref, ...props }: SelectLabelProps) => (\n <SelectPrimitive.Label\n className={cn(\"py-1.5 pr-2 pl-8 font-semibold text-base\", className)}\n ref={ref}\n {...props}\n />\n);\n\ninterface SelectItemProps\n extends Omit<\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>,\n \"ref\"\n > {\n hideCheck?: boolean;\n ref?: Ref<React.ElementRef<typeof SelectPrimitive.Item>>;\n}\n\nconst SelectItem = ({\n className,\n children,\n hideCheck,\n ref,\n ...props\n}: SelectItemProps) => (\n <SelectPrimitive.Item\n className={cn(\n \"relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pr-2 pl-8 text-base outline-none focus:bg-gray-50 focus:text-gray-950 data-[disabled]:pointer-events-none data-[disabled]:opacity-50 dark:focus:bg-gray-900 dark:focus:text-white\",\n className\n )}\n ref={ref}\n {...props}\n >\n {hideCheck ? null : (\n <span className=\"absolute left-2 flex h-3.5 w-3.5 items-center justify-center\">\n <SelectPrimitive.ItemIndicator>\n <Check className=\"h-4 w-4\" />\n </SelectPrimitive.ItemIndicator>\n </span>\n )}\n\n <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>\n </SelectPrimitive.Item>\n);\n\ninterface SelectSeparatorProps\n extends Omit<\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>,\n \"ref\"\n > {\n ref?: Ref<React.ElementRef<typeof SelectPrimitive.Separator>>;\n}\n\nconst SelectSeparator = ({\n className,\n ref,\n ...props\n}: SelectSeparatorProps) => (\n <SelectPrimitive.Separator\n className={cn(\"-mx-1 my-1 h-px bg-gray-150 dark:bg-gray-800\", className)}\n ref={ref}\n {...props}\n />\n);\n\nexport {\n Select,\n SelectGroup,\n SelectValue,\n SelectTrigger,\n SelectContent,\n SelectContentPopper,\n SelectLabel,\n SelectItem,\n SelectSeparator,\n SelectScrollUpButton,\n SelectScrollDownButton,\n};\n"],
4
+ "sourcesContent": ["/**\n * @module Select\n *\n * Dropdown select menu for single value selection. Built on Radix UI Select primitive.\n * Includes keyboard navigation, typeahead, and customizable styling.\n *\n * @see {@link https://ui.shadcn.com/docs/components/select Shadcn Select}\n * @see {@link https://www.radix-ui.com/primitives/docs/components/select Radix Select}\n *\n * @example\n * // Basic select\n * <Select>\n * <SelectTrigger className=\"w-48\">\n * <SelectValue placeholder=\"Select option\" />\n * </SelectTrigger>\n * <SelectContent>\n * <SelectItem value=\"a\">Option A</SelectItem>\n * <SelectItem value=\"b\">Option B</SelectItem>\n * </SelectContent>\n * </Select>\n *\n * @example\n * // Grouped options\n * <SelectContent>\n * <SelectGroup>\n * <SelectLabel>Fruits</SelectLabel>\n * <SelectItem value=\"apple\">Apple</SelectItem>\n * <SelectItem value=\"banana\">Banana</SelectItem>\n * </SelectGroup>\n * <SelectSeparator />\n * <SelectGroup>\n * <SelectLabel>Vegetables</SelectLabel>\n * <SelectItem value=\"carrot\">Carrot</SelectItem>\n * </SelectGroup>\n * </SelectContent>\n */\nimport { Check, ChevronDown, ChevronUp } from \"lucide-react\";\nimport { Select as SelectPrimitive } from \"radix-ui\";\nimport type * as React from \"react\";\nimport type { Ref } from \"react\";\n\nimport { cn } from \"../lib/utils\";\n\n/** Root component that manages select state. */\nconst Select = SelectPrimitive.Root;\n\n/** Container for grouping related select items with a label. */\nconst SelectGroup = SelectPrimitive.Group;\n\n/** Displays the selected value or placeholder text. */\nconst SelectValue = SelectPrimitive.Value;\n\n/**\n * Button that opens the select dropdown.\n * @param icon - Custom dropdown icon (defaults to ChevronDown)\n */\ninterface SelectTriggerProps\n extends Omit<\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>,\n \"ref\"\n > {\n icon?: React.ReactNode;\n ref?: Ref<React.ElementRef<typeof SelectPrimitive.Trigger>>;\n}\n\nconst SelectTrigger = ({\n className,\n children,\n icon,\n ref,\n ...props\n}: SelectTriggerProps) => (\n <SelectPrimitive.Trigger\n className={cn(\n \"group flex h-10 w-full min-w-44 items-center justify-between gap-2 rounded-lg border border-gray-200 border-solid bg-white px-4 py-2 text-base transition-colors focus-visible:border-gray-400 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--focus-ring)] focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1\",\n \"dark:border-gray-800 dark:bg-gray-950 dark:text-white dark:focus-visible:border-gray-600 dark:focus-visible:ring-[var(--focus-ring)] dark:focus-visible:ring-offset-gray-950\",\n className\n )}\n ref={ref}\n {...props}\n >\n {children}\n <SelectPrimitive.Icon asChild>\n {icon ?? <ChevronDown className=\"h-5 w-5\" />}\n </SelectPrimitive.Icon>\n </SelectPrimitive.Trigger>\n);\n\ninterface SelectScrollUpButtonProps\n extends Omit<\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollUpButton>,\n \"ref\"\n > {\n ref?: Ref<React.ElementRef<typeof SelectPrimitive.ScrollUpButton>>;\n}\n\nconst SelectScrollUpButton = ({\n className,\n ref,\n ...props\n}: SelectScrollUpButtonProps) => (\n <SelectPrimitive.ScrollUpButton\n className={cn(\n \"flex cursor-default items-center justify-center py-1\",\n className\n )}\n ref={ref}\n {...props}\n >\n <ChevronUp className=\"h-5 w-5\" />\n </SelectPrimitive.ScrollUpButton>\n);\n\ninterface SelectScrollDownButtonProps\n extends Omit<\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollDownButton>,\n \"ref\"\n > {\n ref?: Ref<React.ElementRef<typeof SelectPrimitive.ScrollDownButton>>;\n}\n\nconst SelectScrollDownButton = ({\n className,\n ref,\n ...props\n}: SelectScrollDownButtonProps) => (\n <SelectPrimitive.ScrollDownButton\n className={cn(\n \"flex cursor-default items-center justify-center py-1\",\n className\n )}\n ref={ref}\n {...props}\n >\n <ChevronDown className=\"h-4 w-4\" />\n </SelectPrimitive.ScrollDownButton>\n);\n\ninterface SelectContentProps\n extends Omit<\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>,\n \"ref\"\n > {\n ref?: Ref<React.ElementRef<typeof SelectPrimitive.Content>>;\n}\n\nconst SelectContent = ({\n className,\n children,\n position,\n ref,\n ...props\n}: SelectContentProps) => (\n <SelectPrimitive.Portal>\n <SelectPrimitive.Content\n className={cn(\n \"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-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 relative z-50 max-h-96 min-w-[8rem] rounded-lg border border-gray-200 border-solid bg-white text-gray-950 shadow-[0px_2px_4px_0px_rgba(0,_0,_0,_0.15)] data-[state=closed]:animate-out data-[state=open]:animate-in dark:border-gray-800 dark:bg-gray-950 dark:text-white\",\n position === \"popper\" &&\n \"data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=bottom]:translate-y-1 data-[side=top]:-translate-y-1\",\n className\n )}\n position={position}\n ref={ref}\n {...props}\n >\n <SelectScrollUpButton />\n <SelectPrimitive.Viewport\n className={cn(\n \"p-1\",\n position === \"popper\" &&\n \"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]\"\n )}\n >\n {children}\n </SelectPrimitive.Viewport>\n <SelectScrollDownButton />\n </SelectPrimitive.Content>\n </SelectPrimitive.Portal>\n);\n\ninterface SelectContentPopperProps\n extends Omit<\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>,\n \"ref\"\n > {\n ref?: Ref<React.ElementRef<typeof SelectPrimitive.Content>>;\n}\n\nconst SelectContentPopper = ({\n className,\n children,\n position = \"popper\",\n ref,\n ...props\n}: SelectContentPopperProps) => (\n <SelectPrimitive.Portal>\n <SelectPrimitive.Content\n className={cn(\n \"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-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 relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-lg border border-gray-200 border-solid bg-white text-gray-950 shadow-100 data-[state=closed]:animate-out data-[state=open]:animate-in dark:border-gray-800 dark:bg-gray-950 dark:text-white\",\n position === \"popper\" &&\n \"data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=bottom]:translate-y-1 data-[side=top]:-translate-y-1\",\n className\n )}\n position={position}\n ref={ref}\n {...props}\n >\n <SelectScrollUpButton />\n <SelectPrimitive.Viewport\n className={cn(\n \"p-1\",\n position === \"popper\" &&\n \"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]\"\n )}\n >\n {children}\n </SelectPrimitive.Viewport>\n <SelectScrollDownButton />\n </SelectPrimitive.Content>\n </SelectPrimitive.Portal>\n);\n\ninterface SelectLabelProps\n extends Omit<\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>,\n \"ref\"\n > {\n ref?: Ref<React.ElementRef<typeof SelectPrimitive.Label>>;\n}\n\nconst SelectLabel = ({ className, ref, ...props }: SelectLabelProps) => (\n <SelectPrimitive.Label\n className={cn(\"py-1.5 pr-2 pl-8 font-semibold text-base\", className)}\n ref={ref}\n {...props}\n />\n);\n\ninterface SelectItemProps\n extends Omit<\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>,\n \"ref\"\n > {\n hideCheck?: boolean;\n ref?: Ref<React.ElementRef<typeof SelectPrimitive.Item>>;\n}\n\nconst SelectItem = ({\n className,\n children,\n hideCheck,\n ref,\n ...props\n}: SelectItemProps) => (\n <SelectPrimitive.Item\n className={cn(\n \"relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pr-2 pl-8 text-base outline-none focus:bg-gray-50 focus:text-gray-950 data-[disabled]:pointer-events-none data-[disabled]:opacity-50 dark:focus:bg-gray-900 dark:focus:text-white\",\n className\n )}\n ref={ref}\n {...props}\n >\n {hideCheck ? null : (\n <span className=\"absolute left-2 flex h-3.5 w-3.5 items-center justify-center\">\n <SelectPrimitive.ItemIndicator>\n <Check className=\"h-4 w-4\" />\n </SelectPrimitive.ItemIndicator>\n </span>\n )}\n\n <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>\n </SelectPrimitive.Item>\n);\n\ninterface SelectSeparatorProps\n extends Omit<\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>,\n \"ref\"\n > {\n ref?: Ref<React.ElementRef<typeof SelectPrimitive.Separator>>;\n}\n\nconst SelectSeparator = ({\n className,\n ref,\n ...props\n}: SelectSeparatorProps) => (\n <SelectPrimitive.Separator\n className={cn(\"-mx-1 my-1 h-px bg-gray-150 dark:bg-gray-800\", className)}\n ref={ref}\n {...props}\n />\n);\n\nexport {\n Select,\n SelectGroup,\n SelectValue,\n SelectTrigger,\n SelectContent,\n SelectContentPopper,\n SelectLabel,\n SelectItem,\n SelectSeparator,\n SelectScrollUpButton,\n SelectScrollDownButton,\n};\n"],
5
5
  "mappings": "AAwEE,SAWa,KAXb;AApCF,SAAS,OAAO,aAAa,iBAAiB;AAC9C,SAAS,UAAU,uBAAuB;AAI1C,SAAS,UAAU;AAGnB,MAAM,SAAS,gBAAgB;AAG/B,MAAM,cAAc,gBAAgB;AAGpC,MAAM,cAAc,gBAAgB;AAepC,MAAM,gBAAgB,CAAC;AAAA,EACrB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,GAAG;AACL,MACE;AAAA,EAAC,gBAAgB;AAAA,EAAhB;AAAA,IACC,WAAW;AAAA,MACT;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA;AAAA,IACC,GAAG;AAAA,IAEH;AAAA;AAAA,MACD,oBAAC,gBAAgB,MAAhB,EAAqB,SAAO,MAC1B,kBAAQ,oBAAC,eAAY,WAAU,WAAU,GAC5C;AAAA;AAAA;AACF;AAWF,MAAM,uBAAuB,CAAC;AAAA,EAC5B;AAAA,EACA;AAAA,EACA,GAAG;AACL,MACE;AAAA,EAAC,gBAAgB;AAAA,EAAhB;AAAA,IACC,WAAW;AAAA,MACT;AAAA,MACA;AAAA,IACF;AAAA,IACA;AAAA,IACC,GAAG;AAAA,IAEJ,8BAAC,aAAU,WAAU,WAAU;AAAA;AACjC;AAWF,MAAM,yBAAyB,CAAC;AAAA,EAC9B;AAAA,EACA;AAAA,EACA,GAAG;AACL,MACE;AAAA,EAAC,gBAAgB;AAAA,EAAhB;AAAA,IACC,WAAW;AAAA,MACT;AAAA,MACA;AAAA,IACF;AAAA,IACA;AAAA,IACC,GAAG;AAAA,IAEJ,8BAAC,eAAY,WAAU,WAAU;AAAA;AACnC;AAWF,MAAM,gBAAgB,CAAC;AAAA,EACrB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,GAAG;AACL,MACE,oBAAC,gBAAgB,QAAhB,EACC;AAAA,EAAC,gBAAgB;AAAA,EAAhB;AAAA,IACC,WAAW;AAAA,MACT;AAAA,MACA,aAAa,YACX;AAAA,MACF;AAAA,IACF;AAAA,IACA;AAAA,IACA;AAAA,IACC,GAAG;AAAA,IAEJ;AAAA,0BAAC,wBAAqB;AAAA,MACtB;AAAA,QAAC,gBAAgB;AAAA,QAAhB;AAAA,UACC,WAAW;AAAA,YACT;AAAA,YACA,aAAa,YACX;AAAA,UACJ;AAAA,UAEC;AAAA;AAAA,MACH;AAAA,MACA,oBAAC,0BAAuB;AAAA;AAAA;AAC1B,GACF;AAWF,MAAM,sBAAsB,CAAC;AAAA,EAC3B;AAAA,EACA;AAAA,EACA,WAAW;AAAA,EACX;AAAA,EACA,GAAG;AACL,MACE,oBAAC,gBAAgB,QAAhB,EACC;AAAA,EAAC,gBAAgB;AAAA,EAAhB;AAAA,IACC,WAAW;AAAA,MACT;AAAA,MACA,aAAa,YACX;AAAA,MACF;AAAA,IACF;AAAA,IACA;AAAA,IACA;AAAA,IACC,GAAG;AAAA,IAEJ;AAAA,0BAAC,wBAAqB;AAAA,MACtB;AAAA,QAAC,gBAAgB;AAAA,QAAhB;AAAA,UACC,WAAW;AAAA,YACT;AAAA,YACA,aAAa,YACX;AAAA,UACJ;AAAA,UAEC;AAAA;AAAA,MACH;AAAA,MACA,oBAAC,0BAAuB;AAAA;AAAA;AAC1B,GACF;AAWF,MAAM,cAAc,CAAC,EAAE,WAAW,KAAK,GAAG,MAAM,MAC9C;AAAA,EAAC,gBAAgB;AAAA,EAAhB;AAAA,IACC,WAAW,GAAG,4CAA4C,SAAS;AAAA,IACnE;AAAA,IACC,GAAG;AAAA;AACN;AAYF,MAAM,aAAa,CAAC;AAAA,EAClB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,GAAG;AACL,MACE;AAAA,EAAC,gBAAgB;AAAA,EAAhB;AAAA,IACC,WAAW;AAAA,MACT;AAAA,MACA;AAAA,IACF;AAAA,IACA;AAAA,IACC,GAAG;AAAA,IAEH;AAAA,kBAAY,OACX,oBAAC,UAAK,WAAU,gEACd,8BAAC,gBAAgB,eAAhB,EACC,8BAAC,SAAM,WAAU,WAAU,GAC7B,GACF;AAAA,MAGF,oBAAC,gBAAgB,UAAhB,EAA0B,UAAS;AAAA;AAAA;AACtC;AAWF,MAAM,kBAAkB,CAAC;AAAA,EACvB;AAAA,EACA;AAAA,EACA,GAAG;AACL,MACE;AAAA,EAAC,gBAAgB;AAAA,EAAhB;AAAA,IACC,WAAW,GAAG,gDAAgD,SAAS;AAAA,IACvE;AAAA,IACC,GAAG;AAAA;AACN;",
6
6
  "names": []
7
7
  }
@@ -1,6 +1,6 @@
1
- @tailwind base;
2
- @tailwind components;
3
- @tailwind utilities;
1
+ @import "tailwindcss";
2
+ @import "../../tailwind.config.v4.css";
3
+ @import "./index.v4.css";
4
4
 
5
5
  /* Storybook-specific global styles */
6
6
  body {
@@ -11,4 +11,6 @@ body {
11
11
  .theme-dark {
12
12
  background-color: #151a20;
13
13
  color: #fff;
14
+ -webkit-font-smoothing: antialiased;
15
+ -moz-osx-font-smoothing: grayscale;
14
16
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@music-vine/cadence",
3
- "version": "2.5.0",
3
+ "version": "2.6.0",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -100,7 +100,7 @@
100
100
  "class-variance-authority": "^0.7.1",
101
101
  "clsx": "^2.1.1",
102
102
  "embla-carousel-react": "^8.5.1",
103
- "lodash": "^4.17.23",
103
+ "lodash": "^4.18.1",
104
104
  "lucide-react": "^0.531.0",
105
105
  "motion": "^12.0.0",
106
106
  "radix-ui": "^1.1.2",
@@ -139,7 +139,7 @@
139
139
  "tailwindcss-v3": "npm:tailwindcss@^3.4.18",
140
140
  "tsx": "^4.21.0",
141
141
  "typescript": "^5.2.2",
142
- "vite": "^6.4.2",
142
+ "vite": "^8.0.7",
143
143
  "vitest": "^3.2.4"
144
144
  },
145
145
  "scripts": {