@bearmenu/ui 0.8.3 → 0.8.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-2WDJR6K7.js +76 -0
- package/dist/chunk-637L7XQ3.js +85 -0
- package/dist/chunk-6XMYEZVR.js +26 -0
- package/dist/chunk-DNSXMHYX.js +8 -0
- package/dist/chunk-FDITZ2VM.js +16 -0
- package/dist/chunk-GPH6L4DX.js +55 -0
- package/dist/{chunk-N2KNBPAQ.js → chunk-QXHMPJ35.js} +1 -0
- package/dist/chunk-TT3WFS6S.js +379 -0
- package/dist/chunk-UXWAOISG.js +170 -0
- package/dist/{chunk-UZ3PCIE5.js → chunk-XWMRABZW.js} +2 -2
- package/dist/{chunk-VBA45GLP.js → chunk-ZDAHKI3A.js} +12 -2
- package/dist/components/alert.d.ts +2 -2
- package/dist/components/badge.d.ts +1 -1
- package/dist/components/button.d.ts +1 -1
- package/dist/components/card.d.ts +2 -2
- package/dist/components/card.js +4 -127
- package/dist/components/category-deck.d.ts +50 -0
- package/dist/components/category-deck.js +128 -0
- package/dist/components/command.d.ts +15 -15
- package/dist/components/concept-card.d.ts +43 -0
- package/dist/components/concept-card.js +81 -0
- package/dist/components/coverflow.d.ts +123 -0
- package/dist/components/coverflow.js +300 -0
- package/dist/components/date-input.d.ts +1 -1
- package/dist/components/day-rail-chip.d.ts +82 -0
- package/dist/components/day-rail-chip.js +105 -0
- package/dist/components/drift-rail.d.ts +28 -0
- package/dist/components/drift-rail.js +3 -0
- package/dist/components/event-card.d.ts +97 -0
- package/dist/components/event-card.js +161 -0
- package/dist/components/event-compact-row.d.ts +72 -0
- package/dist/components/event-compact-row.js +133 -0
- package/dist/components/event-day-header.d.ts +65 -0
- package/dist/components/event-day-header.js +95 -0
- package/dist/components/event-day-separator.d.ts +46 -0
- package/dist/components/event-day-separator.js +50 -0
- package/dist/components/event-detail-panel.d.ts +80 -0
- package/dist/components/event-detail-panel.js +251 -0
- package/dist/components/event-hub-hero.d.ts +34 -0
- package/dist/components/event-hub-hero.js +57 -0
- package/dist/components/event-index-row.d.ts +68 -0
- package/dist/components/event-index-row.js +84 -0
- package/dist/components/event-list-row-wide.d.ts +101 -0
- package/dist/components/event-list-row-wide.js +218 -0
- package/dist/components/event-list-row.d.ts +82 -0
- package/dist/components/event-list-row.js +173 -0
- package/dist/components/event-poster-fallback.d.ts +20 -0
- package/dist/components/event-poster-fallback.js +3 -0
- package/dist/components/event-types.d.ts +118 -0
- package/dist/components/event-types.js +2 -0
- package/dist/components/events-empty-state.d.ts +44 -0
- package/dist/components/events-empty-state.js +73 -0
- package/dist/components/facet-group.d.ts +93 -0
- package/dist/components/facet-group.js +222 -0
- package/dist/components/featured-rail-item.d.ts +42 -0
- package/dist/components/featured-rail-item.js +88 -0
- package/dist/components/hour-band.d.ts +68 -0
- package/dist/components/hour-band.js +53 -0
- package/dist/components/hour-group.d.ts +67 -0
- package/dist/components/hour-group.js +59 -0
- package/dist/components/icon.d.ts +18 -0
- package/dist/components/icon.js +31 -0
- package/dist/components/item.d.ts +2 -2
- package/dist/components/motion-icon-controls.d.ts +26 -0
- package/dist/components/motion-icon-controls.js +4 -0
- package/dist/components/motion-icon-set.d.ts +27 -0
- package/dist/components/motion-icon-set.js +139 -0
- package/dist/components/motion-icon.d.ts +44 -0
- package/dist/components/motion-icon.js +3 -0
- package/dist/components/multi-select-combobox.js +1 -1
- package/dist/components/place-about-tab.js +3 -3
- package/dist/components/place-details-mobile.js +3 -3
- package/dist/components/poster-stack.d.ts +30 -0
- package/dist/components/poster-stack.js +104 -0
- package/dist/components/poster-tape.d.ts +36 -0
- package/dist/components/poster-tape.js +64 -0
- package/dist/components/poster-wall.d.ts +25 -0
- package/dist/components/poster-wall.js +183 -0
- package/dist/components/quiet-stretch.d.ts +43 -0
- package/dist/components/quiet-stretch.js +54 -0
- package/dist/components/section-header.d.ts +53 -0
- package/dist/components/section-header.js +63 -0
- package/dist/components/segmented-nav.d.ts +62 -0
- package/dist/components/segmented-nav.js +54 -0
- package/dist/components/skeleton-card.js +1 -1
- package/dist/components/skeleton.d.ts +17 -0
- package/dist/components/skeleton.js +1 -1
- package/dist/components/spinner.d.ts +2 -2
- package/dist/components/tag-group.js +3 -26
- package/dist/components/tag.d.ts +2 -2
- package/dist/components/tag.js +1 -1
- package/dist/components/text.d.ts +3 -3
- package/dist/components/timeline-band.d.ts +46 -0
- package/dist/components/timeline-band.js +5 -0
- package/dist/components/venue-events-block.d.ts +143 -0
- package/dist/components/venue-events-block.js +222 -0
- package/dist/components/weekend-panel.d.ts +44 -0
- package/dist/components/weekend-panel.js +231 -0
- package/dist/lib/icon-registry.d.ts +58 -0
- package/dist/lib/icon-registry.js +39 -0
- package/package.json +2 -1
- package/src/components/accordion.stories.tsx +1 -1
- package/src/components/card.tsx +44 -1
- package/src/components/category-deck.stories.tsx +96 -0
- package/src/components/category-deck.tsx +227 -0
- package/src/components/combobox.stories.tsx +1 -1
- package/src/components/concept-card.stories.tsx +80 -0
- package/src/components/concept-card.tsx +143 -0
- package/src/components/coverflow.stories.tsx +240 -0
- package/src/components/coverflow.test.tsx +193 -0
- package/src/components/coverflow.tsx +550 -0
- package/src/components/currency-input.stories.tsx +3 -3
- package/src/components/day-rail-chip.stories.tsx +145 -0
- package/src/components/day-rail-chip.tsx +201 -0
- package/src/components/drift-rail.stories.tsx +92 -0
- package/src/components/drift-rail.tsx +86 -0
- package/src/components/empty-state.stories.tsx +3 -3
- package/src/components/event-card.stories.tsx +248 -0
- package/src/components/event-card.test.tsx +177 -0
- package/src/components/event-card.tsx +321 -0
- package/src/components/event-compact-row.stories.tsx +221 -0
- package/src/components/event-compact-row.tsx +268 -0
- package/src/components/event-day-header.stories.tsx +124 -0
- package/src/components/event-day-header.tsx +180 -0
- package/src/components/event-day-separator.stories.tsx +70 -0
- package/src/components/event-day-separator.tsx +95 -0
- package/src/components/event-detail-panel.stories.tsx +191 -0
- package/src/components/event-detail-panel.tsx +360 -0
- package/src/components/event-hub-hero.stories.tsx +66 -0
- package/src/components/event-hub-hero.tsx +110 -0
- package/src/components/event-index-row.stories.tsx +116 -0
- package/src/components/event-index-row.tsx +181 -0
- package/src/components/event-list-row-wide.stories.tsx +204 -0
- package/src/components/event-list-row-wide.tsx +386 -0
- package/src/components/event-list-row.stories.tsx +159 -0
- package/src/components/event-list-row.tsx +335 -0
- package/src/components/event-poster-fallback.stories.tsx +52 -0
- package/src/components/event-poster-fallback.tsx +31 -0
- package/src/components/event-types.ts +133 -0
- package/src/components/events-empty-state.stories.tsx +59 -0
- package/src/components/events-empty-state.tsx +131 -0
- package/src/components/facet-group.stories.tsx +247 -0
- package/src/components/facet-group.test.tsx +225 -0
- package/src/components/facet-group.tsx +352 -0
- package/src/components/featured-rail-item.stories.tsx +86 -0
- package/src/components/featured-rail-item.tsx +153 -0
- package/src/components/form.stories.tsx +1 -1
- package/src/components/hour-band.stories.tsx +159 -0
- package/src/components/hour-band.tsx +126 -0
- package/src/components/hour-group.stories.tsx +100 -0
- package/src/components/hour-group.tsx +124 -0
- package/src/components/icon.tsx +57 -0
- package/src/components/input-otp.stories.tsx +1 -1
- package/src/components/markdown-renderer.stories.tsx +1 -1
- package/src/components/motion-icon-controls.test.tsx +78 -0
- package/src/components/motion-icon-controls.tsx +552 -0
- package/src/components/motion-icon-set.stories.tsx +185 -0
- package/src/components/motion-icon-set.tsx +219 -0
- package/src/components/motion-icon.test.tsx +159 -0
- package/src/components/motion-icon.tsx +163 -0
- package/src/components/multi-select-combobox.stories.tsx +1 -1
- package/src/components/phone-frame.stories.tsx +1 -1
- package/src/components/poster-stack.stories.tsx +68 -0
- package/src/components/poster-stack.tsx +155 -0
- package/src/components/poster-tape.stories.tsx +70 -0
- package/src/components/poster-tape.tsx +122 -0
- package/src/components/poster-wall.stories.tsx +95 -0
- package/src/components/poster-wall.tsx +220 -0
- package/src/components/quiet-stretch.stories.tsx +62 -0
- package/src/components/quiet-stretch.tsx +115 -0
- package/src/components/rating.stories.tsx +3 -3
- package/src/components/searchable-select.stories.tsx +1 -1
- package/src/components/section-header.stories.tsx +70 -0
- package/src/components/section-header.tsx +134 -0
- package/src/components/segmented-nav.stories.tsx +118 -0
- package/src/components/segmented-nav.tsx +113 -0
- package/src/components/selection-bar.stories.tsx +1 -1
- package/src/components/skeleton-card.stories.tsx +3 -3
- package/src/components/skeleton.tsx +18 -0
- package/src/components/sliding-panels.stories.tsx +1 -1
- package/src/components/stepper.stories.tsx +1 -1
- package/src/components/sticky-container.stories.tsx +3 -3
- package/src/components/tag.tsx +11 -1
- package/src/components/timeline-band.stories.tsx +206 -0
- package/src/components/timeline-band.tsx +207 -0
- package/src/components/toggle-group.stories.tsx +1 -1
- package/src/components/tooltip.stories.tsx +2 -2
- package/src/components/venue-events-block.stories.tsx +216 -0
- package/src/components/venue-events-block.tsx +408 -0
- package/src/components/weekend-panel.stories.tsx +92 -0
- package/src/components/weekend-panel.tsx +322 -0
- package/src/globals.css +19 -0
- package/src/lib/icon-registry.ts +139 -0
- package/src/test/setup.ts +5 -1
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { cn } from "../lib/utils";
|
|
5
|
+
import { Separator } from "./separator";
|
|
6
|
+
import { Skeleton } from "./skeleton";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* TimelineBand — the desktop timeline's three-column row: lane │ spine │ content.
|
|
10
|
+
*
|
|
11
|
+
* Desktop counterpart of `<HourBand>`. Where the mobile timeline collapses the
|
|
12
|
+
* axis into a 52px gutter beside full-width cards, the desktop layout gives the
|
|
13
|
+
* poster its own lane (`--timeline-lane`), a dedicated spine
|
|
14
|
+
* (`--timeline-spine`) carrying the hairline, and a separate content column for
|
|
15
|
+
* whatever follows.
|
|
16
|
+
*
|
|
17
|
+
* ── The band does not draw hours ──────────────────────────────────────────
|
|
18
|
+
* IT USED TO, and only for the hour it opened on. Every LATER hour was an
|
|
19
|
+
* inline `HourGroup` heading in the content column instead, so one concept was
|
|
20
|
+
* drawn in two grammars 48px apart — a bordered puck ON the axis at the top of
|
|
21
|
+
* the day, bare muted text OFF it for the rest of it. An axis whose ticks are
|
|
22
|
+
* labelled once is not an axis, and that is exactly how it read: the later
|
|
23
|
+
* hours were harder to follow than the first.
|
|
24
|
+
*
|
|
25
|
+
* Now every hour is a station and `HourGroup` emits its own, via
|
|
26
|
+
* `TimelineHourPuck` below. The band keeps the geometry and the hairline and
|
|
27
|
+
* nothing else — which is also why `puckTop` is gone: each puck centres itself
|
|
28
|
+
* on its own heading rule, so there is no magic offset left for a caller to
|
|
29
|
+
* tune against a lane height.
|
|
30
|
+
*
|
|
31
|
+
* The invariant the old docblock was reaching for still holds, and states more
|
|
32
|
+
* simply now: AN HOUR APPEARS EXACTLY ONCE, AND ALWAYS ON THE SPINE.
|
|
33
|
+
*
|
|
34
|
+
* HOW THE CONTENT COLUMN IS MEANT TO BE FILLED — the source design composes it
|
|
35
|
+
* in tiers, and getting this wrong is what makes the band look flat:
|
|
36
|
+
*
|
|
37
|
+
* lane one feature card (an `EventCard` with `emphasis`), plus any
|
|
38
|
+
* "N earlier events" collapse control beneath it.
|
|
39
|
+
* content an `<HourGroup spine>` per hour — the opening one holding a grid
|
|
40
|
+
* of `EventCard`s, later ones holding the denser
|
|
41
|
+
* `<EventCompactRow>` as the night thins out.
|
|
42
|
+
*
|
|
43
|
+
* So the density falls off across the row: one poster, then a few cards, then
|
|
44
|
+
* rows.
|
|
45
|
+
*/
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Ring pulse on the live puck — inherited from the deleted `TimelinePlayhead`,
|
|
49
|
+
* which carried this surface's only motion. Kept at its 2.4s `ease-in-out` and
|
|
50
|
+
* its reduced-motion kill switch, but BOX-SHADOW ONLY: the playhead's
|
|
51
|
+
* `transform: scale` would make the hairline visibly breathe here, because
|
|
52
|
+
* unlike the playhead this puck sits ON the rail.
|
|
53
|
+
*/
|
|
54
|
+
const LIVE_PUCK_PULSE_CLASS = "bm-timeline-live-puck-pulse";
|
|
55
|
+
|
|
56
|
+
export type TimelineHourPuckProps = {
|
|
57
|
+
/** The hour, already formatted and localised — or an em dash for a quiet day. */
|
|
58
|
+
time: string;
|
|
59
|
+
/** Marks this as the hour currently in progress. */
|
|
60
|
+
isLive?: boolean;
|
|
61
|
+
/** Shown under the puck when live, e.g. "NOW ON". Required to render it. */
|
|
62
|
+
liveLabel?: string;
|
|
63
|
+
className?: string;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* The station on the timeline's axis — ONE object, positioned by its host:
|
|
68
|
+
* `HourGroup` centres it on its own heading rule out in the desktop spine,
|
|
69
|
+
* `HourBand` pins it to the top of the mobile gutter.
|
|
70
|
+
*
|
|
71
|
+
* Extracted because it was hand-rolled twice and the copies had already
|
|
72
|
+
* drifted — this one bordered and `rounded-[7px] px-[7px]`, `hour-band.tsx`'s
|
|
73
|
+
* borderless and `rounded-md px-1.5`. The `bg-background` fill punches through
|
|
74
|
+
* the hairline so the rail reads as continuous behind the label rather than
|
|
75
|
+
* butting into it.
|
|
76
|
+
*
|
|
77
|
+
* The live label is absolutely positioned rather than a flex sibling: hosts
|
|
78
|
+
* centre this puck with `-translate-y-1/2`, and a two-item column would centre
|
|
79
|
+
* the STACK, lifting the puck off the line it exists to sit on.
|
|
80
|
+
*/
|
|
81
|
+
export function TimelineHourPuck({ time, isLive, liveLabel, className }: TimelineHourPuckProps) {
|
|
82
|
+
return (
|
|
83
|
+
<span className={cn("relative flex justify-center", className)}>
|
|
84
|
+
{isLive && (
|
|
85
|
+
<style>{`
|
|
86
|
+
@keyframes ${LIVE_PUCK_PULSE_CLASS} {
|
|
87
|
+
0%, 100% { box-shadow: 0 0 0 0 color-mix(in oklch, var(--success) 40%, transparent); }
|
|
88
|
+
50% { box-shadow: 0 0 0 4px color-mix(in oklch, var(--success) 0%, transparent); }
|
|
89
|
+
}
|
|
90
|
+
.${LIVE_PUCK_PULSE_CLASS} {
|
|
91
|
+
animation: ${LIVE_PUCK_PULSE_CLASS} 2.4s ease-in-out infinite;
|
|
92
|
+
}
|
|
93
|
+
@media (prefers-reduced-motion: reduce) {
|
|
94
|
+
.${LIVE_PUCK_PULSE_CLASS} { animation: none; }
|
|
95
|
+
}
|
|
96
|
+
`}</style>
|
|
97
|
+
)}
|
|
98
|
+
<span
|
|
99
|
+
className={cn(
|
|
100
|
+
"whitespace-nowrap rounded-[7px] border bg-background px-[7px] py-[3px]",
|
|
101
|
+
"font-mono text-[12.5px] font-medium tabular-nums",
|
|
102
|
+
isLive
|
|
103
|
+
? cn("border-success/40 text-success", LIVE_PUCK_PULSE_CLASS)
|
|
104
|
+
: "border-border text-foreground"
|
|
105
|
+
)}
|
|
106
|
+
>
|
|
107
|
+
{time}
|
|
108
|
+
</span>
|
|
109
|
+
{isLive && !!liveLabel && (
|
|
110
|
+
<span
|
|
111
|
+
className={cn(
|
|
112
|
+
"absolute top-[calc(100%+6px)] whitespace-nowrap bg-background px-1",
|
|
113
|
+
"text-[9.5px] font-extrabold tracking-[0.08em] text-success"
|
|
114
|
+
)}
|
|
115
|
+
>
|
|
116
|
+
{liveLabel}
|
|
117
|
+
</span>
|
|
118
|
+
)}
|
|
119
|
+
</span>
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export type TimelineBandProps = {
|
|
124
|
+
/** The lane column — usually a poster card, or empty for a quiet hour. */
|
|
125
|
+
lane: React.ReactNode;
|
|
126
|
+
/** The content column, normally one `<HourGroup spine>` per hour. */
|
|
127
|
+
children: React.ReactNode;
|
|
128
|
+
className?: string;
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
export function TimelineBand({ lane, children, className }: TimelineBandProps) {
|
|
132
|
+
return (
|
|
133
|
+
<div className={cn("flex items-stretch", className)}>
|
|
134
|
+
<div className="w-[var(--timeline-lane)] shrink-0">{lane}</div>
|
|
135
|
+
|
|
136
|
+
<div className="relative flex w-[var(--timeline-spine)] shrink-0 justify-center self-stretch">
|
|
137
|
+
<Separator orientation="vertical" className="h-full" />
|
|
138
|
+
</div>
|
|
139
|
+
|
|
140
|
+
<div className="flex min-w-0 flex-1 flex-col">{children}</div>
|
|
141
|
+
</div>
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* A fixed-size shimmer block. `radiusPx` only needs to be passed for the
|
|
147
|
+
* poster-sized blocks (12, matching `rounded-xl`) — every thinner text-line
|
|
148
|
+
* height here (13–18px) is less than twice that radius, so the browser clamps
|
|
149
|
+
* it to a pill automatically even left at the `Skeleton` default.
|
|
150
|
+
*/
|
|
151
|
+
function sk(width: string, height: number, radiusPx = 12) {
|
|
152
|
+
return (
|
|
153
|
+
<Skeleton
|
|
154
|
+
style={{ width, height: `${height}px`, borderRadius: `${radiusPx}px` }}
|
|
155
|
+
/>
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export type TimelineBandSkeletonProps = {
|
|
160
|
+
className?: string;
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Holds the lane/spine/content geometry so nothing jumps when data lands.
|
|
165
|
+
* The spine renders its hairline with no puck — there's no hour to show yet.
|
|
166
|
+
*/
|
|
167
|
+
export function TimelineBandSkeleton({ className }: TimelineBandSkeletonProps) {
|
|
168
|
+
return (
|
|
169
|
+
<div className={cn("flex items-stretch", className)} aria-hidden="true">
|
|
170
|
+
<div className="flex w-[var(--timeline-lane)] shrink-0 flex-col gap-3">
|
|
171
|
+
{sk("100%", 297, 12)}
|
|
172
|
+
{sk("70%", 18)}
|
|
173
|
+
{sk("45%", 13)}
|
|
174
|
+
</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 gap-4">
|
|
181
|
+
<div className="grid grid-cols-3 gap-[10px]">
|
|
182
|
+
{Array.from({ length: 3 }, (_, index) => (
|
|
183
|
+
<div key={index} className="flex flex-col gap-2">
|
|
184
|
+
{sk("100%", 190, 12)}
|
|
185
|
+
{sk("85%", 15)}
|
|
186
|
+
{sk("55%", 12)}
|
|
187
|
+
</div>
|
|
188
|
+
))}
|
|
189
|
+
</div>
|
|
190
|
+
|
|
191
|
+
<div className="flex flex-col">
|
|
192
|
+
{Array.from({ length: 3 }, (_, index) => (
|
|
193
|
+
<div key={index} className="relative flex items-center gap-[11px] py-2">
|
|
194
|
+
<Separator className="absolute inset-x-0 top-0" />
|
|
195
|
+
|
|
196
|
+
<Skeleton className="h-[34px] w-[34px] shrink-0 rounded-md" />
|
|
197
|
+
<div className="flex min-w-0 flex-1 flex-col gap-1.5">
|
|
198
|
+
{sk("60%", 13)}
|
|
199
|
+
{sk("40%", 11.5)}
|
|
200
|
+
</div>
|
|
201
|
+
</div>
|
|
202
|
+
))}
|
|
203
|
+
</div>
|
|
204
|
+
</div>
|
|
205
|
+
</div>
|
|
206
|
+
);
|
|
207
|
+
}
|
|
@@ -3,7 +3,7 @@ import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "./tool
|
|
|
3
3
|
import { Button } from "./button";
|
|
4
4
|
import { Plus } from "lucide-react";
|
|
5
5
|
|
|
6
|
-
const meta = {
|
|
6
|
+
const meta: Meta<typeof Tooltip> = {
|
|
7
7
|
title: "Components/Tooltip",
|
|
8
8
|
component: Tooltip,
|
|
9
9
|
tags: ["autodocs"],
|
|
@@ -14,7 +14,7 @@ const meta = {
|
|
|
14
14
|
</TooltipProvider>
|
|
15
15
|
),
|
|
16
16
|
],
|
|
17
|
-
}
|
|
17
|
+
};
|
|
18
18
|
|
|
19
19
|
export default meta;
|
|
20
20
|
type Story = StoryObj<typeof meta>;
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
|
+
import {
|
|
3
|
+
VenueEventsBlock,
|
|
4
|
+
VenueEventsBlockSkeleton,
|
|
5
|
+
type VenueEventsBlockDish,
|
|
6
|
+
type VenueEventsBlockNight,
|
|
7
|
+
} from "./venue-events-block";
|
|
8
|
+
|
|
9
|
+
const nights: VenueEventsBlockNight[] = [
|
|
10
|
+
{ id: "n1", dayLabel: "Fri", title: "Psihotrop — Infinity", timeLabel: "20:00", href: "/events/n1" },
|
|
11
|
+
{ id: "n2", dayLabel: "Sat", title: "Warehouse Sessions", timeLabel: "22:00", href: "/events/n2" },
|
|
12
|
+
{ id: "n3", dayLabel: "Sun", title: "Jazz at the Attic", timeLabel: "19:30", href: "/events/n3" },
|
|
13
|
+
];
|
|
14
|
+
|
|
15
|
+
const dishes: VenueEventsBlockDish[] = [
|
|
16
|
+
{
|
|
17
|
+
id: "d1",
|
|
18
|
+
name: "Smoked brisket burger",
|
|
19
|
+
description: "Slow-smoked 14h, pickled onion, house sauce",
|
|
20
|
+
priceLabel: "54 RON",
|
|
21
|
+
quantityLabel: "Serves 1",
|
|
22
|
+
mentionsLabel: "18",
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
id: "d2",
|
|
26
|
+
name: "Truffle fries",
|
|
27
|
+
description: "Parmesan, rosemary salt",
|
|
28
|
+
priceLabel: "28 RON",
|
|
29
|
+
mentionsLabel: "9",
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
id: "d3",
|
|
33
|
+
name: "Craft lager, 0.5l",
|
|
34
|
+
priceLabel: "22 RON",
|
|
35
|
+
},
|
|
36
|
+
];
|
|
37
|
+
|
|
38
|
+
// Explicitly annotated rather than `satisfies`: the meta-level decorator makes
|
|
39
|
+
// the inferred type reference storybook's internal CSF module, which tsc cannot
|
|
40
|
+
// name portably (TS2742).
|
|
41
|
+
const meta: Meta<typeof VenueEventsBlock> = {
|
|
42
|
+
title: "Events/VenueEventsBlock",
|
|
43
|
+
component: VenueEventsBlock,
|
|
44
|
+
tags: ["autodocs"],
|
|
45
|
+
args: {
|
|
46
|
+
venue: {
|
|
47
|
+
name: "Flying Circus",
|
|
48
|
+
ratingLabel: "4.7",
|
|
49
|
+
metaLabel: "pub · $$",
|
|
50
|
+
isOpenNow: true,
|
|
51
|
+
href: "/places/flying-circus",
|
|
52
|
+
},
|
|
53
|
+
nights,
|
|
54
|
+
allEventsLabel: "All 12 events at Flying Circus",
|
|
55
|
+
allEventsHref: "/places/flying-circus/events",
|
|
56
|
+
menuHeader: <h3 className="mb-1 text-sm font-bold">From the menu</h3>,
|
|
57
|
+
dishes,
|
|
58
|
+
footnote: "Menu prices last confirmed 2 days ago.",
|
|
59
|
+
},
|
|
60
|
+
decorators: [
|
|
61
|
+
(Story) => (
|
|
62
|
+
<div className="max-w-2xl">
|
|
63
|
+
<Story />
|
|
64
|
+
</div>
|
|
65
|
+
),
|
|
66
|
+
],
|
|
67
|
+
parameters: {
|
|
68
|
+
docs: {
|
|
69
|
+
description: {
|
|
70
|
+
component:
|
|
71
|
+
"'One venue, a whole fortnight' — the venue and its upcoming nights on the left, its menu on the right. Stacks to a single column on mobile, menu below.",
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
export default meta;
|
|
78
|
+
type Story = StoryObj<typeof VenueEventsBlock>;
|
|
79
|
+
|
|
80
|
+
export const Default: Story = {};
|
|
81
|
+
|
|
82
|
+
/** No thumbnail, not currently open, no rating yet. */
|
|
83
|
+
export const MinimalVenue: Story = {
|
|
84
|
+
args: {
|
|
85
|
+
venue: { name: "The Attic", metaLabel: "wine bar" },
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
/** Neither the venue header, night rows, nor the chip are links — a read-only summary block. */
|
|
90
|
+
export const NoLinks: Story = {
|
|
91
|
+
args: {
|
|
92
|
+
venue: { name: "Flying Circus", ratingLabel: "4.7", metaLabel: "pub · $$", isOpenNow: true },
|
|
93
|
+
nights: nights.map((night) => ({ ...night, href: undefined })),
|
|
94
|
+
allEventsHref: undefined,
|
|
95
|
+
},
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
export const Skeleton: Story = {
|
|
99
|
+
render: () => (
|
|
100
|
+
<div className="max-w-2xl">
|
|
101
|
+
<VenueEventsBlockSkeleton />
|
|
102
|
+
</div>
|
|
103
|
+
),
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* `menuContent` in use — a consuming app renders its OWN menu-row component
|
|
108
|
+
* here instead of the block's built-in dish row, so a dish looks identical
|
|
109
|
+
* whether it is met on this block, the venue's menu page, or anywhere else
|
|
110
|
+
* that app renders a row. `dishes` is omitted: it is ignored once
|
|
111
|
+
* `menuContent` is given.
|
|
112
|
+
*/
|
|
113
|
+
export const WithCustomMenuContent: Story = {
|
|
114
|
+
args: {
|
|
115
|
+
dishes: undefined,
|
|
116
|
+
menuContent: (
|
|
117
|
+
<ul>
|
|
118
|
+
{dishes.map((dish, index) => (
|
|
119
|
+
<li
|
|
120
|
+
key={dish.id}
|
|
121
|
+
className="relative flex items-start justify-between gap-3 border-2 border-dashed border-primary/40 py-3 first:border-t-0"
|
|
122
|
+
>
|
|
123
|
+
<div className="min-w-0">
|
|
124
|
+
<p className="text-[15px] font-semibold">{dish.name}</p>
|
|
125
|
+
<p className="text-xs text-muted-foreground">
|
|
126
|
+
Stand-in for the host app's own menu row, item {index + 1}
|
|
127
|
+
</p>
|
|
128
|
+
</div>
|
|
129
|
+
<span className="shrink-0 text-sm font-bold text-primary">{dish.priceLabel}</span>
|
|
130
|
+
</li>
|
|
131
|
+
))}
|
|
132
|
+
</ul>
|
|
133
|
+
),
|
|
134
|
+
},
|
|
135
|
+
parameters: {
|
|
136
|
+
docs: {
|
|
137
|
+
description: {
|
|
138
|
+
story:
|
|
139
|
+
"The DS renders only the layout column; the row markup itself belongs to whatever menu row the host app already owns.",
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
},
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* All three content slots in use at once — `venueContent`, `nightsContent`,
|
|
147
|
+
* and `menuContent` — the shape a real consuming app actually reaches for:
|
|
148
|
+
* its own place card up top, its own event row per night, its own menu row
|
|
149
|
+
* per dish. `venue`, `nights`, and `dishes` are all omitted; none of them are
|
|
150
|
+
* read once their slot is given. This is the block's real-world shape more
|
|
151
|
+
* than `Default` is — `Default` exists to prove the plain-data fallback still
|
|
152
|
+
* renders something reasonable on its own.
|
|
153
|
+
*/
|
|
154
|
+
export const WithAllContentSlots: Story = {
|
|
155
|
+
args: {
|
|
156
|
+
venue: undefined,
|
|
157
|
+
venueContent: (
|
|
158
|
+
<div className="flex gap-3 rounded-xl border-2 border-dashed border-primary/40 p-3">
|
|
159
|
+
<div className="h-[72px] w-28 shrink-0 rounded-lg bg-muted" />
|
|
160
|
+
<div className="min-w-0 flex-1">
|
|
161
|
+
<p className="text-sm font-semibold">Stand-in for the host app's own place card</p>
|
|
162
|
+
<p className="mt-1 line-clamp-2 text-xs text-muted-foreground">
|
|
163
|
+
Real photo, cuisine/price meta, and the place's description — the app's
|
|
164
|
+
canonical card, not a second one owned by this block.
|
|
165
|
+
</p>
|
|
166
|
+
</div>
|
|
167
|
+
</div>
|
|
168
|
+
),
|
|
169
|
+
nights: undefined,
|
|
170
|
+
nightsContent: (
|
|
171
|
+
<ul>
|
|
172
|
+
{nights.map((night, index) => (
|
|
173
|
+
<li
|
|
174
|
+
key={night.id}
|
|
175
|
+
className="relative flex items-center gap-3 border-2 border-dashed border-primary/40 py-2 first:border-t-0"
|
|
176
|
+
>
|
|
177
|
+
<div className="h-[34px] w-[34px] shrink-0 rounded-md bg-muted" />
|
|
178
|
+
<div className="min-w-0 flex-1">
|
|
179
|
+
<p className="truncate text-[13px] font-semibold">{night.title}</p>
|
|
180
|
+
<p className="truncate text-[11.5px] text-muted-foreground">
|
|
181
|
+
Stand-in for the host app's own event row, night {index + 1}
|
|
182
|
+
</p>
|
|
183
|
+
</div>
|
|
184
|
+
</li>
|
|
185
|
+
))}
|
|
186
|
+
</ul>
|
|
187
|
+
),
|
|
188
|
+
dishes: undefined,
|
|
189
|
+
menuContent: (
|
|
190
|
+
<ul>
|
|
191
|
+
{dishes.map((dish, index) => (
|
|
192
|
+
<li
|
|
193
|
+
key={dish.id}
|
|
194
|
+
className="relative flex items-start justify-between gap-3 border-2 border-dashed border-primary/40 py-3 first:border-t-0"
|
|
195
|
+
>
|
|
196
|
+
<div className="min-w-0">
|
|
197
|
+
<p className="text-[15px] font-semibold">{dish.name}</p>
|
|
198
|
+
<p className="text-xs text-muted-foreground">
|
|
199
|
+
Stand-in for the host app's own menu row, item {index + 1}
|
|
200
|
+
</p>
|
|
201
|
+
</div>
|
|
202
|
+
<span className="shrink-0 text-sm font-bold text-primary">{dish.priceLabel}</span>
|
|
203
|
+
</li>
|
|
204
|
+
))}
|
|
205
|
+
</ul>
|
|
206
|
+
),
|
|
207
|
+
},
|
|
208
|
+
parameters: {
|
|
209
|
+
docs: {
|
|
210
|
+
description: {
|
|
211
|
+
story:
|
|
212
|
+
"The block renders nothing but the two-column layout and the chip; every row shape — venue, night, dish — belongs to the host app.",
|
|
213
|
+
},
|
|
214
|
+
},
|
|
215
|
+
},
|
|
216
|
+
};
|