@bearmenu/ui 0.8.17 → 0.8.18

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 (71) hide show
  1. package/dist/{chunk-WYTJR2YU.js → chunk-5CPBSNWQ.js} +6 -1
  2. package/dist/{chunk-54FQQZZJ.js → chunk-EQYB4Q77.js} +1 -1
  3. package/dist/chunk-LYREUZH6.js +156 -0
  4. package/dist/chunk-PW247TG2.js +81 -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/command.d.ts +15 -15
  10. package/dist/components/concept-compare-rail.js +2 -2
  11. package/dist/components/concept-compare.js +2 -2
  12. package/dist/components/event-card.d.ts +22 -4
  13. package/dist/components/event-card.js +95 -50
  14. package/dist/components/event-day-header.d.ts +5 -3
  15. package/dist/components/event-day-header.js +12 -4
  16. package/dist/components/event-door.d.ts +41 -0
  17. package/dist/components/event-door.js +65 -0
  18. package/dist/components/event-hub-hero.d.ts +10 -2
  19. package/dist/components/event-hub-hero.js +11 -5
  20. package/dist/components/event-tonight-strip.d.ts +50 -0
  21. package/dist/components/event-tonight-strip.js +67 -0
  22. package/dist/components/item.d.ts +2 -2
  23. package/dist/components/menu-hub-hero.d.ts +7 -1
  24. package/dist/components/menu-hub-hero.js +2 -1
  25. package/dist/components/place-card.js +1 -1
  26. package/dist/components/poster-stack.d.ts +36 -3
  27. package/dist/components/poster-stack.js +49 -18
  28. package/dist/components/snap-rail.d.ts +2 -2
  29. package/dist/components/snap-rail.js +1 -1
  30. package/dist/components/spinner.d.ts +2 -2
  31. package/dist/components/tag.d.ts +2 -2
  32. package/dist/components/text.d.ts +3 -3
  33. package/dist/components/timeline-day.d.ts +54 -0
  34. package/dist/components/timeline-day.js +65 -0
  35. package/dist/components/timeline-hour-puck.d.ts +47 -0
  36. package/dist/components/timeline-hour-puck.js +3 -0
  37. package/dist/components/timeline-hour.d.ts +96 -0
  38. package/dist/components/{timeline-band.js → timeline-hour.js} +2 -1
  39. package/dist/components/timeline-playhead.js +1 -3
  40. package/package.json +1 -1
  41. package/src/components/event-card.tsx +70 -13
  42. package/src/components/event-day-header.tsx +19 -7
  43. package/src/components/event-door.stories.tsx +88 -0
  44. package/src/components/event-door.tsx +97 -0
  45. package/src/components/event-hub-hero.tsx +21 -3
  46. package/src/components/event-tonight-strip.stories.tsx +78 -0
  47. package/src/components/event-tonight-strip.test.tsx +59 -0
  48. package/src/components/event-tonight-strip.tsx +134 -0
  49. package/src/components/menu-hub-hero.test.tsx +13 -0
  50. package/src/components/menu-hub-hero.tsx +9 -2
  51. package/src/components/poster-stack.stories.tsx +43 -1
  52. package/src/components/poster-stack.tsx +98 -19
  53. package/src/components/snap-rail.tsx +6 -1
  54. package/src/components/timeline-day.stories.tsx +201 -0
  55. package/src/components/timeline-day.test.tsx +78 -0
  56. package/src/components/timeline-day.tsx +114 -0
  57. package/src/components/timeline-hour-puck.tsx +139 -0
  58. package/src/components/timeline-hour.tsx +266 -0
  59. package/src/components/timeline-playhead.tsx +1 -1
  60. package/dist/chunk-NSNEANOQ.js +0 -104
  61. package/dist/components/hour-band.d.ts +0 -66
  62. package/dist/components/hour-band.js +0 -81
  63. package/dist/components/hour-group.d.ts +0 -67
  64. package/dist/components/hour-group.js +0 -59
  65. package/dist/components/timeline-band.d.ts +0 -64
  66. package/src/components/hour-band.stories.tsx +0 -182
  67. package/src/components/hour-band.tsx +0 -146
  68. package/src/components/hour-group.stories.tsx +0 -100
  69. package/src/components/hour-group.tsx +0 -124
  70. package/src/components/timeline-band.stories.tsx +0 -206
  71. package/src/components/timeline-band.tsx +0 -247
