@bearmenu/ui 0.8.6 → 0.8.8

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 (88) hide show
  1. package/dist/{chunk-637L7XQ3.js → chunk-3TNPSN4D.js} +28 -9
  2. package/dist/chunk-AHL45DD2.js +145 -0
  3. package/dist/chunk-IYDH5IU6.js +66 -0
  4. package/dist/chunk-OPVKL4VS.js +18 -0
  5. package/dist/components/alert.d.ts +2 -2
  6. package/dist/components/badge.d.ts +1 -1
  7. package/dist/components/button.d.ts +1 -1
  8. package/dist/components/card.d.ts +2 -2
  9. package/dist/components/category-deck.js +10 -1
  10. package/dist/components/command.d.ts +15 -15
  11. package/dist/components/coverflow.d.ts +11 -3
  12. package/dist/components/coverflow.js +34 -51
  13. package/dist/components/drift-rail.d.ts +24 -10
  14. package/dist/components/drift-rail.js +1 -1
  15. package/dist/components/event-card.d.ts +8 -0
  16. package/dist/components/event-card.js +68 -35
  17. package/dist/components/event-compact-row.d.ts +34 -6
  18. package/dist/components/event-compact-row.js +120 -38
  19. package/dist/components/event-day-header.js +3 -1
  20. package/dist/components/event-day-separator.d.ts +7 -1
  21. package/dist/components/event-day-separator.js +3 -2
  22. package/dist/components/event-hub-hero.d.ts +25 -4
  23. package/dist/components/event-hub-hero.js +89 -19
  24. package/dist/components/event-list-row.d.ts +19 -6
  25. package/dist/components/event-list-row.js +78 -34
  26. package/dist/components/event-types.d.ts +5 -1
  27. package/dist/components/hour-band.d.ts +38 -40
  28. package/dist/components/hour-band.js +55 -27
  29. package/dist/components/hour-group.js +1 -1
  30. package/dist/components/item.d.ts +2 -2
  31. package/dist/components/poster-tape.js +1 -1
  32. package/dist/components/poster-wall.d.ts +3 -3
  33. package/dist/components/poster-wall.js +73 -17
  34. package/dist/components/section-header.d.ts +16 -1
  35. package/dist/components/section-header.js +26 -7
  36. package/dist/components/snap-rail.d.ts +34 -0
  37. package/dist/components/snap-rail.js +51 -0
  38. package/dist/components/spinner.d.ts +2 -2
  39. package/dist/components/tag.d.ts +2 -2
  40. package/dist/components/text.d.ts +3 -3
  41. package/dist/components/timeline-band.d.ts +20 -2
  42. package/dist/components/timeline-band.js +1 -1
  43. package/dist/components/timeline-playhead.d.ts +26 -0
  44. package/dist/components/timeline-playhead.js +25 -0
  45. package/dist/components/weekend-panel.d.ts +5 -4
  46. package/dist/components/weekend-panel.js +85 -71
  47. package/dist/hooks/use-drag-swipe.d.ts +44 -0
  48. package/dist/hooks/use-drag-swipe.js +2 -0
  49. package/dist/hooks/use-media-query.d.ts +17 -0
  50. package/dist/hooks/use-media-query.js +2 -0
  51. package/package.json +1 -1
  52. package/src/components/category-deck.tsx +19 -8
  53. package/src/components/coverflow.test.tsx +28 -1
  54. package/src/components/coverflow.tsx +37 -43
  55. package/src/components/drift-rail.tsx +167 -42
  56. package/src/components/event-card.test.tsx +11 -0
  57. package/src/components/event-card.tsx +67 -14
  58. package/src/components/event-compact-row.stories.tsx +30 -0
  59. package/src/components/event-compact-row.test.tsx +100 -0
  60. package/src/components/event-compact-row.tsx +166 -40
  61. package/src/components/event-day-header.tsx +3 -1
  62. package/src/components/event-day-separator.tsx +8 -1
  63. package/src/components/event-hub-hero.stories.tsx +30 -0
  64. package/src/components/event-hub-hero.tsx +102 -30
  65. package/src/components/event-list-row.stories.tsx +34 -0
  66. package/src/components/event-list-row.test.tsx +53 -0
  67. package/src/components/event-list-row.tsx +135 -87
  68. package/src/components/event-types.ts +5 -1
  69. package/src/components/hour-band.stories.tsx +28 -5
  70. package/src/components/hour-band.tsx +90 -70
  71. package/src/components/poster-wall.test.tsx +28 -0
  72. package/src/components/poster-wall.tsx +96 -23
  73. package/src/components/section-header.stories.tsx +24 -0
  74. package/src/components/section-header.test.tsx +28 -0
  75. package/src/components/section-header.tsx +45 -7
  76. package/src/components/snap-rail.stories.tsx +79 -0
  77. package/src/components/snap-rail.test.tsx +18 -0
  78. package/src/components/snap-rail.tsx +67 -0
  79. package/src/components/timeline-band.tsx +52 -12
  80. package/src/components/timeline-playhead.stories.tsx +30 -0
  81. package/src/components/timeline-playhead.test.tsx +11 -0
  82. package/src/components/timeline-playhead.tsx +46 -0
  83. package/src/components/weekend-panel.test.tsx +43 -0
  84. package/src/components/weekend-panel.tsx +142 -108
  85. package/src/hooks/use-drag-swipe.test.tsx +66 -0
  86. package/src/hooks/use-drag-swipe.ts +105 -0
  87. package/src/hooks/use-media-query.ts +31 -0
  88. package/dist/chunk-GPH6L4DX.js +0 -55
