@avenue-ticketing/ui 0.4.0 → 0.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.
Files changed (73) hide show
  1. package/dist/react/avatar.d.ts +42 -0
  2. package/dist/react/avatar.js +159 -0
  3. package/dist/react/avatar.js.map +1 -0
  4. package/dist/react/badge.d.ts +12 -0
  5. package/dist/react/badge.js +35 -1
  6. package/dist/react/badge.js.map +1 -1
  7. package/dist/react/button.d.ts +1 -1
  8. package/dist/react/button.js +3 -3
  9. package/dist/react/button.js.map +1 -1
  10. package/dist/react/calendar.d.ts +13 -0
  11. package/dist/react/calendar.js +4639 -0
  12. package/dist/react/calendar.js.map +1 -0
  13. package/dist/react/card.d.ts +11 -0
  14. package/dist/react/card.js +113 -0
  15. package/dist/react/card.js.map +1 -0
  16. package/dist/react/checkbox.d.ts +11 -0
  17. package/dist/react/checkbox.js +129 -0
  18. package/dist/react/checkbox.js.map +1 -0
  19. package/dist/react/datetime-picker.d.ts +21 -0
  20. package/dist/react/datetime-picker.js +6124 -0
  21. package/dist/react/datetime-picker.js.map +1 -0
  22. package/dist/react/dialog.js +1 -1
  23. package/dist/react/dialog.js.map +1 -1
  24. package/dist/react/dropdown.d.ts +34 -9
  25. package/dist/react/dropdown.js +295 -144
  26. package/dist/react/dropdown.js.map +1 -1
  27. package/dist/react/input.d.ts +7 -0
  28. package/dist/react/input.js +15 -2
  29. package/dist/react/input.js.map +1 -1
  30. package/dist/react/pagination.d.ts +28 -0
  31. package/dist/react/pagination.js +262 -0
  32. package/dist/react/pagination.js.map +1 -0
  33. package/dist/react/popover.d.ts +76 -0
  34. package/dist/react/popover.js +564 -0
  35. package/dist/react/popover.js.map +1 -0
  36. package/dist/react/scroll-header.js +13 -1
  37. package/dist/react/scroll-header.js.map +1 -1
  38. package/dist/react/scroll-wheel.d.ts +45 -0
  39. package/dist/react/scroll-wheel.js +557 -0
  40. package/dist/react/scroll-wheel.js.map +1 -0
  41. package/dist/react/select.d.ts +62 -0
  42. package/dist/react/select.js +889 -0
  43. package/dist/react/select.js.map +1 -0
  44. package/dist/react/sheet.js +1 -1
  45. package/dist/react/sheet.js.map +1 -1
  46. package/dist/react/switch.d.ts +38 -0
  47. package/dist/react/switch.js +117 -0
  48. package/dist/react/switch.js.map +1 -0
  49. package/dist/react/table-pagination.d.ts +15 -0
  50. package/dist/react/table-pagination.js +1153 -0
  51. package/dist/react/table-pagination.js.map +1 -0
  52. package/dist/react/table-view/column-menu.d.ts +15 -0
  53. package/dist/react/table-view/column-menu.js +955 -0
  54. package/dist/react/table-view/column-menu.js.map +1 -0
  55. package/dist/react/table-view/index.d.ts +70 -0
  56. package/dist/react/table-view/index.js +2190 -0
  57. package/dist/react/table-view/index.js.map +1 -0
  58. package/dist/react/table.d.ts +86 -0
  59. package/dist/react/table.js +414 -0
  60. package/dist/react/table.js.map +1 -0
  61. package/dist/react/tabs.d.ts +9 -3
  62. package/dist/react/tabs.js +204 -48
  63. package/dist/react/tabs.js.map +1 -1
  64. package/dist/react/textarea.d.ts +6 -0
  65. package/dist/react/textarea.js +33 -0
  66. package/dist/react/textarea.js.map +1 -0
  67. package/dist/react/time-picker.d.ts +22 -0
  68. package/dist/react/time-picker.js +856 -0
  69. package/dist/react/time-picker.js.map +1 -0
  70. package/dist/react/tooltip.d.ts +45 -0
  71. package/dist/react/tooltip.js +540 -0
  72. package/dist/react/tooltip.js.map +1 -0
  73. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/lib/utils.ts","../../src/react/scroll-header.tsx"],"names":[],"mappings":"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","file":"scroll-header.js","sourcesContent":["import { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n","\"use client\";\n\nimport { cn } from \"@/lib/utils\";\nimport {\n Children,\n createContext,\n isValidElement,\n ReactElement,\n ReactNode,\n useContext,\n useEffect,\n useRef,\n} from \"react\";\n\nconst DATA_ATTR = \"data-scrolled\";\n\nexport type ScrollHeaderRevealAt = \"start\" | \"center\" | \"end\";\n\ninterface ScrollHeaderContextValue {\n revealAt: ScrollHeaderRevealAt;\n slideIn: boolean;\n onRevealChange?: (revealed: boolean) => void;\n}\n\nconst ScrollHeaderContext = createContext<ScrollHeaderContextValue>({\n revealAt: \"center\",\n slideIn: true,\n onRevealChange: undefined,\n});\n\nfunction isScrollHeaderStickyEl(child: ReactElement): boolean {\n return (\n (child.type as { displayName?: string }).displayName ===\n \"ScrollHeaderSticky\"\n );\n}\n\nfunction isScrollHeaderTopEl(child: ReactElement): boolean {\n return (\n (child.type as { displayName?: string }).displayName === \"ScrollHeaderTop\"\n );\n}\n\n/** Sticky first so `position: sticky; top: 0` pins to the scrollport, not below the hero. */\nfunction reorderScrollHeaderChildren(children: ReactNode): ReactNode[] {\n const sticky: ReactNode[] = [];\n const top: ReactNode[] = [];\n const rest: ReactNode[] = [];\n\n Children.forEach(children, (child) => {\n if (!isValidElement(child)) {\n if (child != null && child !== false) rest.push(child);\n return;\n }\n if (isScrollHeaderStickyEl(child)) sticky.push(child);\n else if (isScrollHeaderTopEl(child)) top.push(child);\n else rest.push(child);\n });\n\n return [...sticky, ...top, ...rest];\n}\n\ninterface ScrollHeaderProps extends React.HTMLAttributes<HTMLDivElement> {\n children: ReactNode;\n /**\n * Which part of `ScrollHeaderTop` must leave the scrollport before the sticky bar shows.\n * `start` — top edge (earliest). `center` — vertical midpoint. `end` — bottom edge (latest).\n * If omitted: defaults to `center` when `slideIn` is true, and `start` when `slideIn` is false (in-flow bar).\n */\n revealAt?: ScrollHeaderRevealAt;\n /**\n * When `true` (default), `ScrollHeaderSticky` overlays without layout height and slides in with opacity.\n * When `false`, the sticky bar stays in document flow (takes vertical space); use `onRevealChange` and\n * your own styles for any fade or toolbar behavior.\n */\n slideIn?: boolean;\n /**\n * Called when the sticky header reveal state changes after scroll.\n * `true` once the top section has crossed the reveal threshold (same moment as `data-scrolled=\"true\"`).\n */\n onRevealChange?: (revealed: boolean) => void;\n}\n\nconst ScrollHeader: React.FC<ScrollHeaderProps> = ({\n revealAt,\n slideIn = true,\n onRevealChange,\n children,\n className,\n ...props\n}) => {\n const ref = useRef<HTMLDivElement>(null);\n const resolvedRevealAt = revealAt ?? (slideIn ? \"center\" : \"start\");\n\n return (\n <ScrollHeaderContext.Provider\n value={{\n revealAt: resolvedRevealAt,\n slideIn,\n onRevealChange,\n }}\n >\n <div\n ref={ref}\n data-scrolled=\"false\"\n className={cn(\n \"scroll-header bg-background flex h-full flex-col overflow-y-auto\",\n className,\n )}\n {...props}\n >\n {reorderScrollHeaderChildren(children)}\n </div>\n </ScrollHeaderContext.Provider>\n );\n};\n\nconst sentinelClass = \"pointer-events-none h-px w-full shrink-0\";\n\nconst ScrollHeaderTop: React.FC<React.HTMLAttributes<HTMLDivElement>> = ({\n children,\n className,\n ...props\n}) => {\n const { revealAt, onRevealChange } = useContext(ScrollHeaderContext);\n const sentinelRef = useRef<HTMLDivElement>(null);\n const onRevealChangeRef = useRef(onRevealChange);\n onRevealChangeRef.current = onRevealChange;\n const lastRevealedRef = useRef<boolean | undefined>(undefined);\n\n useEffect(() => {\n const sentinel = sentinelRef.current;\n if (!sentinel) return;\n\n const container = sentinel.closest<HTMLElement>(\"[data-scrolled]\");\n if (!container) return;\n\n lastRevealedRef.current = undefined;\n\n const observer = new IntersectionObserver(\n ([entry]) => {\n if (!entry) return;\n const revealed = !entry.isIntersecting;\n container.setAttribute(DATA_ATTR, revealed ? \"true\" : \"false\");\n if (lastRevealedRef.current !== revealed) {\n lastRevealedRef.current = revealed;\n onRevealChangeRef.current?.(revealed);\n }\n },\n { root: container, threshold: 0 },\n );\n\n observer.observe(sentinel);\n return () => observer.disconnect();\n }, [revealAt]);\n\n if (revealAt === \"end\") {\n return (\n <div className={cn(\"shrink-0\", className)} {...props}>\n {children}\n <div ref={sentinelRef} className={sentinelClass} aria-hidden />\n </div>\n );\n }\n\n if (revealAt === \"center\") {\n return (\n <div className={cn(\"relative shrink-0\", className)} {...props}>\n <div\n ref={sentinelRef}\n className={cn(\n sentinelClass,\n \"absolute left-0 right-0 top-1/2 z-0 -translate-y-1/2\",\n )}\n aria-hidden\n />\n {children}\n </div>\n );\n }\n\n return (\n <div className={cn(\"shrink-0\", className)} {...props}>\n <div ref={sentinelRef} className={sentinelClass} aria-hidden />\n {children}\n </div>\n );\n};\n\nScrollHeaderTop.displayName = \"ScrollHeaderTop\";\n\nconst stickyChromeBgClass =\n \"bg-background supports-backdrop-filter:bg-background\";\n\nconst stickyChromeBorderClass = \"border-primary/10 border-b\";\n\nconst stickyChromeClass = cn(stickyChromeBgClass, stickyChromeBorderClass);\n\nconst revealEase =\n \"duration-300 ease-[cubic-bezier(0.22,1,0.36,1)] motion-reduce:duration-0 motion-reduce:transition-none\";\n\nconst ScrollHeaderSticky: React.FC<React.HTMLAttributes<HTMLDivElement>> = ({\n children,\n className,\n ...props\n}) => {\n const { slideIn } = useContext(ScrollHeaderContext);\n\n return (\n <div\n className={cn(\n \"sticky top-0 z-40\",\n slideIn ? \"h-0 overflow-visible\" : \"shrink-0\",\n )}\n >\n {slideIn ? (\n <div\n className={cn(\n stickyChromeClass,\n \"transition-[opacity, transform]\",\n revealEase,\n '[.scroll-header:not([data-scrolled=\"true\"])_&]:pointer-events-none [.scroll-header:not([data-scrolled=\"true\"])_&]:-translate-y-1 [.scroll-header:not([data-scrolled=\"true\"])_&]:opacity-0',\n '[.scroll-header[data-scrolled=\"true\"]_&]:translate-y-0 [.scroll-header[data-scrolled=\"true\"]_&]:opacity-100',\n className,\n )}\n {...props}\n >\n {children}\n </div>\n ) : (\n <div className={stickyChromeBgClass}>\n <div className={cn(stickyChromeBorderClass, className)} {...props}>\n {children}\n </div>\n </div>\n )}\n </div>\n );\n};\n\nScrollHeaderSticky.displayName = \"ScrollHeaderSticky\";\n\nexport { ScrollHeader, ScrollHeaderSticky, ScrollHeaderTop };\n"]}