@@ -1,247 +0,0 @@
1
- "use client";
2
-
3
- import * as React from "react";
4
- import { Radio } from "lucide-react";
5
- import { cn } from "../lib/utils";
6
- import { Separator } from "./separator";
7
- import { Skeleton } from "./skeleton";
8
-
9
- /**
10
- * TimelineBand — the desktop timeline's three-column row: lane │ spine │ content.
11
- *
12
- * Desktop counterpart of `<HourBand>`. Where the mobile timeline collapses the
13
- * axis into a 52px gutter beside full-width cards, the desktop layout gives the
14
- * poster its own lane (`--timeline-lane`), a dedicated spine
15
- * (`--timeline-spine`) carrying the hairline, and a separate content column for
16
- * whatever follows.
17
- *
18
- * ── The band does not draw hours ──────────────────────────────────────────
19
- * IT USED TO, and only for the hour it opened on. Every LATER hour was an
20
- * inline `HourGroup` heading in the content column instead, so one concept was
21
- * drawn in two grammars 48px apart — a bordered puck ON the axis at the top of
22
- * the day, bare muted text OFF it for the rest of it. An axis whose ticks are
23
- * labelled once is not an axis, and that is exactly how it read: the later
24
- * hours were harder to follow than the first.
25
- *
26
- * Now every hour is a station and `HourGroup` emits its own, via
27
- * `TimelineHourPuck` below. The band keeps the geometry and the hairline and
28
- * nothing else — which is also why `puckTop` is gone: each puck centres itself
29
- * on its own heading rule, so there is no magic offset left for a caller to
30
- * tune against a lane height.
31
- *
32
- * The invariant the old docblock was reaching for still holds, and states more
33
- * simply now: AN HOUR APPEARS EXACTLY ONCE, AND ALWAYS ON THE SPINE.
34
- *
35
- * HOW THE CONTENT COLUMN IS MEANT TO BE FILLED — the source design composes it
36
- * in tiers, and getting this wrong is what makes the band look flat:
37
- *
38
- * lane one feature card (an `EventCard` with `emphasis`), plus any
39
- * "N earlier events" collapse control beneath it.
40
- * content an `<HourGroup spine>` per hour — the opening one holding a grid
41
- * of `EventCard`s, later ones holding the denser
42
- * `<EventCompactRow>` as the night thins out.
43
- *
44
- * So the density falls off across the row: one poster, then a few cards, then
45
- * rows.
46
- */
47
-
48
- /**
49
- * Ring pulse on the live puck — inherited from an earlier `TimelinePlayhead`,
50
- * which carried this surface's only motion. Kept at its 2.4s `ease-in-out` and
51
- * its reduced-motion kill switch, but BOX-SHADOW ONLY: a `transform: scale`
52
- * would make the hairline visibly breathe here, because this puck sits ON the
53
- * rail. (Today's `timeline-playhead.tsx` is static and sits between bands.)
54
- */
55
- const LIVE_PUCK_PULSE_CLASS = "bm-timeline-live-puck-pulse";
56
-
57
- /**
58
- * The mobile axis gutter, shared by `HourBand` and `TimelinePlayhead` so the
59
- * hour chips and the current-time label sit in one column. 58px is the width
60
- * of a five-glyph 24-hour label ("20:00") in the chip's mono type plus its
61
- * padding; a 12-hour locale's "09:00 AM" overflows it, which is one of the
62
- * reasons the events surfaces format 24-hour everywhere.
63
- */
64
- export const TIMELINE_MOBILE_GUTTER_CLASS = "w-[58px]";
65
-
66
- export type TimelineHourPuckProps = {
67
- /** The hour, already formatted and localised — or an em dash for a quiet day. */
68
- time: string;
69
- /** Marks this as the hour currently in progress. */
70
- isLive?: boolean;
71
- /** Shown under the puck when live, e.g. "NOW ON". Required to render it. */
72
- liveLabel?: string;
73
- /**
74
- * `station` (default): the bordered puck that sits ON the hairline, for the
75
- * desktop spine. `chip`: a filled, fixed-width lozenge with the hairline
76
- * starting BELOW it, for the mobile gutter — an app's time label, not a
77
- * station on a rail. In `chip` the live label is visually hidden: the tinted
78
- * chip plus a small on-air glyph are the visible signal (the glyph so the
79
- * state never rests on colour alone once the pulse is reduced away) and the
80
- * label stays for screen readers only.
81
- */
82
- variant?: "station" | "chip";
83
- className?: string;
84
- };
85
-
86
- /**
87
- * The station on the timeline's axis — ONE object, positioned by its host:
88
- * `HourGroup` centres it on its own heading rule out in the desktop spine,
89
- * `HourBand` pins it to the top of the mobile gutter.
90
- *
91
- * Extracted because it was hand-rolled twice and the copies had already
92
- * drifted — this one bordered and `rounded-[7px] px-[7px]`, `hour-band.tsx`'s
93
- * borderless and `rounded-md px-1.5`. The `bg-background` fill punches through
94
- * the hairline so the rail reads as continuous behind the label rather than
95
- * butting into it.
96
- *
97
- * The live label is absolutely positioned rather than a flex sibling: hosts
98
- * centre this puck with `-translate-y-1/2`, and a two-item column would centre
99
- * the STACK, lifting the puck off the line it exists to sit on.
100
- */
101
- export function TimelineHourPuck({
102
- time,
103
- isLive,
104
- liveLabel,
105
- variant = "station",
106
- className,
107
- }: TimelineHourPuckProps) {
108
- const isChip = variant === "chip";
109
-
110
- return (
111
- <span className={cn("relative flex justify-center", className)}>
112
- {isLive && (
113
- <style>{`
114
- @keyframes ${LIVE_PUCK_PULSE_CLASS} {
115
- 0%, 100% { box-shadow: 0 0 0 0 color-mix(in oklch, var(--success) 40%, transparent); }
116
- 50% { box-shadow: 0 0 0 4px color-mix(in oklch, var(--success) 0%, transparent); }
117
- }
118
- .${LIVE_PUCK_PULSE_CLASS} {
119
- animation: ${LIVE_PUCK_PULSE_CLASS} 2.4s ease-in-out infinite;
120
- }
121
- @media (prefers-reduced-motion: reduce) {
122
- .${LIVE_PUCK_PULSE_CLASS} { animation: none; }
123
- }
124
- `}</style>
125
- )}
126
- <span
127
- className={cn(
128
- "whitespace-nowrap font-mono tabular-nums",
129
- isChip
130
- ? cn(
131
- "inline-flex h-6 min-w-[58px] items-center justify-center gap-1 rounded-[7px] px-1 text-[12px] font-medium",
132
- isLive ? cn("bg-success/10 text-success", LIVE_PUCK_PULSE_CLASS) : "bg-muted text-foreground"
133
- )
134
- : cn(
135
- "rounded-[7px] border bg-background px-[7px] py-[3px] text-[12.5px] font-medium",
136
- isLive
137
- ? cn("border-success/40 text-success", LIVE_PUCK_PULSE_CLASS)
138
- : "border-border text-foreground"
139
- )
140
- )}
141
- >
142
- {isChip && isLive && <Radio className="h-2.5 w-2.5 shrink-0" aria-hidden />}
143
- {time}
144
- </span>
145
- {isLive && !!liveLabel && (
146
- <span
147
- className={cn(
148
- isChip
149
- ? "sr-only"
150
- : cn(
151
- "absolute top-[calc(100%+6px)] whitespace-nowrap bg-background px-1",
152
- "text-[9.5px] font-extrabold tracking-[0.08em] text-success"
153
- )
154
- )}
155
- >
156
- {liveLabel}
157
- </span>
158
- )}
159
- </span>
160
- );
161
- }
162
-
163
- export type TimelineBandProps = {
164
- /** The lane column — usually a poster card, or empty for a quiet hour. */
165
- lane: React.ReactNode;
166
- /** The content column, normally one `<HourGroup spine>` per hour. */
167
- children: React.ReactNode;
168
- className?: string;
169
- };
170
-
171
- export function TimelineBand({ lane, children, className }: TimelineBandProps) {
172
- return (
173
- <div className={cn("flex items-stretch", className)}>
174
- <div className="w-[var(--timeline-lane)] shrink-0">{lane}</div>
175
-
176
- <div className="relative flex w-[var(--timeline-spine)] shrink-0 justify-center self-stretch">
177
- <Separator orientation="vertical" className="h-full" />
178
- </div>
179
-
180
- <div className="flex min-w-0 flex-1 flex-col">{children}</div>
181
- </div>
182
- );
183
- }
184
-
185
- /**
186
- * A fixed-size shimmer block. `radiusPx` only needs to be passed for the
187
- * poster-sized blocks (12, matching `rounded-xl`) — every thinner text-line
188
- * height here (13–18px) is less than twice that radius, so the browser clamps
189
- * it to a pill automatically even left at the `Skeleton` default.
190
- */
191
- function sk(width: string, height: number, radiusPx = 12) {
192
- return (
193
- <Skeleton
194
- style={{ width, height: `${height}px`, borderRadius: `${radiusPx}px` }}
195
- />
196
- );
197
- }
198
-
199
- export type TimelineBandSkeletonProps = {
200
- className?: string;
201
- };
202
-
203
- /**
204
- * Holds the lane/spine/content geometry so nothing jumps when data lands.
205
- * The spine renders its hairline with no puck — there's no hour to show yet.
206
- */
207
- export function TimelineBandSkeleton({ className }: TimelineBandSkeletonProps) {
208
- return (
209
- <div className={cn("flex items-stretch", className)} aria-hidden="true">
210
- <div className="flex w-[var(--timeline-lane)] shrink-0 flex-col gap-3">
211
- {sk("100%", 297, 12)}
212
- {sk("70%", 18)}
213
- {sk("45%", 13)}
214
- </div>
215
-
216
- <div className="relative flex w-[var(--timeline-spine)] shrink-0 justify-center self-stretch">
217
- <Separator orientation="vertical" className="h-full" />
218
- </div>
219
-
220
- <div className="flex min-w-0 flex-1 flex-col gap-4">
221
- <div className="grid grid-cols-3 gap-[10px]">
222
- {Array.from({ length: 3 }, (_, index) => (
223
- <div key={index} className="flex flex-col gap-2">
224
- {sk("100%", 190, 12)}
225
- {sk("85%", 15)}
226
- {sk("55%", 12)}
227
- </div>
228
- ))}
229
- </div>
230
-
231
- <div className="flex flex-col">
232
- {Array.from({ length: 3 }, (_, index) => (
233
- <div key={index} className="relative flex items-center gap-[11px] py-2">
234
- <Separator className="absolute inset-x-0 top-0" />
235
-
236
- <Skeleton className="h-[34px] w-[34px] shrink-0 rounded-md" />
237
- <div className="flex min-w-0 flex-1 flex-col gap-1.5">
238
- {sk("60%", 13)}
239
- {sk("40%", 11.5)}
240
- </div>
241
- </div>
242
- ))}
243
- </div>
244
- </div>
245
- </div>
246
- );
247
- }