@@ -1,10 +1,19 @@
1
1
  import { Separator } from './chunk-WSRWFA6K.js';
2
2
  import { Skeleton } from './chunk-QXHMPJ35.js';
3
3
  import { cn } from './chunk-FEK5XGZW.js';
4
+ import { Radio } from 'lucide-react';
4
5
  import { jsxs, jsx } from 'react/jsx-runtime';
5
6
 
6
7
  var LIVE_PUCK_PULSE_CLASS = "bm-timeline-live-puck-pulse";
7
- function TimelineHourPuck({ time, isLive, liveLabel, className }) {
8
+ var TIMELINE_MOBILE_GUTTER_CLASS = "w-[58px]";
9
+ function TimelineHourPuck({
10
+ time,
11
+ isLive,
12
+ liveLabel,
13
+ variant = "station",
14
+ className
15
+ }) {
16
+ const isChip = variant === "chip";
8
17
  return /* @__PURE__ */ jsxs("span", { className: cn("relative flex justify-center", className), children: [
9
18
  isLive && /* @__PURE__ */ jsx("style", { children: `
10
19
  @keyframes ${LIVE_PUCK_PULSE_CLASS} {
@@ -18,23 +27,33 @@ function TimelineHourPuck({ time, isLive, liveLabel, className }) {
18
27
  .${LIVE_PUCK_PULSE_CLASS} { animation: none; }
19
28
  }
20
29
  ` }),
21
- /* @__PURE__ */ jsx(
30
+ /* @__PURE__ */ jsxs(
22
31
  "span",
23
32
  {
24
33
  className: cn(
25
- "whitespace-nowrap rounded-[7px] border bg-background px-[7px] py-[3px]",
26
- "font-mono text-[12.5px] font-medium tabular-nums",
27
- isLive ? cn("border-success/40 text-success", LIVE_PUCK_PULSE_CLASS) : "border-border text-foreground"
34
+ "whitespace-nowrap font-mono tabular-nums",
35
+ isChip ? cn(
36
+ "inline-flex h-6 min-w-[58px] items-center justify-center gap-1 rounded-[7px] px-1 text-[12px] font-medium",
37
+ isLive ? cn("bg-success/10 text-success", LIVE_PUCK_PULSE_CLASS) : "bg-muted text-foreground"
38
+ ) : cn(
39
+ "rounded-[7px] border bg-background px-[7px] py-[3px] text-[12.5px] font-medium",
40
+ isLive ? cn("border-success/40 text-success", LIVE_PUCK_PULSE_CLASS) : "border-border text-foreground"
41
+ )
28
42
  ),
29
- children: time
43
+ children: [
44
+ isChip && isLive && /* @__PURE__ */ jsx(Radio, { className: "h-2.5 w-2.5 shrink-0", "aria-hidden": true }),
45
+ time
46
+ ]
30
47
  }
31
48
  ),
32
49
  isLive && !!liveLabel && /* @__PURE__ */ jsx(
33
50
  "span",
34
51
  {
35
52
  className: cn(
36
- "absolute top-[calc(100%+6px)] whitespace-nowrap bg-background px-1",
37
- "text-[9.5px] font-extrabold tracking-[0.08em] text-success"
53
+ isChip ? "sr-only" : cn(
54
+ "absolute top-[calc(100%+6px)] whitespace-nowrap bg-background px-1",
55
+ "text-[9.5px] font-extrabold tracking-[0.08em] text-success"
56
+ )
38
57
  ),
39
58
  children: liveLabel
40
59
  }
@@ -82,4 +101,4 @@ function TimelineBandSkeleton({ className }) {
82
101
  ] });
83
102
  }
84
103
 
85
- export { TimelineBand, TimelineBandSkeleton, TimelineHourPuck };
104
+ export { TIMELINE_MOBILE_GUTTER_CLASS, TimelineBand, TimelineBandSkeleton, TimelineHourPuck };
@@ -0,0 +1,145 @@
1
+ import { cn } from './chunk-FEK5XGZW.js';
2
+ import { useRef, useState, useEffect } from 'react';
3
+ import { jsx, jsxs } from 'react/jsx-runtime';
4
+
5
+ var DRIFT_RAIL_DEFAULT_DURATION_SECONDS = 42;
6
+ var DRIFT_RAIL_RESUME_DELAY_MS = 3e3;
7
+ function DriftRail({
8
+ children,
9
+ reverse = false,
10
+ durationSeconds = DRIFT_RAIL_DEFAULT_DURATION_SECONDS,
11
+ pauseOnHover = true,
12
+ className
13
+ }) {
14
+ const scrollerRef = useRef(null);
15
+ const trackRef = useRef(null);
16
+ const [isHovered, setIsHovered] = useState(false);
17
+ const [inView, setInView] = useState(true);
18
+ const [reducedMotion, setReducedMotion] = useState(false);
19
+ const heldUntil = useRef(0);
20
+ const mouseDrag = useRef(null);
21
+ const position = useRef(0);
22
+ useEffect(() => {
23
+ if (typeof window === "undefined" || !window.matchMedia) return;
24
+ const query = window.matchMedia("(prefers-reduced-motion: reduce)");
25
+ setReducedMotion(query.matches);
26
+ const onChange = () => setReducedMotion(query.matches);
27
+ query.addEventListener("change", onChange);
28
+ return () => query.removeEventListener("change", onChange);
29
+ }, []);
30
+ useEffect(() => {
31
+ const node = scrollerRef.current;
32
+ if (!node || typeof IntersectionObserver === "undefined") return void 0;
33
+ const observer = new IntersectionObserver(([entry]) => setInView(entry.isIntersecting), {
34
+ threshold: 0
35
+ });
36
+ observer.observe(node);
37
+ return () => observer.disconnect();
38
+ }, []);
39
+ useEffect(() => {
40
+ const scroller = scrollerRef.current;
41
+ const track = trackRef.current;
42
+ if (!scroller || !track) return void 0;
43
+ let frame = 0;
44
+ let last = performance.now();
45
+ const drifting = !reducedMotion && inView && !(pauseOnHover && isHovered);
46
+ if (reverse && scroller.scrollLeft === 0) scroller.scrollLeft = track.scrollWidth / 2;
47
+ position.current = scroller.scrollLeft;
48
+ const tick = (now) => {
49
+ const half = track.scrollWidth / 2;
50
+ if (half > 0) {
51
+ const driftingNow = drifting && now >= heldUntil.current;
52
+ if (driftingNow) {
53
+ const perSecond = half / durationSeconds;
54
+ const delta = perSecond * (now - last) / 1e3;
55
+ position.current += reverse ? -delta : delta;
56
+ } else {
57
+ position.current = scroller.scrollLeft;
58
+ }
59
+ let wrapped = false;
60
+ if (position.current >= half) {
61
+ position.current -= half;
62
+ wrapped = true;
63
+ } else if (position.current < 0) {
64
+ position.current += half;
65
+ wrapped = true;
66
+ }
67
+ if (driftingNow || wrapped) scroller.scrollLeft = position.current;
68
+ }
69
+ last = now;
70
+ frame = requestAnimationFrame(tick);
71
+ };
72
+ frame = requestAnimationFrame(tick);
73
+ return () => cancelAnimationFrame(frame);
74
+ }, [durationSeconds, inView, isHovered, pauseOnHover, reducedMotion, reverse]);
75
+ const hold = () => {
76
+ heldUntil.current = Number.POSITIVE_INFINITY;
77
+ };
78
+ const release = () => {
79
+ heldUntil.current = performance.now() + DRIFT_RAIL_RESUME_DELAY_MS;
80
+ };
81
+ const onPointerDown = (event) => {
82
+ hold();
83
+ if (event.pointerType !== "mouse" || event.button !== 0 || !scrollerRef.current) return;
84
+ mouseDrag.current = {
85
+ startX: event.clientX,
86
+ startScroll: scrollerRef.current.scrollLeft,
87
+ pointerId: event.pointerId
88
+ };
89
+ };
90
+ const onPointerMove = (event) => {
91
+ var _a, _b, _c, _d;
92
+ const drag = mouseDrag.current;
93
+ if (!drag || event.pointerId !== drag.pointerId || !scrollerRef.current) return;
94
+ const dx = event.clientX - drag.startX;
95
+ if (Math.abs(dx) > 4 && !((_b = (_a = event.currentTarget).hasPointerCapture) == null ? void 0 : _b.call(_a, event.pointerId))) {
96
+ (_d = (_c = event.currentTarget).setPointerCapture) == null ? void 0 : _d.call(_c, event.pointerId);
97
+ event.currentTarget.dataset.dragging = "true";
98
+ }
99
+ scrollerRef.current.scrollLeft = drag.startScroll - dx;
100
+ };
101
+ const onPointerEnd = (event) => {
102
+ var _a, _b;
103
+ release();
104
+ const drag = mouseDrag.current;
105
+ mouseDrag.current = null;
106
+ if (drag && ((_b = (_a = event.currentTarget).hasPointerCapture) == null ? void 0 : _b.call(_a, event.pointerId))) {
107
+ event.currentTarget.releasePointerCapture(event.pointerId);
108
+ }
109
+ delete event.currentTarget.dataset.dragging;
110
+ };
111
+ const onClickCapture = (event) => {
112
+ if (event.currentTarget.dataset.dragging === "true") {
113
+ event.preventDefault();
114
+ event.stopPropagation();
115
+ }
116
+ };
117
+ return /* @__PURE__ */ jsx(
118
+ "div",
119
+ {
120
+ ref: scrollerRef,
121
+ onMouseEnter: () => setIsHovered(true),
122
+ onMouseLeave: () => setIsHovered(false),
123
+ onPointerDown,
124
+ onPointerMove,
125
+ onPointerUp: onPointerEnd,
126
+ onPointerCancel: onPointerEnd,
127
+ onWheel: () => {
128
+ release();
129
+ },
130
+ onDragStart: (event) => event.preventDefault(),
131
+ onClickCapture,
132
+ className: cn(
133
+ "overflow-x-auto overflow-y-hidden py-1.5 scrollbar-hide [touch-action:pan-x]",
134
+ "cursor-grab select-none data-[dragging=true]:cursor-grabbing",
135
+ className
136
+ ),
137
+ children: /* @__PURE__ */ jsxs("div", { ref: trackRef, className: "flex w-max items-stretch gap-3.5", children: [
138
+ children,
139
+ /* @__PURE__ */ jsx("div", { className: "contents", "aria-hidden": "true", children })
140
+ ] })
141
+ }
142
+ );
143
+ }
144
+
145
+ export { DriftRail };
@@ -0,0 +1,66 @@
1
+ import { useState, useRef, useCallback } from 'react';
2
+
3
+ function useDragSwipe({ threshold = 40, onSwipe, disabled }) {
4
+ const [dragX, setDragX] = useState(0);
5
+ const startX = useRef(null);
6
+ const pointerId = useRef(null);
7
+ const didDrag = useRef(false);
8
+ const onPointerDown = useCallback(
9
+ (event) => {
10
+ if (disabled || event.button !== 0) return;
11
+ startX.current = event.clientX;
12
+ pointerId.current = event.pointerId;
13
+ didDrag.current = false;
14
+ },
15
+ [disabled]
16
+ );
17
+ const onPointerMove = useCallback((event) => {
18
+ var _a, _b;
19
+ if (startX.current === null || event.pointerId !== pointerId.current) return;
20
+ const dx = event.clientX - startX.current;
21
+ if (!didDrag.current && Math.abs(dx) > 6) {
22
+ didDrag.current = true;
23
+ (_b = (_a = event.currentTarget).setPointerCapture) == null ? void 0 : _b.call(_a, event.pointerId);
24
+ }
25
+ if (didDrag.current) setDragX(dx);
26
+ }, []);
27
+ const end = useCallback(
28
+ (event, cancelled) => {
29
+ var _a, _b;
30
+ if (startX.current === null || event.pointerId !== pointerId.current) return;
31
+ const dx = event.clientX - startX.current;
32
+ startX.current = null;
33
+ pointerId.current = null;
34
+ if ((_b = (_a = event.currentTarget).hasPointerCapture) == null ? void 0 : _b.call(_a, event.pointerId)) {
35
+ event.currentTarget.releasePointerCapture(event.pointerId);
36
+ }
37
+ setDragX(0);
38
+ if (!cancelled && didDrag.current && Math.abs(dx) >= threshold) {
39
+ onSwipe(dx < 0 ? 1 : -1);
40
+ }
41
+ },
42
+ [onSwipe, threshold]
43
+ );
44
+ const onPointerUp = useCallback((event) => end(event, false), [end]);
45
+ const onPointerCancel = useCallback(
46
+ (event) => end(event, true),
47
+ [end]
48
+ );
49
+ const onDragStart = useCallback((event) => {
50
+ event.preventDefault();
51
+ }, []);
52
+ const onClickCapture = useCallback((event) => {
53
+ if (didDrag.current) {
54
+ event.preventDefault();
55
+ event.stopPropagation();
56
+ didDrag.current = false;
57
+ }
58
+ }, []);
59
+ return {
60
+ dragX,
61
+ isDragging: dragX !== 0,
62
+ handlers: { onPointerDown, onPointerMove, onPointerUp, onPointerCancel, onClickCapture, onDragStart }
63
+ };
64
+ }
65
+
66
+ export { useDragSwipe };
@@ -0,0 +1,18 @@
1
+ import { useSyncExternalStore } from 'react';
2
+
3
+ function useMediaQuery(query) {
4
+ return useSyncExternalStore(
5
+ (onChange) => {
6
+ if (typeof window.matchMedia !== "function") return () => {
7
+ };
8
+ const mql = window.matchMedia(query);
9
+ mql.addEventListener("change", onChange);
10
+ return () => mql.removeEventListener("change", onChange);
11
+ },
12
+ () => typeof window.matchMedia === "function" ? window.matchMedia(query).matches : false,
13
+ () => false
14
+ );
15
+ }
16
+ var DESKTOP_MEDIA_QUERY = "(min-width: 1024px)";
17
+
18
+ export { DESKTOP_MEDIA_QUERY, useMediaQuery };
@@ -3,10 +3,10 @@ import * as React from 'react';
3
3
  import { VariantProps } from 'class-variance-authority';
4
4
 
5
5
  declare const alertVariants: (props?: ({
6
- variant?: "default" | "destructive" | "warning" | "success" | null | undefined;
6
+ variant?: "success" | "warning" | "default" | "destructive" | null | undefined;
7
7
  } & class_variance_authority_types.ClassProp) | undefined) => string;
8
8
  declare const Alert: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLDivElement> & VariantProps<(props?: ({
9
- variant?: "default" | "destructive" | "warning" | "success" | null | undefined;
9
+ variant?: "success" | "warning" | "default" | "destructive" | null | undefined;
10
10
  } & class_variance_authority_types.ClassProp) | undefined) => string> & React.RefAttributes<HTMLDivElement>>;
11
11
  declare const AlertTitle: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLHeadingElement> & React.RefAttributes<HTMLParagraphElement>>;
12
12
  declare const AlertDescription: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLParagraphElement> & React.RefAttributes<HTMLParagraphElement>>;
@@ -4,7 +4,7 @@ import * as React from 'react';
4
4
  import { VariantProps } from 'class-variance-authority';
5
5
 
6
6
  declare const badgeVariants: (props?: ({
7
- variant?: "default" | "destructive" | "warning" | "success" | "outline" | "secondary" | "glass" | "overlay" | "brown" | null | undefined;
7
+ variant?: "success" | "warning" | "default" | "destructive" | "outline" | "secondary" | "glass" | "overlay" | "brown" | null | undefined;
8
8
  live?: boolean | null | undefined;
9
9
  } & class_variance_authority_types.ClassProp) | undefined) => string;
10
10
  interface BadgeProps extends React.HTMLAttributes<HTMLDivElement>, VariantProps<typeof badgeVariants> {
@@ -3,7 +3,7 @@ import * as React from 'react';
3
3
  import { VariantProps } from 'class-variance-authority';
4
4
 
5
5
  declare const buttonVariants: (props?: ({
6
- variant?: "default" | "destructive" | "link" | "outline" | "secondary" | "ghost" | "glass" | "overlay" | "brown" | "antagonist" | null | undefined;
6
+ variant?: "link" | "default" | "destructive" | "outline" | "secondary" | "ghost" | "glass" | "overlay" | "brown" | "antagonist" | null | undefined;
7
7
  size?: "default" | "xs" | "sm" | "lg" | "icon" | "icon-xs" | "icon-sm" | "icon-lg" | null | undefined;
8
8
  fullWidth?: boolean | null | undefined;
9
9
  } & class_variance_authority_types.ClassProp) | undefined) => string;
@@ -3,12 +3,12 @@ import * as React from 'react';
3
3
  import { VariantProps } from 'class-variance-authority';
4
4
 
5
5
  declare const cardVariants: (props?: ({
6
- variant?: "muted" | "outline" | "glass" | "surface" | "elevated" | "elevatedPanel" | null | undefined;
6
+ variant?: "outline" | "glass" | "muted" | "surface" | "elevated" | "elevatedPanel" | null | undefined;
7
7
  size?: "default" | "sm" | "lg" | null | undefined;
8
8
  interactive?: boolean | null | undefined;
9
9
  } & class_variance_authority_types.ClassProp) | undefined) => string;
10
10
  declare const Card: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLDivElement> & VariantProps<(props?: ({
11
- variant?: "muted" | "outline" | "glass" | "surface" | "elevated" | "elevatedPanel" | null | undefined;
11
+ variant?: "outline" | "glass" | "muted" | "surface" | "elevated" | "elevatedPanel" | null | undefined;
12
12
  size?: "default" | "sm" | "lg" | null | undefined;
13
13
  interactive?: boolean | null | undefined;
14
14
  } & class_variance_authority_types.ClassProp) | undefined) => string> & React.RefAttributes<HTMLDivElement>>;
@@ -57,7 +57,10 @@ function CategoryDeck({
57
57
  {
58
58
  className: cn(
59
59
  "absolute left-1/2 top-0 aspect-[4/5] w-24 overflow-hidden rounded-[10px] bg-muted",
60
+ // Keyed to --foreground so the shadow is ink in light mode; in
61
+ // dark mode that same 18% is a light halo, so it drops to 7%.
60
62
  "shadow-[0_10px_26px_color-mix(in_oklch,var(--foreground)_18%,transparent)]",
63
+ "dark:shadow-[0_10px_26px_color-mix(in_oklch,var(--foreground)_7%,transparent)]",
61
64
  "sm:w-[120px]",
62
65
  transform.rest,
63
66
  transform.spread,
@@ -86,7 +89,13 @@ function CategoryDeck({
86
89
  /* @__PURE__ */ jsx("div", { className: "truncate text-[14.5px] font-extrabold tracking-[-0.015em] sm:text-base", children: name }),
87
90
  /* @__PURE__ */ jsx("div", { className: "truncate font-mono text-[11.5px] text-muted-foreground", children: meta })
88
91
  ] }),
89
- /* @__PURE__ */ jsx(ChevronRight, { className: "ml-auto h-4 w-4 shrink-0 text-muted-foreground", "aria-hidden": true })
92
+ /* @__PURE__ */ jsx(
93
+ ChevronRight,
94
+ {
95
+ className: "ml-auto h-4 w-4 shrink-0 text-muted-foreground",
96
+ "aria-hidden": true
97
+ }
98
+ )
90
99
  ] })
91
100
  ] });
92
101
  const rootClasses = cn(
@@ -4,11 +4,11 @@ import { DialogProps } from '@radix-ui/react-dialog';
4
4
 
5
5
  declare const Command: React.ForwardRefExoticComponent<Omit<{
6
6
  children?: React.ReactNode;
7
- } & Pick<Pick<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, keyof React.HTMLAttributes<HTMLDivElement> | "key"> & {
7
+ } & Pick<Pick<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "key" | keyof React.HTMLAttributes<HTMLDivElement>> & {
8
8
  ref?: React.Ref<HTMLDivElement>;
9
9
  } & {
10
10
  asChild?: boolean;
11
- }, keyof React.HTMLAttributes<HTMLDivElement> | "asChild" | "key"> & {
11
+ }, "key" | keyof React.HTMLAttributes<HTMLDivElement> | "asChild"> & {
12
12
  label?: string;
13
13
  shouldFilter?: boolean;
14
14
  filter?: (value: string, search: string, keywords?: string[]) => number;
@@ -28,61 +28,61 @@ declare const CommandInput: React.ForwardRefExoticComponent<Omit<Omit<Pick<Pick<
28
28
  ref?: React.Ref<HTMLInputElement>;
29
29
  } & {
30
30
  asChild?: boolean;
31
- }, "asChild" | "key" | keyof React.InputHTMLAttributes<HTMLInputElement>>, "onChange" | "type" | "value"> & {
31
+ }, "key" | "asChild" | keyof React.InputHTMLAttributes<HTMLInputElement>>, "onChange" | "type" | "value"> & {
32
32
  value?: string;
33
33
  onValueChange?: (search: string) => void;
34
34
  } & React.RefAttributes<HTMLInputElement>, "ref"> & React.RefAttributes<HTMLInputElement>>;
35
35
  declare const CommandList: React.ForwardRefExoticComponent<Omit<{
36
36
  children?: React.ReactNode;
37
- } & Pick<Pick<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, keyof React.HTMLAttributes<HTMLDivElement> | "key"> & {
37
+ } & Pick<Pick<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "key" | keyof React.HTMLAttributes<HTMLDivElement>> & {
38
38
  ref?: React.Ref<HTMLDivElement>;
39
39
  } & {
40
40
  asChild?: boolean;
41
- }, keyof React.HTMLAttributes<HTMLDivElement> | "asChild" | "key"> & {
41
+ }, "key" | keyof React.HTMLAttributes<HTMLDivElement> | "asChild"> & {
42
42
  label?: string;
43
43
  } & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
44
44
  declare const CommandEmpty: React.ForwardRefExoticComponent<Omit<{
45
45
  children?: React.ReactNode;
46
- } & Pick<Pick<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, keyof React.HTMLAttributes<HTMLDivElement> | "key"> & {
46
+ } & Pick<Pick<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "key" | keyof React.HTMLAttributes<HTMLDivElement>> & {
47
47
  ref?: React.Ref<HTMLDivElement>;
48
48
  } & {
49
49
  asChild?: boolean;
50
- }, keyof React.HTMLAttributes<HTMLDivElement> | "asChild" | "key"> & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
50
+ }, "key" | keyof React.HTMLAttributes<HTMLDivElement> | "asChild"> & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
51
51
  declare const CommandLoading: React.ForwardRefExoticComponent<Omit<{
52
52
  children?: React.ReactNode;
53
- } & Pick<Pick<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, keyof React.HTMLAttributes<HTMLDivElement> | "key"> & {
53
+ } & Pick<Pick<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "key" | keyof React.HTMLAttributes<HTMLDivElement>> & {
54
54
  ref?: React.Ref<HTMLDivElement>;
55
55
  } & {
56
56
  asChild?: boolean;
57
- }, keyof React.HTMLAttributes<HTMLDivElement> | "asChild" | "key"> & {
57
+ }, "key" | keyof React.HTMLAttributes<HTMLDivElement> | "asChild"> & {
58
58
  progress?: number;
59
59
  label?: string;
60
60
  } & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
61
61
  declare const CommandGroup: React.ForwardRefExoticComponent<Omit<{
62
62
  children?: React.ReactNode;
63
- } & Omit<Pick<Pick<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, keyof React.HTMLAttributes<HTMLDivElement> | "key"> & {
63
+ } & Omit<Pick<Pick<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "key" | keyof React.HTMLAttributes<HTMLDivElement>> & {
64
64
  ref?: React.Ref<HTMLDivElement>;
65
65
  } & {
66
66
  asChild?: boolean;
67
- }, keyof React.HTMLAttributes<HTMLDivElement> | "asChild" | "key">, "heading" | "value"> & {
67
+ }, "key" | keyof React.HTMLAttributes<HTMLDivElement> | "asChild">, "heading" | "value"> & {
68
68
  heading?: React.ReactNode;
69
69
  value?: string;
70
70
  forceMount?: boolean;
71
71
  } & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
72
- declare const CommandSeparator: React.ForwardRefExoticComponent<Omit<Pick<Pick<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, keyof React.HTMLAttributes<HTMLDivElement> | "key"> & {
72
+ declare const CommandSeparator: React.ForwardRefExoticComponent<Omit<Pick<Pick<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "key" | keyof React.HTMLAttributes<HTMLDivElement>> & {
73
73
  ref?: React.Ref<HTMLDivElement>;
74
74
  } & {
75
75
  asChild?: boolean;
76
- }, keyof React.HTMLAttributes<HTMLDivElement> | "asChild" | "key"> & {
76
+ }, "key" | keyof React.HTMLAttributes<HTMLDivElement> | "asChild"> & {
77
77
  alwaysRender?: boolean;
78
78
  } & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
79
79
  declare const CommandItem: React.ForwardRefExoticComponent<Omit<{
80
80
  children?: React.ReactNode;
81
- } & Omit<Pick<Pick<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, keyof React.HTMLAttributes<HTMLDivElement> | "key"> & {
81
+ } & Omit<Pick<Pick<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "key" | keyof React.HTMLAttributes<HTMLDivElement>> & {
82
82
  ref?: React.Ref<HTMLDivElement>;
83
83
  } & {
84
84
  asChild?: boolean;
85
- }, keyof React.HTMLAttributes<HTMLDivElement> | "asChild" | "key">, "onSelect" | "disabled" | "value"> & {
85
+ }, "key" | keyof React.HTMLAttributes<HTMLDivElement> | "asChild">, "onSelect" | "disabled" | "value"> & {
86
86
  disabled?: boolean;
87
87
  onSelect?: (value: string) => void;
88
88
  value?: string;
@@ -3,9 +3,17 @@ import * as react_jsx_runtime from 'react/jsx-runtime';
3
3
  /**
4
4
  * Coverflow — the busiest-events browser: a 3D perspective lane with a large
5
5
  * lead poster flanked by up to three shrinking, desaturating posters on each
6
- * side. The lane itself drifts a few pixels back and forth
7
- * (`prefers-reduced-motion` stops this outright; an `IntersectionObserver`
8
- * pauses it off-screen).
6
+ * side.
7
+ *
8
+ * NOTHING MOVES ON ITS OWN. The lane used to drift ±40px on a loop; it was
9
+ * removed at product request — a stage that sways is read as a carousel that
10
+ * is about to advance, and the reader waits for it instead of touching it.
11
+ * The stage is driven by the reader: a horizontal drag (touch, pen or mouse)
12
+ * steps the ring one card in the drag's direction and the lane follows the
13
+ * finger a little on the way, so the gesture has weight. The prev/next arrows
14
+ * stay for desktop and keyboard; below `lg` they are hidden so nothing sits
15
+ * over the lead card — the swipe is the control there, and every card is
16
+ * still a focusable, named button for keyboard and screen-reader users.
9
17
  *
10
18
  * IT IS A RING, NOT A LANE. A card's slot comes from `ringOffset` — the
11
19
  * SIGNED SHORTEST WAY ROUND the list, not the raw `index - activeIndex`. With
@@ -1,7 +1,8 @@
1
+ import { useDragSwipe } from '../chunk-IYDH5IU6.js';
1
2
  import { EventPosterFallback } from '../chunk-FDITZ2VM.js';
2
3
  import { cn } from '../chunk-FEK5XGZW.js';
3
- import { __objRest, __spreadValues } from '../chunk-3GWWZKX7.js';
4
- import { useId, useRef, useState, useEffect } from 'react';
4
+ import { __spreadProps, __spreadValues, __objRest } from '../chunk-3GWWZKX7.js';
5
+ import { useId, useState } from 'react';
5
6
  import { ChevronLeft, ChevronRight, Gift } from 'lucide-react';
6
7
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
7
8
 
@@ -21,10 +22,8 @@ var COVERFLOW_CARD_FOCUS_CLASS = "focus-visible:outline-none focus-visible:ring-
21
22
  var COVERFLOW_SIDE_SHADOW_CLASS = "shadow-[0_11px_27px_oklch(0.3_0.02_60/0.22)]";
22
23
  var COVERFLOW_LEAD_SHADOW_CLASS = "shadow-[0_24px_56px_oklch(0.28_0.03_45/0.32)]";
23
24
  var COVERFLOW_PILL_CLASS = "inline-flex h-6 max-w-[85%] items-center gap-1 rounded-full px-2 text-[11px] font-semibold";
24
- var COVERFLOW_DRIFT_DURATION_SECONDS = 26;
25
- var COVERFLOW_DRIFT_PIXELS = 40;
26
25
  var COVERFLOW_LEAD_Z_INDEX = 6;
27
- var COVERFLOW_ARROW_CLASS = "absolute top-1/2 z-20 flex h-11 w-11 -translate-y-1/2 cursor-pointer items-center justify-center rounded-full bg-background/85 text-foreground shadow-[var(--shadow-overlay)] backdrop-blur-sm transition-colors duration-150 hover:bg-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 motion-reduce:transition-none";
26
+ var COVERFLOW_ARROW_CLASS = "absolute top-1/2 z-20 hidden h-11 w-11 -translate-y-1/2 lg:flex cursor-pointer items-center justify-center rounded-full bg-background/85 text-foreground shadow-[var(--shadow-overlay)] backdrop-blur-sm transition-colors duration-150 hover:bg-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 motion-reduce:transition-none";
28
27
  function wrapIndex(index, length) {
29
28
  return (index % length + length) % length;
30
29
  }
@@ -141,7 +140,6 @@ function Coverflow({
141
140
  }) {
142
141
  const scopeId = useId().replace(/[^a-zA-Z0-9]/g, "");
143
142
  const scopeClass = `coverflow-${scopeId}`;
144
- const containerRef = useRef(null);
145
143
  const length = items.length;
146
144
  const active = length > 0 ? wrapIndex(activeIndex, length) : 0;
147
145
  const [step, setStep] = useState({ from: active, to: active, length });
@@ -150,21 +148,13 @@ function Coverflow({
150
148
  step.length === length ? { from: step.to, to: active, length } : { from: active, to: active, length }
151
149
  );
152
150
  }
153
- useEffect(() => {
154
- const node = containerRef.current;
155
- if (!node || typeof IntersectionObserver === "undefined") return void 0;
156
- const observer = new IntersectionObserver(
157
- ([entry]) => {
158
- node.dataset.offscreen = entry.isIntersecting ? "false" : "true";
159
- },
160
- { threshold: 0 }
161
- );
162
- observer.observe(node);
163
- return () => observer.disconnect();
164
- }, []);
151
+ const goTo = (index) => onActiveIndexChange == null ? void 0 : onActiveIndexChange(wrapIndex(index, length));
152
+ const { dragX, isDragging, handlers } = useDragSwipe({
153
+ onSwipe: (direction) => goTo(active + direction),
154
+ disabled: length < 2
155
+ });
165
156
  if (length === 0) return null;
166
157
  const activeItem = items[active];
167
- const goTo = (index) => onActiveIndexChange == null ? void 0 : onActiveIndexChange(wrapIndex(index, length));
168
158
  const activeDelta = ringOffset(step.to - step.from, length);
169
159
  const visibleCards = items.map((item, index) => {
170
160
  const offset = ringOffset(index - active, length);
@@ -208,28 +198,11 @@ function Coverflow({
208
198
  --cf-tier-base: 0.5714;
209
199
  }
210
200
  }
211
- .${scopeClass} .coverflow-lane {
212
- animation: ${scopeClass}-drift ${COVERFLOW_DRIFT_DURATION_SECONDS}s ease-in-out infinite alternate;
213
- }
214
- .${scopeClass}[data-offscreen="true"] .coverflow-lane {
215
- animation-play-state: paused;
216
- }
217
- @keyframes ${scopeClass}-drift {
218
- from { transform: translateX(-${COVERFLOW_DRIFT_PIXELS}px); }
219
- to { transform: translateX(${COVERFLOW_DRIFT_PIXELS}px); }
220
- }
221
- @media (prefers-reduced-motion: reduce) {
222
- .${scopeClass} .coverflow-lane {
223
- animation: none;
224
- transform: none;
225
- }
226
- }
227
201
  ` }),
228
202
  /* @__PURE__ */ jsxs(
229
203
  "div",
230
- {
231
- ref: containerRef,
232
- className: "relative flex h-[300px] items-center justify-center overflow-x-clip overflow-y-visible [perspective:900px] sm:h-[380px] sm:[perspective:1400px]",
204
+ __spreadProps(__spreadValues({}, handlers), {
205
+ className: "relative flex h-[300px] select-none items-center justify-center overflow-x-clip overflow-y-visible [touch-action:pan-y] [perspective:900px] sm:h-[380px] sm:[perspective:1400px]",
233
206
  children: [
234
207
  /* @__PURE__ */ jsx(
235
208
  "div",
@@ -267,21 +240,31 @@ function Coverflow({
267
240
  }
268
241
  )
269
242
  ] }),
270
- /* @__PURE__ */ jsx("div", { className: "coverflow-lane relative h-full w-full [transform-style:preserve-3d] motion-reduce:[animation:none]", children: visibleCards.map(({ item, index, offset, suppressTransition }) => /* @__PURE__ */ jsx(
271
- CoverflowCard,
243
+ /* @__PURE__ */ jsx(
244
+ "div",
272
245
  {
273
- item,
274
- index,
275
- offset,
276
- suppressTransition,
277
- freeLabel,
278
- onActivate: goTo,
279
- linkComponent: LinkComponent
280
- },
281
- item.id
282
- )) })
246
+ className: cn(
247
+ "coverflow-lane relative h-full w-full [transform-style:preserve-3d]",
248
+ !isDragging && "transition-transform duration-300 ease-out motion-reduce:transition-none"
249
+ ),
250
+ style: { transform: `translateX(${dragX / 3}px)` },
251
+ children: visibleCards.map(({ item, index, offset, suppressTransition }) => /* @__PURE__ */ jsx(
252
+ CoverflowCard,
253
+ {
254
+ item,
255
+ index,
256
+ offset,
257
+ suppressTransition,
258
+ freeLabel,
259
+ onActivate: goTo,
260
+ linkComponent: LinkComponent
261
+ },
262
+ item.id
263
+ ))
264
+ }
265
+ )
283
266
  ]
284
- }
267
+ })
285
268
  ),
286
269
  /* @__PURE__ */ jsxs("div", { className: "mt-4 space-y-4 text-center", children: [
287
270
  /* @__PURE__ */ jsxs("div", { "aria-live": "polite", children: [