1
+ {"version":3,"sources":["../../src/lib/utils.ts","../../src/react/scroll-header.tsx"],"names":[],"mappings":"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","file":"scroll-header.js","sourcesContent":["import { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n","\"use client\";\n\nimport { cn } from \"@/lib/utils\";\nimport {\n Children,\n createContext,\n isValidElement,\n ReactElement,\n ReactNode,\n useContext,\n useEffect,\n useRef,\n} from \"react\";\n\nconst DATA_ATTR = \"data-scrolled\";\n\nexport type ScrollHeaderRevealAt = \"start\" | \"center\" | \"end\";\n\ninterface ScrollHeaderContextValue {\n revealAt: ScrollHeaderRevealAt;\n slideIn: boolean;\n onRevealChange?: (revealed: boolean) => void;\n}\n\nconst ScrollHeaderContext = createContext<ScrollHeaderContextValue>({\n revealAt: \"center\",\n slideIn: true,\n onRevealChange: undefined,\n});\n\nfunction isScrollHeaderStickyEl(child: ReactElement): boolean {\n return (\n (child.type as { displayName?: string }).displayName ===\n \"ScrollHeaderSticky\"\n );\n}\n\nfunction isScrollHeaderTopEl(child: ReactElement): boolean {\n return (\n (child.type as { displayName?: string }).displayName === \"ScrollHeaderTop\"\n );\n}\n\n/** Sticky first so `position: sticky; top: 0` pins to the scrollport, not below the hero. */\nfunction reorderScrollHeaderChildren(children: ReactNode): ReactNode[] {\n const sticky: ReactNode[] = [];\n const top: ReactNode[] = [];\n const rest: ReactNode[] = [];\n\n Children.forEach(children, (child) => {\n if (!isValidElement(child)) {\n if (child != null && child !== false) rest.push(child);\n return;\n }\n if (isScrollHeaderStickyEl(child)) sticky.push(child);\n else if (isScrollHeaderTopEl(child)) top.push(child);\n else rest.push(child);\n });\n\n return [...sticky, ...top, ...rest];\n}\n\ninterface ScrollHeaderProps extends React.HTMLAttributes<HTMLDivElement> {\n children: ReactNode;\n /**\n * Which part of `ScrollHeaderTop` must leave the scrollport before the sticky bar shows.\n * `start` — top edge (earliest). `center` — vertical midpoint. `end` — bottom edge (latest).\n * If omitted: defaults to `center` when `slideIn` is true, and `start` when `slideIn` is false (in-flow bar).\n */\n revealAt?: ScrollHeaderRevealAt;\n /**\n * When `true` (default), `ScrollHeaderSticky` overlays without layout height and slides in with opacity.\n * When `false`, the sticky bar stays in document flow (takes vertical space); use `onRevealChange` and\n * your own styles for any fade or toolbar behavior.\n */\n slideIn?: boolean;\n /**\n * Called when the sticky header reveal state changes after scroll.\n * `true` once the top section has crossed the reveal threshold (same moment as `data-scrolled=\"true\"`).\n */\n onRevealChange?: (revealed: boolean) => void;\n}\n\nconst ScrollHeader: React.FC<ScrollHeaderProps> = ({\n revealAt,\n slideIn = true,\n onRevealChange,\n children,\n className,\n ...props\n}) => {\n const ref = useRef<HTMLDivElement>(null);\n const resolvedRevealAt = revealAt ?? (slideIn ? \"center\" : \"start\");\n\n return (\n <ScrollHeaderContext.Provider\n value={{\n revealAt: resolvedRevealAt,\n slideIn,\n onRevealChange,\n }}\n >\n <div\n ref={ref}\n data-scrolled=\"false\"\n className={cn(\n \"scroll-header bg-background flex h-full flex-col overflow-y-auto\",\n className,\n )}\n {...props}\n >\n {reorderScrollHeaderChildren(children)}\n </div>\n </ScrollHeaderContext.Provider>\n );\n};\n\nconst sentinelClass = \"pointer-events-none h-px w-full shrink-0\";\n\nconst ScrollHeaderTop: React.FC<React.HTMLAttributes<HTMLDivElement>> = ({\n children,\n className,\n ...props\n}) => {\n const { revealAt, onRevealChange } = useContext(ScrollHeaderContext);\n const sentinelRef = useRef<HTMLDivElement>(null);\n const onRevealChangeRef = useRef(onRevealChange);\n onRevealChangeRef.current = onRevealChange;\n const lastRevealedRef = useRef<boolean | undefined>(undefined);\n\n useEffect(() => {\n const sentinel = sentinelRef.current;\n if (!sentinel) return;\n\n const container = sentinel.closest<HTMLElement>(\"[data-scrolled]\");\n if (!container) return;\n\n lastRevealedRef.current = undefined;\n\n const observer = new IntersectionObserver(\n ([entry]) => {\n if (!entry) return;\n const revealed = !entry.isIntersecting;\n container.setAttribute(DATA_ATTR, revealed ? \"true\" : \"false\");\n if (lastRevealedRef.current !== revealed) {\n lastRevealedRef.current = revealed;\n onRevealChangeRef.current?.(revealed);\n }\n },\n { root: container, threshold: 0 },\n );\n\n observer.observe(sentinel);\n return () => observer.disconnect();\n }, [revealAt]);\n\n if (revealAt === \"end\") {\n return (\n <div className={cn(\"shrink-0\", className)} {...props}>\n {children}\n <div ref={sentinelRef} className={sentinelClass} aria-hidden />\n </div>\n );\n }\n\n if (revealAt === \"center\") {\n return (\n <div className={cn(\"relative shrink-0\", className)} {...props}>\n <div\n ref={sentinelRef}\n className={cn(\n sentinelClass,\n \"absolute left-0 right-0 top-1/2 z-0 -translate-y-1/2\",\n )}\n aria-hidden\n />\n {children}\n </div>\n );\n }\n\n return (\n <div className={cn(\"shrink-0\", className)} {...props}>\n <div ref={sentinelRef} className={sentinelClass} aria-hidden />\n {children}\n </div>\n );\n};\n\nScrollHeaderTop.displayName = \"ScrollHeaderTop\";\n\nconst stickyChromeBgClass =\n \"bg-background supports-backdrop-filter:bg-background\";\n\nconst stickyChromeBorderClass = \"border-primary/10 border-b\";\n\nconst stickyChromeClass = cn(stickyChromeBgClass, stickyChromeBorderClass);\n\nconst revealEase =\n \"duration-300 ease-[cubic-bezier(0.22,1,0.36,1)] motion-reduce:duration-0 motion-reduce:transition-none\";\n\nconst ScrollHeaderSticky: React.FC<React.HTMLAttributes<HTMLDivElement>> = ({\n children,\n className,\n ...props\n}) => {\n const { slideIn } = useContext(ScrollHeaderContext);\n\n return (\n <div\n className={cn(\n \"sticky top-0 z-40\",\n slideIn ? \"h-0 overflow-visible\" : \"shrink-0\",\n )}\n >\n {slideIn ? (\n <div\n className={cn(\n stickyChromeClass,\n \"transition-[opacity, transform]\",\n revealEase,\n '[.scroll-header:not([data-scrolled=\"true\"])_&]:pointer-events-none [.scroll-header:not([data-scrolled=\"true\"])_&]:-translate-y-1 [.scroll-header:not([data-scrolled=\"true\"])_&]:opacity-0',\n '[.scroll-header[data-scrolled=\"true\"]_&]:translate-y-0 [.scroll-header[data-scrolled=\"true\"]_&]:opacity-100',\n className,\n )}\n {...props}\n >\n {children}\n </div>\n ) : (\n <div className={stickyChromeBgClass}>\n <div\n className={cn(\n stickyChromeBorderClass,\n \"transition-[border-color] duration-300 motion-reduce:duration-0 motion-reduce:transition-none\",\n '[.scroll-header:not([data-scrolled=\"true\"])_&]:border-b-transparent',\n className,\n )}\n {...props}\n >\n {children}\n </div>\n </div>\n )}\n </div>\n );\n};\n\nScrollHeaderSticky.displayName = \"ScrollHeaderSticky\";\n\nexport { ScrollHeader, ScrollHeaderSticky, ScrollHeaderTop };\n"]}
@@ -0,0 +1,45 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import * as React from 'react';
3
+
4
+ declare const scrollWheelItemProps: {
5
+ readonly "data-scroll-wheel-item": "";
6
+ };
7
+ type ScrollWheelOrientation = "vertical" | "horizontal";
8
+ type ScrollWheelProps = {
9
+ /** Bumps padding and recenters when external selection changes. Unmount to tear down timers. */
10
+ recenterKey: string | number;
11
+ /** `vertical` (default): `scrollTop`; `horizontal`: `scrollLeft` and row layout. */
12
+ orientation?: ScrollWheelOrientation;
13
+ /**
14
+ * When true, disables scroll-settle commits and turns off **center spotlight** (proximity
15
+ * transform/opacity on rows still updates while scrolling).
16
+ */
17
+ disabled?: boolean;
18
+ /** Fires with the nearest-to-center index after scroll idle or `scrollend`. */
19
+ onScrollSettle?: (index: number) => void;
20
+ children: React.ReactNode;
21
+ /** Merged onto the outer shell (e.g. `h-full` in a fixed-height host). */
22
+ className?: string;
23
+ /**
24
+ * When non-empty, the nearest row **without** `data-selected` gets `data-scroll-wheel-center`,
25
+ * your Tailwind tokens on `classList` when possible, and a **primary @ 40% border-color** via
26
+ * **inline style** (so React `className` commits do not remove the ring). Skipping the selected
27
+ * row avoids a duplicate ring when it sits in the center. For a short interval after `recenterKey`
28
+ * changes, spotlight is hidden while optical center still lags the new selection (post-click flash
29
+ * on the previous row). Whitespace-only turns spotlight off. Has no effect while `disabled` is
30
+ * true.
31
+ */
32
+ centerSpotlightClassName?: string;
33
+ };
34
+ /**
35
+ * Scroll list: value = item nearest the viewport center. End padding, proximity, `scrollend` or
36
+ * idle settle, touch + pen gesture suppression (iOS-safe touch ids), programmatic recenter.
37
+ */
38
+ declare function ScrollWheelRoot({ recenterKey, orientation, disabled, onScrollSettle, children, className, centerSpotlightClassName, }: ScrollWheelProps): react_jsx_runtime.JSX.Element;
39
+ declare const ScrollWheelMemo: React.MemoExoticComponent<typeof ScrollWheelRoot>;
40
+ /** Memoized wheel — spread `ScrollWheel.itemProps` on each row. */
41
+ declare const ScrollWheel: typeof ScrollWheelMemo & {
42
+ readonly itemProps: typeof scrollWheelItemProps;
43
+ };
44
+
45
+ export { ScrollWheel, type ScrollWheelOrientation, type ScrollWheelProps };
@@ -0,0 +1,557 @@
1
+ import * as React from 'react';
2
+ import { clsx } from 'clsx';
3
+ import { twMerge } from 'tailwind-merge';
4
+ import { jsx, jsxs } from 'react/jsx-runtime';
5
+
6
+ function cn(...inputs) {
7
+ return twMerge(clsx(inputs));
8
+ }
9
+
10
+ // src/react/scroll-wheel-axis.ts
11
+ var MAIN_AXIS_MIN_PX = 8;
12
+ function viewportCenter(horizontal, cr) {
13
+ return horizontal ? cr.left + cr.width / 2 : cr.top + cr.height / 2;
14
+ }
15
+ function itemCenter(horizontal, r) {
16
+ return horizontal ? r.left + r.width / 2 : r.top + r.height / 2;
17
+ }
18
+ function mainClientSize(horizontal, el) {
19
+ return horizontal ? el.clientWidth : el.clientHeight;
20
+ }
21
+ function mainViewportRect(horizontal, cr) {
22
+ return horizontal ? cr.width : cr.height;
23
+ }
24
+ function halfMainViewport(horizontal, cr) {
25
+ return Math.max(mainViewportRect(horizontal, cr) / 2, 1);
26
+ }
27
+ function readScroll(horizontal, root) {
28
+ return horizontal ? root.scrollLeft : root.scrollTop;
29
+ }
30
+ function addScroll(horizontal, root, delta) {
31
+ if (horizontal) root.scrollLeft += delta;
32
+ else root.scrollTop += delta;
33
+ }
34
+ function maxScroll(horizontal, root) {
35
+ return horizontal ? Math.max(0, root.scrollWidth - root.clientWidth) : Math.max(0, root.scrollHeight - root.clientHeight);
36
+ }
37
+ function setScroll(horizontal, root, next, behavior) {
38
+ const max = maxScroll(horizontal, root);
39
+ const clamped = Math.min(Math.max(0, next), max);
40
+ if (behavior === "smooth") {
41
+ if (horizontal) root.scrollTo({ left: clamped, behavior: "smooth" });
42
+ else root.scrollTo({ top: clamped, behavior: "smooth" });
43
+ } else if (horizontal) {
44
+ root.scrollLeft = clamped;
45
+ } else {
46
+ root.scrollTop = clamped;
47
+ }
48
+ }
49
+ function applyEndPadding(horizontal, list, root, slotRect) {
50
+ if (horizontal) {
51
+ const pad = Math.max(0, (root.clientWidth - slotRect.width) / 2);
52
+ list.style.paddingLeft = `${pad}px`;
53
+ list.style.paddingRight = `${pad}px`;
54
+ list.style.paddingTop = "";
55
+ list.style.paddingBottom = "";
56
+ } else {
57
+ const pad = Math.max(0, (root.clientHeight - slotRect.height) / 2);
58
+ list.style.paddingTop = `${pad}px`;
59
+ list.style.paddingBottom = `${pad}px`;
60
+ list.style.paddingLeft = "";
61
+ list.style.paddingRight = "";
62
+ }
63
+ }
64
+ var SCROLL_WHEEL_ITEM_ATTR = "data-scroll-wheel-item";
65
+ var scrollWheelItemProps = { [SCROLL_WHEEL_ITEM_ATTR]: "" };
66
+ var WHEEL_SETTLE_SUPPRESS_MS = 48;
67
+ var WHEEL_SMOOTH_SCROLL_SUPPRESS_MS = 520;
68
+ var WHEEL_SETTLE_IDLE_MS = 200;
69
+ var WHEEL_SETTLE_SCROLLEND_BACKUP_MS = 420;
70
+ var SPOTLIGHT_CENTER_MISMATCH_SUPPRESS_MS = 280;
71
+ var SCROLL_END_SUPPORTED = typeof HTMLElement !== "undefined" && "onscrollend" in HTMLElement.prototype;
72
+ var itemSelector = `[${SCROLL_WHEEL_ITEM_ATTR}]`;
73
+ var selectedItemSelector = `[${SCROLL_WHEEL_ITEM_ATTR}][data-selected]`;
74
+ var CLAMP_EPS = 1;
75
+ var CLAMP_ITERS = 8;
76
+ function applySpotlightFrameStyles(el) {
77
+ el.style.setProperty("position", "relative");
78
+ el.style.setProperty("z-index", "10");
79
+ el.style.setProperty(
80
+ "border-color",
81
+ "color-mix(in oklab, var(--primary) 40%, transparent)"
82
+ );
83
+ }
84
+ function stripSpotlightFrameStyles(el) {
85
+ el.style.removeProperty("position");
86
+ el.style.removeProperty("z-index");
87
+ el.style.removeProperty("border-color");
88
+ }
89
+ var FADE_EDGES = {
90
+ vertical: [
91
+ "pointer-events-none absolute inset-x-0 top-0 z-10 h-16 bg-linear-to-b from-background via-background/85 to-transparent",
92
+ "pointer-events-none absolute inset-x-0 bottom-0 z-10 h-16 bg-linear-to-t from-background via-background/85 to-transparent"
93
+ ],
94
+ horizontal: [
95
+ "pointer-events-none absolute inset-y-0 left-0 z-10 w-16 bg-linear-to-r from-background via-background/85 to-transparent",
96
+ "pointer-events-none absolute inset-y-0 right-0 z-10 w-16 bg-linear-to-l from-background via-background/85 to-transparent"
97
+ ]
98
+ };
99
+ function ScrollWheelRoot({
100
+ recenterKey,
101
+ orientation = "vertical",
102
+ disabled,
103
+ onScrollSettle,
104
+ children,
105
+ className,
106
+ centerSpotlightClassName
107
+ }) {
108
+ const horizontal = orientation === "horizontal";
109
+ const centerSpotlightTokens = React.useMemo(
110
+ () => centerSpotlightClassName?.trim().split(/\s+/).filter(Boolean) ?? [],
111
+ [centerSpotlightClassName]
112
+ );
113
+ const centerSpotlightActive = centerSpotlightTokens.length > 0 && disabled !== true;
114
+ const centerSpotlightFingerprint = centerSpotlightClassName?.trim() ?? "";
115
+ const rootRef = React.useRef(null);
116
+ const listRef = React.useRef(null);
117
+ const rafProximityRef = React.useRef(0);
118
+ const settleTimerRef = React.useRef(null);
119
+ const lastSettledIdxRef = React.useRef(null);
120
+ const suppressSettleUntilRef = React.useRef(0);
121
+ const wasPopoverOpenRef = React.useRef(false);
122
+ const prevRecenterKeyRef = React.useRef(void 0);
123
+ const penPointerDownRef = React.useRef(false);
124
+ const touchIdsOnRootRef = React.useRef(/* @__PURE__ */ new Set());
125
+ const spotlightDomRef = React.useRef(null);
126
+ const spotlightAddedClassesRef = React.useRef(
127
+ /* @__PURE__ */ new WeakMap()
128
+ );
129
+ const spotlightFingerprintRef = React.useRef("");
130
+ const spotlightSuppressCenterMismatchUntilRef = React.useRef(0);
131
+ const stripSpotlightFromEl = React.useCallback((el) => {
132
+ if (!el) return;
133
+ const added = spotlightAddedClassesRef.current.get(el);
134
+ if (added) {
135
+ added.forEach((token) => el.classList.remove(token));
136
+ spotlightAddedClassesRef.current.delete(el);
137
+ }
138
+ el.removeAttribute("data-scroll-wheel-center");
139
+ stripSpotlightFrameStyles(el);
140
+ }, []);
141
+ const moveSpotlightDom = React.useCallback(
142
+ (next, tokens) => {
143
+ const prev = spotlightDomRef.current;
144
+ if (tokens.length === 0) {
145
+ if (prev) stripSpotlightFromEl(prev);
146
+ spotlightDomRef.current = null;
147
+ spotlightFingerprintRef.current = "";
148
+ return;
149
+ }
150
+ if (!next) return;
151
+ if (prev && prev !== next) stripSpotlightFromEl(prev);
152
+ stripSpotlightFromEl(next);
153
+ const newlyAdded = /* @__PURE__ */ new Set();
154
+ for (const token of tokens) {
155
+ if (!next.classList.contains(token)) {
156
+ next.classList.add(token);
157
+ newlyAdded.add(token);
158
+ }
159
+ }
160
+ if (newlyAdded.size > 0) {
161
+ spotlightAddedClassesRef.current.set(next, newlyAdded);
162
+ }
163
+ next.setAttribute("data-scroll-wheel-center", "");
164
+ applySpotlightFrameStyles(next);
165
+ spotlightDomRef.current = next;
166
+ },
167
+ [stripSpotlightFromEl]
168
+ );
169
+ const suppressingGesture = () => touchIdsOnRootRef.current.size > 0 || penPointerDownRef.current;
170
+ const runScrollCommitRef = React.useRef(() => {
171
+ });
172
+ const tryCommitAfterGestureRef = React.useRef(() => {
173
+ });
174
+ const clearSettleTimerRef = React.useRef(() => {
175
+ });
176
+ const clearSettleTimer = React.useCallback(() => {
177
+ if (settleTimerRef.current != null) {
178
+ clearTimeout(settleTimerRef.current);
179
+ settleTimerRef.current = null;
180
+ }
181
+ }, []);
182
+ clearSettleTimerRef.current = clearSettleTimer;
183
+ const applyProximityStyles = React.useCallback(() => {
184
+ const root = rootRef.current;
185
+ if (!root) return;
186
+ const slots = root.querySelectorAll(itemSelector);
187
+ if (slots.length === 0) return;
188
+ const cr = root.getBoundingClientRect();
189
+ const mid = viewportCenter(horizontal, cr);
190
+ const half = halfMainViewport(horizontal, cr);
191
+ const nodes = [...slots];
192
+ const dists = nodes.map((node) => {
193
+ const r = node.getBoundingClientRect();
194
+ return Math.abs(itemCenter(horizontal, r) - mid);
195
+ });
196
+ let bestI = 0;
197
+ let bestD = Number.POSITIVE_INFINITY;
198
+ nodes.forEach((node, i) => {
199
+ const dist = dists[i];
200
+ const t = Math.min(1, dist / half);
201
+ node.style.transform = `scale(${1 - 0.42 * t})`;
202
+ node.style.opacity = String(1 - 0.52 * t);
203
+ if (dist < bestD) {
204
+ bestD = dist;
205
+ bestI = i;
206
+ }
207
+ });
208
+ if (!centerSpotlightActive) {
209
+ if (spotlightDomRef.current !== null || spotlightFingerprintRef.current !== "") {
210
+ moveSpotlightDom(null, []);
211
+ }
212
+ return;
213
+ }
214
+ const selectedIdx = nodes.findIndex((n) => n.hasAttribute("data-selected"));
215
+ const el = nodes[bestI];
216
+ if (selectedIdx >= 0 && bestI !== selectedIdx && Date.now() < spotlightSuppressCenterMismatchUntilRef.current) {
217
+ if (spotlightDomRef.current !== null || spotlightFingerprintRef.current !== "") {
218
+ moveSpotlightDom(null, []);
219
+ }
220
+ spotlightFingerprintRef.current = "";
221
+ return;
222
+ }
223
+ if (el.hasAttribute("data-selected")) {
224
+ if (spotlightDomRef.current !== null || spotlightFingerprintRef.current !== "") {
225
+ moveSpotlightDom(null, []);
226
+ }
227
+ spotlightFingerprintRef.current = "";
228
+ return;
229
+ }
230
+ const fp = centerSpotlightFingerprint;
231
+ if (el === spotlightDomRef.current && fp === spotlightFingerprintRef.current) {
232
+ applySpotlightFrameStyles(el);
233
+ return;
234
+ }
235
+ moveSpotlightDom(el, centerSpotlightTokens);
236
+ spotlightFingerprintRef.current = fp;
237
+ }, [
238
+ horizontal,
239
+ centerSpotlightActive,
240
+ centerSpotlightTokens,
241
+ centerSpotlightFingerprint,
242
+ moveSpotlightDom
243
+ ]);
244
+ const queueProximity = React.useCallback(() => {
245
+ cancelAnimationFrame(rafProximityRef.current);
246
+ rafProximityRef.current = requestAnimationFrame(applyProximityStyles);
247
+ }, [applyProximityStyles]);
248
+ React.useLayoutEffect(() => {
249
+ if (disabled === true) {
250
+ moveSpotlightDom(null, []);
251
+ spotlightFingerprintRef.current = "";
252
+ return;
253
+ }
254
+ if (centerSpotlightActive) {
255
+ queueProximity();
256
+ }
257
+ }, [disabled, centerSpotlightActive, moveSpotlightDom, queueProximity]);
258
+ const applyWheelPadding = React.useCallback(() => {
259
+ const list = listRef.current;
260
+ const root = rootRef.current;
261
+ if (!list || !root) return;
262
+ const slot = root.querySelector(itemSelector);
263
+ if (!slot) return;
264
+ applyEndPadding(horizontal, list, root, slot.getBoundingClientRect());
265
+ }, [horizontal]);
266
+ const findBestIndex = React.useCallback(() => {
267
+ const root = rootRef.current;
268
+ if (!root) return -1;
269
+ if (mainClientSize(horizontal, root) < MAIN_AXIS_MIN_PX) return -1;
270
+ const slots = [...root.querySelectorAll(itemSelector)];
271
+ if (slots.length === 0) return -1;
272
+ const cr = root.getBoundingClientRect();
273
+ if (mainViewportRect(horizontal, cr) < MAIN_AXIS_MIN_PX) return -1;
274
+ const mid = viewportCenter(horizontal, cr);
275
+ let best = 0;
276
+ let bestD = Number.POSITIVE_INFINITY;
277
+ slots.forEach((node, i) => {
278
+ const d = Math.abs(itemCenter(horizontal, node.getBoundingClientRect()) - mid);
279
+ if (d < bestD) {
280
+ bestD = d;
281
+ best = i;
282
+ }
283
+ });
284
+ return best;
285
+ }, [horizontal]);
286
+ const clampWheelScroll = React.useCallback(() => {
287
+ const root = rootRef.current;
288
+ if (!root || mainClientSize(horizontal, root) < MAIN_AXIS_MIN_PX) return;
289
+ const slots = [...root.querySelectorAll(itemSelector)];
290
+ if (slots.length < 2) return;
291
+ for (let i = 0; i < CLAMP_ITERS; i += 1) {
292
+ const rootRect = root.getBoundingClientRect();
293
+ const mid = viewportCenter(horizontal, rootRect);
294
+ const fr = slots[0].getBoundingClientRect();
295
+ const lr = slots[slots.length - 1].getBoundingClientRect();
296
+ const firstMid = itemCenter(horizontal, fr);
297
+ const lastMid = itemCenter(horizontal, lr);
298
+ if (firstMid > mid + CLAMP_EPS) {
299
+ suppressSettleUntilRef.current = Date.now() + WHEEL_SETTLE_SUPPRESS_MS;
300
+ addScroll(horizontal, root, firstMid - mid);
301
+ continue;
302
+ }
303
+ if (lastMid < mid - CLAMP_EPS) {
304
+ suppressSettleUntilRef.current = Date.now() + WHEEL_SETTLE_SUPPRESS_MS;
305
+ addScroll(horizontal, root, lastMid - mid);
306
+ continue;
307
+ }
308
+ break;
309
+ }
310
+ }, [horizontal]);
311
+ const runScrollCommit = React.useCallback(() => {
312
+ if (suppressingGesture() || !onScrollSettle || disabled) return;
313
+ if (Date.now() < suppressSettleUntilRef.current) return;
314
+ clampWheelScroll();
315
+ const idx = findBestIndex();
316
+ if (idx < 0) return;
317
+ if (idx !== lastSettledIdxRef.current) {
318
+ lastSettledIdxRef.current = idx;
319
+ onScrollSettle(idx);
320
+ }
321
+ queueProximity();
322
+ }, [onScrollSettle, disabled, findBestIndex, queueProximity, clampWheelScroll]);
323
+ runScrollCommitRef.current = runScrollCommit;
324
+ const scrollSelectedToCenter = React.useCallback(
325
+ (scrollBehavior = "auto") => {
326
+ const root = rootRef.current;
327
+ if (!root) return;
328
+ const selected = root.querySelector(selectedItemSelector);
329
+ lastSettledIdxRef.current = null;
330
+ suppressSettleUntilRef.current = Date.now() + (scrollBehavior === "smooth" ? WHEEL_SMOOTH_SCROLL_SUPPRESS_MS : WHEEL_SETTLE_SUPPRESS_MS);
331
+ if (selected) {
332
+ const rootRect = root.getBoundingClientRect();
333
+ const sr = selected.getBoundingClientRect();
334
+ const delta = itemCenter(horizontal, sr) - viewportCenter(horizontal, rootRect);
335
+ setScroll(
336
+ horizontal,
337
+ root,
338
+ readScroll(horizontal, root) + delta,
339
+ scrollBehavior
340
+ );
341
+ }
342
+ queueProximity();
343
+ },
344
+ [horizontal, queueProximity]
345
+ );
346
+ const scheduleIdleSettle = React.useCallback(() => {
347
+ if (!onScrollSettle || disabled || suppressingGesture()) return;
348
+ clearSettleTimer();
349
+ settleTimerRef.current = setTimeout(
350
+ runScrollCommit,
351
+ SCROLL_END_SUPPORTED ? WHEEL_SETTLE_SCROLLEND_BACKUP_MS : WHEEL_SETTLE_IDLE_MS
352
+ );
353
+ }, [onScrollSettle, disabled, clearSettleTimer, runScrollCommit]);
354
+ const tryCommitAfterGestureRelease = React.useCallback(() => {
355
+ if (suppressingGesture()) return;
356
+ const root = rootRef.current;
357
+ if (!root) return;
358
+ const s0 = readScroll(horizontal, root);
359
+ requestAnimationFrame(() => {
360
+ requestAnimationFrame(() => {
361
+ if (suppressingGesture()) return;
362
+ const r = rootRef.current;
363
+ if (!r) return;
364
+ if (readScroll(horizontal, r) !== s0) {
365
+ queueProximity();
366
+ scheduleIdleSettle();
367
+ return;
368
+ }
369
+ runScrollCommit();
370
+ });
371
+ });
372
+ }, [horizontal, runScrollCommit, scheduleIdleSettle, queueProximity]);
373
+ tryCommitAfterGestureRef.current = tryCommitAfterGestureRelease;
374
+ const onPointerDownCapture = React.useCallback(
375
+ (e) => {
376
+ if (!onScrollSettle || disabled || e.pointerType !== "pen") return;
377
+ penPointerDownRef.current = true;
378
+ clearSettleTimer();
379
+ },
380
+ [onScrollSettle, disabled, clearSettleTimer]
381
+ );
382
+ const onTouchStartCapture = React.useCallback(
383
+ (e) => {
384
+ if (!onScrollSettle || disabled) return;
385
+ for (let i = 0; i < e.changedTouches.length; i += 1) {
386
+ touchIdsOnRootRef.current.add(e.changedTouches[i].identifier);
387
+ }
388
+ clearSettleTimer();
389
+ },
390
+ [onScrollSettle, disabled, clearSettleTimer]
391
+ );
392
+ const onScroll = React.useCallback(() => {
393
+ queueProximity();
394
+ if (!onScrollSettle || disabled) return;
395
+ if (suppressingGesture()) clearSettleTimer();
396
+ else scheduleIdleSettle();
397
+ }, [queueProximity, onScrollSettle, disabled, clearSettleTimer, scheduleIdleSettle]);
398
+ React.useEffect(() => {
399
+ if (disabled || !onScrollSettle) return;
400
+ const onPenEnd = (e) => {
401
+ if (e.pointerType !== "pen" || !penPointerDownRef.current) return;
402
+ penPointerDownRef.current = false;
403
+ tryCommitAfterGestureRef.current();
404
+ };
405
+ const onTouchEnd = (e) => {
406
+ for (let i = 0; i < e.changedTouches.length; i += 1) {
407
+ touchIdsOnRootRef.current.delete(e.changedTouches[i].identifier);
408
+ }
409
+ if (touchIdsOnRootRef.current.size === 0) tryCommitAfterGestureRef.current();
410
+ };
411
+ window.addEventListener("pointerup", onPenEnd, true);
412
+ window.addEventListener("pointercancel", onPenEnd, true);
413
+ window.addEventListener("touchend", onTouchEnd, true);
414
+ window.addEventListener("touchcancel", onTouchEnd, true);
415
+ return () => {
416
+ window.removeEventListener("pointerup", onPenEnd, true);
417
+ window.removeEventListener("pointercancel", onPenEnd, true);
418
+ window.removeEventListener("touchend", onTouchEnd, true);
419
+ window.removeEventListener("touchcancel", onTouchEnd, true);
420
+ };
421
+ }, [disabled, onScrollSettle]);
422
+ React.useLayoutEffect(() => {
423
+ let followRaf1 = 0;
424
+ let followRaf2 = 0;
425
+ let ro;
426
+ const root = rootRef.current;
427
+ if (!root) return;
428
+ const onResize = () => {
429
+ applyWheelPadding();
430
+ queueProximity();
431
+ };
432
+ onResize();
433
+ const prev = prevRecenterKeyRef.current;
434
+ const firstOpen = !wasPopoverOpenRef.current;
435
+ const selectionChanged = wasPopoverOpenRef.current && prev !== void 0 && prev !== recenterKey;
436
+ if (firstOpen || selectionChanged) {
437
+ if (selectionChanged) {
438
+ spotlightSuppressCenterMismatchUntilRef.current = Date.now() + SPOTLIGHT_CENTER_MISMATCH_SUPPRESS_MS;
439
+ }
440
+ const reducedMotion = typeof window !== "undefined" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
441
+ const behavior = selectionChanged && !reducedMotion ? "smooth" : "auto";
442
+ scrollSelectedToCenter(behavior);
443
+ if (firstOpen) {
444
+ followRaf1 = requestAnimationFrame(() => {
445
+ followRaf2 = requestAnimationFrame(() => {
446
+ scrollSelectedToCenter("auto");
447
+ });
448
+ });
449
+ }
450
+ }
451
+ prevRecenterKeyRef.current = recenterKey;
452
+ wasPopoverOpenRef.current = true;
453
+ if (typeof ResizeObserver !== "undefined") {
454
+ ro = new ResizeObserver(onResize);
455
+ ro.observe(root);
456
+ }
457
+ return () => {
458
+ ro?.disconnect();
459
+ cancelAnimationFrame(followRaf1);
460
+ cancelAnimationFrame(followRaf2);
461
+ };
462
+ }, [
463
+ recenterKey,
464
+ orientation,
465
+ centerSpotlightClassName,
466
+ applyWheelPadding,
467
+ scrollSelectedToCenter,
468
+ queueProximity
469
+ ]);
470
+ React.useLayoutEffect(() => {
471
+ return () => {
472
+ penPointerDownRef.current = false;
473
+ touchIdsOnRootRef.current.clear();
474
+ wasPopoverOpenRef.current = false;
475
+ prevRecenterKeyRef.current = void 0;
476
+ lastSettledIdxRef.current = null;
477
+ spotlightSuppressCenterMismatchUntilRef.current = 0;
478
+ moveSpotlightDom(null, []);
479
+ clearSettleTimer();
480
+ cancelAnimationFrame(rafProximityRef.current);
481
+ };
482
+ }, []);
483
+ React.useLayoutEffect(() => {
484
+ if (!onScrollSettle || disabled || !SCROLL_END_SUPPORTED) return;
485
+ const root = rootRef.current;
486
+ if (!root) return;
487
+ const onScrollEnd = () => {
488
+ if (suppressingGesture()) return;
489
+ clearSettleTimerRef.current();
490
+ runScrollCommitRef.current();
491
+ };
492
+ root.addEventListener("scrollend", onScrollEnd);
493
+ return () => root.removeEventListener("scrollend", onScrollEnd);
494
+ }, [disabled, onScrollSettle]);
495
+ React.useLayoutEffect(() => {
496
+ if (!centerSpotlightActive) return;
497
+ queueProximity();
498
+ }, [centerSpotlightActive, children, queueProximity]);
499
+ const fadeClasses = FADE_EDGES[orientation];
500
+ return /* @__PURE__ */ jsx(
501
+ "div",
502
+ {
503
+ className: cn(
504
+ "relative z-2 flex h-full min-h-0 min-w-0 flex-1 basis-0 overflow-hidden",
505
+ horizontal ? "flex-row" : "flex-col",
506
+ className
507
+ ),
508
+ "data-scroll-wheel-orientation": orientation,
509
+ children: /* @__PURE__ */ jsxs(
510
+ "div",
511
+ {
512
+ className: cn(
513
+ "relative isolate flex min-h-0 min-w-0 flex-1",
514
+ horizontal ? "flex-row" : "flex-col"
515
+ ),
516
+ children: [
517
+ /* @__PURE__ */ jsx(
518
+ "div",
519
+ {
520
+ ref: rootRef,
521
+ className: cn(
522
+ "relative z-0 min-h-0 min-w-0 flex-1 [-ms-overflow-style:none] [scrollbar-width:none] [-webkit-overflow-scrolling:touch] [&::-webkit-scrollbar]:hidden",
523
+ horizontal ? "overflow-x-auto overflow-y-hidden overscroll-x-contain" : "overflow-y-auto overflow-x-hidden overscroll-y-contain"
524
+ ),
525
+ onPointerDownCapture,
526
+ onTouchStartCapture,
527
+ onScroll,
528
+ children: /* @__PURE__ */ jsx(
529
+ "div",
530
+ {
531
+ ref: listRef,
532
+ className: cn(
533
+ "flex gap-0.5",
534
+ horizontal ? "min-w-min flex-row flex-nowrap items-center gap-0.5 py-0 pl-2 pr-2 [&>[data-scroll-wheel-item]]:shrink-0" : "min-h-0 flex-col px-2 pb-2"
535
+ ),
536
+ children
537
+ }
538
+ )
539
+ }
540
+ ),
541
+ fadeClasses.map((cls, i) => /* @__PURE__ */ jsx("div", { className: cls, "aria-hidden": true }, i))
542
+ ]
543
+ }
544
+ )
545
+ }
546
+ );
547
+ }
548
+ var ScrollWheelMemo = React.memo(ScrollWheelRoot);
549
+ ScrollWheelMemo.displayName = "ScrollWheel";
550
+ var ScrollWheel = Object.assign(ScrollWheelMemo, {
551
+ /** Marker props for each wheel row — spread onto the focusable control. */
552
+ itemProps: scrollWheelItemProps
553
+ });
554
+
555
+ export { ScrollWheel };
556
+ //# sourceMappingURL=scroll-wheel.js.map
557
+ //# sourceMappingURL=scroll-wheel.js.map