@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.
- package/dist/{chunk-WYTJR2YU.js → chunk-5CPBSNWQ.js} +6 -1
- package/dist/{chunk-54FQQZZJ.js → chunk-EQYB4Q77.js} +1 -1
- package/dist/chunk-LYREUZH6.js +156 -0
- package/dist/chunk-PW247TG2.js +81 -0
- 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/command.d.ts +15 -15
- package/dist/components/concept-compare-rail.js +2 -2
- package/dist/components/concept-compare.js +2 -2
- package/dist/components/event-card.d.ts +22 -4
- package/dist/components/event-card.js +95 -50
- package/dist/components/event-day-header.d.ts +5 -3
- package/dist/components/event-day-header.js +12 -4
- package/dist/components/event-door.d.ts +41 -0
- package/dist/components/event-door.js +65 -0
- package/dist/components/event-hub-hero.d.ts +10 -2
- package/dist/components/event-hub-hero.js +11 -5
- package/dist/components/event-tonight-strip.d.ts +50 -0
- package/dist/components/event-tonight-strip.js +67 -0
- package/dist/components/item.d.ts +2 -2
- package/dist/components/menu-hub-hero.d.ts +7 -1
- package/dist/components/menu-hub-hero.js +2 -1
- package/dist/components/place-card.js +1 -1
- package/dist/components/poster-stack.d.ts +36 -3
- package/dist/components/poster-stack.js +49 -18
- package/dist/components/snap-rail.d.ts +2 -2
- package/dist/components/snap-rail.js +1 -1
- package/dist/components/spinner.d.ts +2 -2
- package/dist/components/tag.d.ts +2 -2
- package/dist/components/text.d.ts +3 -3
- package/dist/components/timeline-day.d.ts +54 -0
- package/dist/components/timeline-day.js +65 -0
- package/dist/components/timeline-hour-puck.d.ts +47 -0
- package/dist/components/timeline-hour-puck.js +3 -0
- package/dist/components/timeline-hour.d.ts +96 -0
- package/dist/components/{timeline-band.js → timeline-hour.js} +2 -1
- package/dist/components/timeline-playhead.js +1 -3
- package/package.json +1 -1
- package/src/components/event-card.tsx +70 -13
- package/src/components/event-day-header.tsx +19 -7
- package/src/components/event-door.stories.tsx +88 -0
- package/src/components/event-door.tsx +97 -0
- package/src/components/event-hub-hero.tsx +21 -3
- package/src/components/event-tonight-strip.stories.tsx +78 -0
- package/src/components/event-tonight-strip.test.tsx +59 -0
- package/src/components/event-tonight-strip.tsx +134 -0
- package/src/components/menu-hub-hero.test.tsx +13 -0
- package/src/components/menu-hub-hero.tsx +9 -2
- package/src/components/poster-stack.stories.tsx +43 -1
- package/src/components/poster-stack.tsx +98 -19
- package/src/components/snap-rail.tsx +6 -1
- package/src/components/timeline-day.stories.tsx +201 -0
- package/src/components/timeline-day.test.tsx +78 -0
- package/src/components/timeline-day.tsx +114 -0
- package/src/components/timeline-hour-puck.tsx +139 -0
- package/src/components/timeline-hour.tsx +266 -0
- package/src/components/timeline-playhead.tsx +1 -1
- package/dist/chunk-NSNEANOQ.js +0 -104
- package/dist/components/hour-band.d.ts +0 -66
- package/dist/components/hour-band.js +0 -81
- package/dist/components/hour-group.d.ts +0 -67
- package/dist/components/hour-group.js +0 -59
- package/dist/components/timeline-band.d.ts +0 -64
- package/src/components/hour-band.stories.tsx +0 -182
- package/src/components/hour-band.tsx +0 -146
- package/src/components/hour-group.stories.tsx +0 -100
- package/src/components/hour-group.tsx +0 -124
- package/src/components/timeline-band.stories.tsx +0 -206
- package/src/components/timeline-band.tsx +0 -247
|
@@ -20,6 +20,7 @@ var EventCard = memo(function EventCard2({
|
|
|
20
20
|
formatters,
|
|
21
21
|
posterAspect = "4/3",
|
|
22
22
|
emphasis,
|
|
23
|
+
density = "card",
|
|
23
24
|
badge,
|
|
24
25
|
tagLabel,
|
|
25
26
|
tags,
|
|
@@ -39,7 +40,8 @@ var EventCard = memo(function EventCard2({
|
|
|
39
40
|
var _a;
|
|
40
41
|
const [imgError, setImgError] = useState(false);
|
|
41
42
|
const showEnded = isEnded && !!endedLabel;
|
|
42
|
-
const time = hideWhen ? "" : formatters.time(event.starts_at);
|
|
43
|
+
const time = hideWhen === true ? "" : formatters.time(event.starts_at);
|
|
44
|
+
const isRowLg = density === "row-lg";
|
|
43
45
|
const fee = resolveEventEntryFee(event);
|
|
44
46
|
const showPoster = !!event.poster_url && !imgError;
|
|
45
47
|
const venue = (_a = event.place_name) != null ? _a : event.location;
|
|
@@ -50,7 +52,9 @@ var EventCard = memo(function EventCard2({
|
|
|
50
52
|
className: cn(
|
|
51
53
|
"relative overflow-hidden rounded-xl bg-muted",
|
|
52
54
|
isEnded && "opacity-45",
|
|
53
|
-
posterAspect === "3/4" ? "aspect-[3/4]" : posterAspect === "4/5" ? "aspect-[4/5]" : "aspect-[4/3]"
|
|
55
|
+
posterAspect === "3/4" ? "aspect-[3/4]" : posterAspect === "4/5" ? "aspect-[4/5]" : "aspect-[4/3]",
|
|
56
|
+
// The compact row's poster from `lg`: 64px, portrait, tighter radius.
|
|
57
|
+
isRowLg && "lg:aspect-[4/5] lg:w-16 lg:shrink-0 lg:rounded-[10px]"
|
|
54
58
|
),
|
|
55
59
|
children: [
|
|
56
60
|
showPoster ? (
|
|
@@ -68,62 +72,103 @@ var EventCard = memo(function EventCard2({
|
|
|
68
72
|
}
|
|
69
73
|
)
|
|
70
74
|
) : fallback,
|
|
71
|
-
/* @__PURE__ */ jsxs(
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
),
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
75
|
+
/* @__PURE__ */ jsxs(
|
|
76
|
+
"div",
|
|
77
|
+
{
|
|
78
|
+
className: cn(
|
|
79
|
+
"absolute inset-x-2 top-2 flex items-start justify-between gap-2",
|
|
80
|
+
isRowLg && "lg:hidden"
|
|
81
|
+
),
|
|
82
|
+
children: [
|
|
83
|
+
badge ? /* @__PURE__ */ jsx("span", { className: cn(PILL_BASE, "bg-black/55 text-white"), children: badge }) : /* @__PURE__ */ jsx("span", {}),
|
|
84
|
+
fee.kind === "free" && /* @__PURE__ */ jsxs(
|
|
85
|
+
"span",
|
|
86
|
+
{
|
|
87
|
+
className: cn(
|
|
88
|
+
PILL_BASE,
|
|
89
|
+
"bg-success font-bold text-success-foreground"
|
|
90
|
+
),
|
|
91
|
+
children: [
|
|
92
|
+
/* @__PURE__ */ jsx(Gift, { className: "h-3 w-3", "aria-hidden": true }),
|
|
93
|
+
labels.free
|
|
94
|
+
]
|
|
95
|
+
}
|
|
96
|
+
)
|
|
97
|
+
]
|
|
98
|
+
}
|
|
99
|
+
),
|
|
100
|
+
tagLabel && /* @__PURE__ */ jsx(
|
|
101
|
+
"div",
|
|
102
|
+
{
|
|
103
|
+
className: cn(
|
|
104
|
+
"absolute inset-x-2 bottom-2 flex gap-1",
|
|
105
|
+
isRowLg && "lg:hidden"
|
|
106
|
+
),
|
|
107
|
+
children: /* @__PURE__ */ jsx("span", { className: cn(PILL_BASE, "bg-black/55 text-white"), children: tagLabel })
|
|
108
|
+
}
|
|
109
|
+
)
|
|
88
110
|
]
|
|
89
111
|
}
|
|
90
112
|
),
|
|
91
|
-
/* @__PURE__ */
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
113
|
+
/* @__PURE__ */ jsxs("div", { className: cn("flex min-w-0 flex-col", isRowLg && "lg:flex-1"), children: [
|
|
114
|
+
/* @__PURE__ */ jsx(
|
|
115
|
+
"h3",
|
|
116
|
+
{
|
|
117
|
+
className: cn(
|
|
118
|
+
"mt-2.5 leading-[1.35]",
|
|
119
|
+
emphasis === true ? "text-lg font-bold tracking-[-0.02em]" : "text-sm font-semibold",
|
|
120
|
+
emphasis === "lg" && "lg:text-lg lg:font-bold lg:tracking-[-0.02em]",
|
|
121
|
+
isRowLg && "lg:mt-0 lg:text-[13.5px]",
|
|
122
|
+
isEnded && "text-muted-foreground"
|
|
123
|
+
),
|
|
124
|
+
children: event.title
|
|
125
|
+
}
|
|
126
|
+
),
|
|
127
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-0.5 flex min-w-0 items-center gap-1 text-xs font-medium text-foreground/85", children: [
|
|
128
|
+
/* @__PURE__ */ jsx(MapPin, { className: "h-3 w-3 shrink-0", "aria-hidden": true }),
|
|
129
|
+
/* @__PURE__ */ jsx("span", { className: "truncate", children: venue })
|
|
130
|
+
] }),
|
|
131
|
+
hideWhen !== true && /* @__PURE__ */ jsxs(
|
|
132
|
+
"div",
|
|
133
|
+
{
|
|
134
|
+
className: cn(
|
|
135
|
+
"mt-[3px] flex items-center gap-1.5 text-xs text-muted-foreground",
|
|
136
|
+
hideWhen === "below-lg" && "hidden lg:flex"
|
|
137
|
+
),
|
|
138
|
+
children: [
|
|
139
|
+
/* @__PURE__ */ jsx("span", { children: dayLabel != null ? dayLabel : formatters.day(event.starts_at) }),
|
|
140
|
+
time && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
141
|
+
/* @__PURE__ */ jsx("span", { className: "text-border", "aria-hidden": true, children: "\xB7" }),
|
|
142
|
+
/* @__PURE__ */ jsx("span", { children: time })
|
|
143
|
+
] }),
|
|
144
|
+
showPrice && fee.kind === "paid" && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
145
|
+
/* @__PURE__ */ jsx("span", { className: "text-border", "aria-hidden": true, children: "\xB7" }),
|
|
146
|
+
/* @__PURE__ */ jsx("span", { children: formatters.price(fee.amountRon) })
|
|
147
|
+
] })
|
|
148
|
+
]
|
|
149
|
+
}
|
|
150
|
+
),
|
|
151
|
+
(tags && tags.length > 0 || isRowLg && fee.kind === "free") && /* @__PURE__ */ jsxs(TagGroup, { className: "mt-1.5", children: [
|
|
152
|
+
tags == null ? void 0 : tags.map((tag) => /* @__PURE__ */ jsx(Tag, { size: "sm", children: tag }, tag)),
|
|
153
|
+
isRowLg && fee.kind === "free" && /* @__PURE__ */ jsxs(Tag, { size: "sm", variant: "success", className: "hidden font-bold lg:inline-flex", children: [
|
|
154
|
+
/* @__PURE__ */ jsx(Gift, { className: "h-3 w-3", "aria-hidden": true }),
|
|
155
|
+
labels.free
|
|
156
|
+
] })
|
|
157
|
+
] }),
|
|
158
|
+
liveStatus && /* @__PURE__ */ jsxs("div", { className: "mt-1 flex items-center gap-1.5 text-xs font-bold text-success", children: [
|
|
159
|
+
/* @__PURE__ */ jsx(Radio, { className: "h-3 w-3 shrink-0", "aria-hidden": true }),
|
|
160
|
+
liveStatus
|
|
111
161
|
] }),
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
/* @__PURE__ */ jsx("span", { children: formatters.price(fee.amountRon) })
|
|
115
|
-
] })
|
|
116
|
-
] }),
|
|
117
|
-
tags && tags.length > 0 && /* @__PURE__ */ jsx(TagGroup, { className: "mt-1.5", children: tags.map((tag) => /* @__PURE__ */ jsx(Tag, { size: "sm", children: tag }, tag)) }),
|
|
118
|
-
liveStatus && /* @__PURE__ */ jsxs("div", { className: "mt-1 flex items-center gap-1.5 text-xs font-bold text-success", children: [
|
|
119
|
-
/* @__PURE__ */ jsx(Radio, { className: "h-3 w-3 shrink-0", "aria-hidden": true }),
|
|
120
|
-
liveStatus
|
|
121
|
-
] }),
|
|
122
|
-
showEnded && /* @__PURE__ */ jsx("div", { className: "mt-1.5", children: /* @__PURE__ */ jsx(Tag, { size: "sm", variant: "default", className: "font-extrabold", children: endedLabel }) })
|
|
162
|
+
showEnded && /* @__PURE__ */ jsx("div", { className: "mt-1.5", children: /* @__PURE__ */ jsx(Tag, { size: "sm", variant: "default", className: "font-extrabold", children: endedLabel }) })
|
|
163
|
+
] })
|
|
123
164
|
] });
|
|
124
165
|
const rootClasses = cn(
|
|
125
166
|
"flex min-w-0 flex-col text-left",
|
|
167
|
+
// The compact row from `lg`: poster beside text, the row's own padding
|
|
168
|
+
// and negative gutter so the hover wash bleeds like `EventCompactRow`'s.
|
|
169
|
+
isRowLg && "lg:-mx-2.5 lg:flex-row lg:items-start lg:gap-[11px] lg:rounded-lg lg:px-2.5 lg:py-2",
|
|
126
170
|
(href || onClick) && "cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 rounded-xl",
|
|
171
|
+
(href || onClick) && isRowLg && "lg:hover:bg-foreground/5",
|
|
127
172
|
className
|
|
128
173
|
);
|
|
129
174
|
if (href) {
|
|
@@ -37,10 +37,12 @@ type EventDayHeaderProps = {
|
|
|
37
37
|
*/
|
|
38
38
|
action?: React.ReactNode;
|
|
39
39
|
/**
|
|
40
|
-
*
|
|
41
|
-
*
|
|
40
|
+
* `sm`: the phone scale (30px day number, 15px weekday, 11.5px meta).
|
|
41
|
+
* `default`: the desktop scale (58 / 18 / 12.5). `responsive`: `sm` below
|
|
42
|
+
* `lg` and `default` from it, in one node — for a server render that
|
|
43
|
+
* cannot know the viewport.
|
|
42
44
|
*/
|
|
43
|
-
size?: "sm" | "default";
|
|
45
|
+
size?: "sm" | "default" | "responsive";
|
|
44
46
|
href?: string;
|
|
45
47
|
linkComponent?: React.ComponentType<EventDayHeaderLinkProps>;
|
|
46
48
|
onClick?: () => void;
|
|
@@ -21,6 +21,7 @@ function EventDayHeader({
|
|
|
21
21
|
}) {
|
|
22
22
|
const isInteractive = !!href || !!onClick;
|
|
23
23
|
const large = size === "default";
|
|
24
|
+
const responsive = size === "responsive";
|
|
24
25
|
const body = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
25
26
|
/* @__PURE__ */ jsx(
|
|
26
27
|
"div",
|
|
@@ -29,16 +30,21 @@ function EventDayHeader({
|
|
|
29
30
|
"font-black leading-[0.82] tracking-[-0.05em] tabular-nums",
|
|
30
31
|
// `sm` is the phone scale: 30px, the design's app day head. 40px
|
|
31
32
|
// was a page-scale number that took a third of a 390px row.
|
|
32
|
-
large ? "text-[58px]" : "text-[30px] leading-[0.86]"
|
|
33
|
+
large ? "text-[58px]" : "text-[30px] leading-[0.86]",
|
|
34
|
+
responsive && "lg:text-[58px] lg:leading-[0.82]"
|
|
33
35
|
),
|
|
34
36
|
children: dayNumber
|
|
35
37
|
}
|
|
36
38
|
),
|
|
37
|
-
/* @__PURE__ */ jsxs("div", { className: large ? "pb-1" : "pb-0.5", children: [
|
|
39
|
+
/* @__PURE__ */ jsxs("div", { className: cn(large ? "pb-1" : "pb-0.5", responsive && "lg:pb-1"), children: [
|
|
38
40
|
/* @__PURE__ */ jsx(
|
|
39
41
|
"div",
|
|
40
42
|
{
|
|
41
|
-
className: cn(
|
|
43
|
+
className: cn(
|
|
44
|
+
"font-extrabold tracking-[-0.015em]",
|
|
45
|
+
large ? "text-lg" : "text-[15px]",
|
|
46
|
+
responsive && "lg:text-lg"
|
|
47
|
+
),
|
|
42
48
|
children: weekday
|
|
43
49
|
}
|
|
44
50
|
),
|
|
@@ -47,7 +53,8 @@ function EventDayHeader({
|
|
|
47
53
|
{
|
|
48
54
|
className: cn(
|
|
49
55
|
"mt-0.5 text-muted-foreground",
|
|
50
|
-
large ? "whitespace-nowrap text-[12.5px]" : "text-[11.5px]"
|
|
56
|
+
large ? "whitespace-nowrap text-[12.5px]" : "text-[11.5px]",
|
|
57
|
+
responsive && "lg:whitespace-nowrap lg:text-[12.5px]"
|
|
51
58
|
),
|
|
52
59
|
children: meta
|
|
53
60
|
}
|
|
@@ -67,6 +74,7 @@ function EventDayHeader({
|
|
|
67
74
|
className: cn(
|
|
68
75
|
"relative flex w-full items-end gap-3 pb-3 pt-[18px]",
|
|
69
76
|
large && "gap-[18px] pb-4",
|
|
77
|
+
responsive && "lg:gap-[18px] lg:pb-4",
|
|
70
78
|
className
|
|
71
79
|
),
|
|
72
80
|
children: [
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* EventDoor — a square tile that is a category's entry point: a photograph of
|
|
6
|
+
* the KIND of night (a stage, a dance floor, a terrace), a scrim, the name.
|
|
7
|
+
*
|
|
8
|
+
* A door and an event row must not look alike. `CategoryDeck` fans four event
|
|
9
|
+
* posters and prints a count, which reads as "these four events"; a card with
|
|
10
|
+
* a single event's poster reads as "tap = that event" and changes daily. The
|
|
11
|
+
* 1:1 shape with the name INSIDE the image is what says "door, not event", and
|
|
12
|
+
* it carries no count — a count is a claim about scale the hub's figures
|
|
13
|
+
* already make, and on a door it competes with the name.
|
|
14
|
+
*
|
|
15
|
+
* Always an anchor: a door with no destination is not a door, so `href` is
|
|
16
|
+
* required. The scrim is a gradient over a photograph, which is mode-
|
|
17
|
+
* independent — the same measured treatment `DoorwayTiles` uses.
|
|
18
|
+
*/
|
|
19
|
+
type EventDoorLinkProps = {
|
|
20
|
+
href: string;
|
|
21
|
+
className?: string;
|
|
22
|
+
children: React.ReactNode;
|
|
23
|
+
onClick?: (mouseEvent: React.MouseEvent) => void;
|
|
24
|
+
};
|
|
25
|
+
type EventDoorProps = {
|
|
26
|
+
/** The category's name — the anchor's accessible name and the tile's label. */
|
|
27
|
+
name: string;
|
|
28
|
+
href: string;
|
|
29
|
+
imageSrc?: string;
|
|
30
|
+
/** Rendered when `imageSrc` is absent or fails. Defaults to `<EventPosterFallback />`. */
|
|
31
|
+
fallback?: React.ReactNode;
|
|
32
|
+
linkComponent?: React.ComponentType<EventDoorLinkProps>;
|
|
33
|
+
onClick?: (mouseEvent: React.MouseEvent) => void;
|
|
34
|
+
className?: string;
|
|
35
|
+
};
|
|
36
|
+
declare function EventDoor({ name, href, imageSrc, fallback, linkComponent: LinkComponent, onClick, className, }: EventDoorProps): react_jsx_runtime.JSX.Element;
|
|
37
|
+
declare function EventDoorSkeleton({ className }: {
|
|
38
|
+
className?: string;
|
|
39
|
+
}): react_jsx_runtime.JSX.Element;
|
|
40
|
+
|
|
41
|
+
export { EventDoor, type EventDoorLinkProps, type EventDoorProps, EventDoorSkeleton };
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { EventPosterFallback } from '../chunk-FDITZ2VM.js';
|
|
2
|
+
import { Skeleton } from '../chunk-XBBEX4SF.js';
|
|
3
|
+
import { cn } from '../chunk-FEK5XGZW.js';
|
|
4
|
+
import { __objRest, __spreadValues } from '../chunk-MMUBH76A.js';
|
|
5
|
+
import { useState } from 'react';
|
|
6
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
7
|
+
|
|
8
|
+
function DefaultLink(_a) {
|
|
9
|
+
var _b = _a, { href } = _b, props = __objRest(_b, ["href"]);
|
|
10
|
+
return /* @__PURE__ */ jsx("a", __spreadValues({ href }, props));
|
|
11
|
+
}
|
|
12
|
+
function EventDoor({
|
|
13
|
+
name,
|
|
14
|
+
href,
|
|
15
|
+
imageSrc,
|
|
16
|
+
fallback = /* @__PURE__ */ jsx(EventPosterFallback, {}),
|
|
17
|
+
linkComponent: LinkComponent = DefaultLink,
|
|
18
|
+
onClick,
|
|
19
|
+
className
|
|
20
|
+
}) {
|
|
21
|
+
const [failed, setFailed] = useState(false);
|
|
22
|
+
const showImage = !!imageSrc && !failed;
|
|
23
|
+
return /* @__PURE__ */ jsxs(
|
|
24
|
+
LinkComponent,
|
|
25
|
+
{
|
|
26
|
+
href,
|
|
27
|
+
onClick,
|
|
28
|
+
className: cn(
|
|
29
|
+
"group relative block aspect-square overflow-hidden rounded-xl bg-muted",
|
|
30
|
+
"transition-transform duration-150 active:scale-[0.98] motion-reduce:transition-none",
|
|
31
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
|
|
32
|
+
className
|
|
33
|
+
),
|
|
34
|
+
children: [
|
|
35
|
+
showImage ? (
|
|
36
|
+
// eslint-disable-next-line @next/next/no-img-element
|
|
37
|
+
/* @__PURE__ */ jsx(
|
|
38
|
+
"img",
|
|
39
|
+
{
|
|
40
|
+
src: imageSrc,
|
|
41
|
+
alt: "",
|
|
42
|
+
loading: "lazy",
|
|
43
|
+
decoding: "async",
|
|
44
|
+
className: "h-full w-full object-cover transition-transform duration-300 group-hover:scale-[1.04] motion-reduce:transition-none",
|
|
45
|
+
onError: () => setFailed(true)
|
|
46
|
+
}
|
|
47
|
+
)
|
|
48
|
+
) : fallback,
|
|
49
|
+
/* @__PURE__ */ jsx(
|
|
50
|
+
"span",
|
|
51
|
+
{
|
|
52
|
+
"aria-hidden": true,
|
|
53
|
+
className: "absolute inset-x-0 bottom-0 h-3/4 bg-gradient-to-t from-black/65 via-black/45 to-transparent"
|
|
54
|
+
}
|
|
55
|
+
),
|
|
56
|
+
/* @__PURE__ */ jsx("span", { className: "absolute inset-x-2 bottom-2 line-clamp-2 text-[12.5px] font-bold leading-[1.2] tracking-[-0.01em] text-white", children: name })
|
|
57
|
+
]
|
|
58
|
+
}
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
function EventDoorSkeleton({ className }) {
|
|
62
|
+
return /* @__PURE__ */ jsx(Skeleton, { className: cn("aspect-square w-full rounded-xl", className) });
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export { EventDoor, EventDoorSkeleton };
|
|
@@ -42,14 +42,22 @@ type EventHubHeroProps = {
|
|
|
42
42
|
statsLabel?: string;
|
|
43
43
|
/** The right-column slot — the poster stack in the source design. */
|
|
44
44
|
aside?: React.ReactNode;
|
|
45
|
+
/**
|
|
46
|
+
* The text column's last row, under the figures — the hub's tonight state
|
|
47
|
+
* strip. The figures say scale; this says what is on right now, and it is
|
|
48
|
+
* the one thing on the first screen a reader can tap into the timeline.
|
|
49
|
+
*/
|
|
50
|
+
footer?: React.ReactNode;
|
|
45
51
|
className?: string;
|
|
46
52
|
};
|
|
47
|
-
declare function EventHubHero({ eyebrow, title, intro, stats, statsLabel, aside, className, }: EventHubHeroProps): react_jsx_runtime.JSX.Element;
|
|
53
|
+
declare function EventHubHero({ eyebrow, title, intro, stats, statsLabel, aside, footer, className, }: EventHubHeroProps): react_jsx_runtime.JSX.Element;
|
|
48
54
|
type EventHubHeroSkeletonProps = {
|
|
49
55
|
/** The right-column slot, e.g. a `PosterStack`-shaped placeholder. Mirrors `aside` on the real component. */
|
|
50
56
|
aside?: React.ReactNode;
|
|
57
|
+
/** Reserves the `footer` row — the tonight strip's 52px. */
|
|
58
|
+
footer?: boolean;
|
|
51
59
|
className?: string;
|
|
52
60
|
};
|
|
53
|
-
declare function EventHubHeroSkeleton({ aside, className }: EventHubHeroSkeletonProps): react_jsx_runtime.JSX.Element;
|
|
61
|
+
declare function EventHubHeroSkeleton({ aside, footer, className }: EventHubHeroSkeletonProps): react_jsx_runtime.JSX.Element;
|
|
54
62
|
|
|
55
63
|
export { EventHubHero, type EventHubHeroProps, EventHubHeroSkeleton, type EventHubHeroSkeletonProps, type EventHubHeroStat };
|
|
@@ -10,13 +10,17 @@ function EventHubHero({
|
|
|
10
10
|
stats,
|
|
11
11
|
statsLabel,
|
|
12
12
|
aside,
|
|
13
|
+
footer,
|
|
13
14
|
className
|
|
14
15
|
}) {
|
|
15
16
|
return /* @__PURE__ */ jsxs(
|
|
16
17
|
"div",
|
|
17
18
|
{
|
|
18
19
|
className: cn(
|
|
19
|
-
|
|
20
|
+
// 16px between the text column and the aside on a phone: with a
|
|
21
|
+
// tappable poster stack the aside is content, not decoration, and
|
|
22
|
+
// gets the within-section rhythm rather than a section gap.
|
|
23
|
+
"grid grid-cols-1 gap-4 lg:grid-cols-[1fr_620px] lg:items-center lg:gap-10",
|
|
20
24
|
className
|
|
21
25
|
),
|
|
22
26
|
children: [
|
|
@@ -82,20 +86,21 @@ function EventHubHero({
|
|
|
82
86
|
);
|
|
83
87
|
})
|
|
84
88
|
}
|
|
85
|
-
)
|
|
89
|
+
),
|
|
90
|
+
footer && /* @__PURE__ */ jsx("div", { className: "order-4 pt-1.5 lg:order-none lg:pt-2", children: footer })
|
|
86
91
|
] }),
|
|
87
92
|
aside && /* @__PURE__ */ jsx("div", { className: "min-w-0", children: aside })
|
|
88
93
|
]
|
|
89
94
|
}
|
|
90
95
|
);
|
|
91
96
|
}
|
|
92
|
-
function EventHubHeroSkeleton({ aside, className }) {
|
|
97
|
+
function EventHubHeroSkeleton({ aside, footer, className }) {
|
|
93
98
|
return /* @__PURE__ */ jsxs(
|
|
94
99
|
"div",
|
|
95
100
|
{
|
|
96
101
|
"aria-hidden": "true",
|
|
97
102
|
className: cn(
|
|
98
|
-
"grid grid-cols-1 gap-
|
|
103
|
+
"grid grid-cols-1 gap-4 lg:grid-cols-[1fr_620px] lg:items-center lg:gap-10",
|
|
99
104
|
className
|
|
100
105
|
),
|
|
101
106
|
children: [
|
|
@@ -116,7 +121,8 @@ function EventHubHeroSkeleton({ aside, className }) {
|
|
|
116
121
|
]
|
|
117
122
|
},
|
|
118
123
|
index
|
|
119
|
-
)) })
|
|
124
|
+
)) }),
|
|
125
|
+
footer && /* @__PURE__ */ jsx("div", { className: "order-4 pt-1.5 lg:order-none lg:pt-2", children: /* @__PURE__ */ jsx(Skeleton, { className: "h-[52px] w-full rounded-[14px]" }) })
|
|
120
126
|
] }),
|
|
121
127
|
aside && /* @__PURE__ */ jsx("div", { className: "min-w-0", children: aside })
|
|
122
128
|
]
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* EventTonightStrip — the hub's state line: what is on right now, what is
|
|
6
|
+
* later tonight, and one tap into the timeline.
|
|
7
|
+
*
|
|
8
|
+
* The hero's figures say SCALE ("52 events · 14 days"); this says STATE ("3
|
|
9
|
+
* live now · 12 more tonight · next at 20:00"). It is the one thing on the
|
|
10
|
+
* hub's first screen a reader can tap into today's programme, so it is a real
|
|
11
|
+
* anchor whenever there is a day to open — never a sheet trigger.
|
|
12
|
+
*
|
|
13
|
+
* Copy is caller-composed and carries only absolute facts: counts and
|
|
14
|
+
* wall-clock start times. Never a countdown or "in 12 minutes" — the hub is
|
|
15
|
+
* ISR-cached, and a ten-minute-old snapshot that says "next at 20:00" is at
|
|
16
|
+
* worst one boundary off, while "in 12 minutes" is simply wrong. The DS keeps
|
|
17
|
+
* no clock, no Intl and no i18n: the app derives `tone`, `lead` and `details`
|
|
18
|
+
* from its own clock and re-derives them after hydration.
|
|
19
|
+
*
|
|
20
|
+
* The lead stays `text-foreground`, not `--success`: 13px bold success on a
|
|
21
|
+
* card computes to roughly 4.5:1 in both modes, too close to the floor for
|
|
22
|
+
* the strip's one claim. The dot and the border carry the colour.
|
|
23
|
+
*/
|
|
24
|
+
type EventTonightStripTone = "live" | "tonight" | "quiet" | "none";
|
|
25
|
+
type EventTonightStripLinkProps = {
|
|
26
|
+
href: string;
|
|
27
|
+
className?: string;
|
|
28
|
+
children: React.ReactNode;
|
|
29
|
+
onClick?: (mouseEvent: React.MouseEvent) => void;
|
|
30
|
+
};
|
|
31
|
+
type EventTonightStripProps = {
|
|
32
|
+
tone: EventTonightStripTone;
|
|
33
|
+
/** The lead segment, bold — "3 live now". */
|
|
34
|
+
lead: string;
|
|
35
|
+
/** Trailing segments, muted, joined with " · " — ["12 more tonight", "next at 20:00"]. */
|
|
36
|
+
details?: string[];
|
|
37
|
+
/** Screen-reader suffix for the anchor — "Open tonight in the schedule". */
|
|
38
|
+
actionLabel?: string;
|
|
39
|
+
/** Absent (tone `none`) renders a non-interactive `role="status"` line. */
|
|
40
|
+
href?: string;
|
|
41
|
+
/** Defaults to a plain `<a>`. Pass `next/link` in a Next app. */
|
|
42
|
+
linkComponent?: React.ComponentType<EventTonightStripLinkProps>;
|
|
43
|
+
className?: string;
|
|
44
|
+
};
|
|
45
|
+
declare function EventTonightStrip({ tone, lead, details, actionLabel, href, linkComponent: LinkComponent, className, }: EventTonightStripProps): react_jsx_runtime.JSX.Element;
|
|
46
|
+
declare function EventTonightStripSkeleton({ className }: {
|
|
47
|
+
className?: string;
|
|
48
|
+
}): react_jsx_runtime.JSX.Element;
|
|
49
|
+
|
|
50
|
+
export { EventTonightStrip, type EventTonightStripLinkProps, type EventTonightStripProps, EventTonightStripSkeleton, type EventTonightStripTone };
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { Skeleton } from '../chunk-XBBEX4SF.js';
|
|
2
|
+
import { cn } from '../chunk-FEK5XGZW.js';
|
|
3
|
+
import { __objRest, __spreadValues } from '../chunk-MMUBH76A.js';
|
|
4
|
+
import * as React from 'react';
|
|
5
|
+
import { Clock, CalendarDays, ArrowRight } from 'lucide-react';
|
|
6
|
+
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
7
|
+
|
|
8
|
+
function DefaultLink(_a) {
|
|
9
|
+
var _b = _a, { href } = _b, props = __objRest(_b, ["href"]);
|
|
10
|
+
return /* @__PURE__ */ jsx("a", __spreadValues({ href }, props));
|
|
11
|
+
}
|
|
12
|
+
function EventTonightStrip({
|
|
13
|
+
tone,
|
|
14
|
+
lead,
|
|
15
|
+
details = [],
|
|
16
|
+
actionLabel,
|
|
17
|
+
href,
|
|
18
|
+
linkComponent: LinkComponent = DefaultLink,
|
|
19
|
+
className
|
|
20
|
+
}) {
|
|
21
|
+
const interactive = !!href;
|
|
22
|
+
const mark = tone === "live" ? /* @__PURE__ */ jsxs("span", { className: "relative flex h-2.5 w-2.5", "aria-hidden": true, children: [
|
|
23
|
+
/* @__PURE__ */ jsx("span", { className: "absolute inline-flex h-full w-full rounded-full bg-success/40 motion-live-pulse motion-reduce:animate-none" }),
|
|
24
|
+
/* @__PURE__ */ jsx("span", { className: "relative inline-flex h-2.5 w-2.5 rounded-full bg-success" })
|
|
25
|
+
] }) : tone === "tonight" ? /* @__PURE__ */ jsx(Clock, { className: "h-4 w-4 text-muted-foreground", "aria-hidden": true }) : /* @__PURE__ */ jsx(
|
|
26
|
+
CalendarDays,
|
|
27
|
+
{
|
|
28
|
+
className: cn("h-4 w-4", tone === "none" ? "text-muted-foreground/70" : "text-muted-foreground"),
|
|
29
|
+
"aria-hidden": true
|
|
30
|
+
}
|
|
31
|
+
);
|
|
32
|
+
const body = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
33
|
+
/* @__PURE__ */ jsx("span", { className: "flex h-4 w-4 shrink-0 items-center justify-center", children: mark }),
|
|
34
|
+
/* @__PURE__ */ jsxs("span", { className: "line-clamp-2 min-w-0 flex-1 text-[13px] leading-[1.3] tabular-nums lg:line-clamp-1 lg:text-sm", children: [
|
|
35
|
+
/* @__PURE__ */ jsx("span", { className: "font-bold text-foreground", children: lead }),
|
|
36
|
+
details.map((detail) => /* @__PURE__ */ jsxs(React.Fragment, { children: [
|
|
37
|
+
" ",
|
|
38
|
+
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground", "aria-hidden": true, children: "\xB7" }),
|
|
39
|
+
" ",
|
|
40
|
+
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: detail })
|
|
41
|
+
] }, detail)),
|
|
42
|
+
interactive && actionLabel && /* @__PURE__ */ jsxs("span", { className: "sr-only", children: [
|
|
43
|
+
". ",
|
|
44
|
+
actionLabel
|
|
45
|
+
] })
|
|
46
|
+
] }),
|
|
47
|
+
interactive && /* @__PURE__ */ jsx(ArrowRight, { className: "h-4 w-4 shrink-0 text-muted-foreground", "aria-hidden": true })
|
|
48
|
+
] });
|
|
49
|
+
const rootClasses = cn(
|
|
50
|
+
"flex min-h-[52px] w-full items-center gap-2.5 rounded-[14px] border bg-card px-3.5 py-2 text-left",
|
|
51
|
+
tone === "live" ? "border-success/40" : "border-border",
|
|
52
|
+
interactive && cn(
|
|
53
|
+
"cursor-pointer transition-colors duration-150 hover:bg-accent-subtle active:scale-[0.99] motion-reduce:transition-none",
|
|
54
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2"
|
|
55
|
+
),
|
|
56
|
+
className
|
|
57
|
+
);
|
|
58
|
+
if (href) {
|
|
59
|
+
return /* @__PURE__ */ jsx(LinkComponent, { href, className: rootClasses, children: body });
|
|
60
|
+
}
|
|
61
|
+
return /* @__PURE__ */ jsx("div", { role: "status", className: rootClasses, children: body });
|
|
62
|
+
}
|
|
63
|
+
function EventTonightStripSkeleton({ className }) {
|
|
64
|
+
return /* @__PURE__ */ jsx(Skeleton, { className: cn("h-[52px] w-full rounded-[14px]", className) });
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export { EventTonightStrip, EventTonightStripSkeleton };
|
|
@@ -3,12 +3,12 @@ import * as React from 'react';
|
|
|
3
3
|
import { VariantProps } from 'class-variance-authority';
|
|
4
4
|
|
|
5
5
|
declare const itemVariants: (props?: ({
|
|
6
|
-
variant?: "default" | "
|
|
6
|
+
variant?: "default" | "outline" | "muted" | 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 Item: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLDivElement> & VariantProps<(props?: ({
|
|
11
|
-
variant?: "default" | "
|
|
11
|
+
variant?: "default" | "outline" | "muted" | 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> & {
|
|
@@ -58,8 +58,14 @@ type MenuHubHeroProps = {
|
|
|
58
58
|
* a 1440px hero.
|
|
59
59
|
*/
|
|
60
60
|
tileUrls?: string[];
|
|
61
|
+
/**
|
|
62
|
+
* The element the headline renders as. `h1` by default; a page that
|
|
63
|
+
* already owns its `<h1>` (an sr-only SEO heading, as on `/menu`) passes
|
|
64
|
+
* `h2` so the document has one h1, not two.
|
|
65
|
+
*/
|
|
66
|
+
headingLevel?: "h1" | "h2";
|
|
61
67
|
className?: string;
|
|
62
68
|
};
|
|
63
|
-
declare function MenuHubHero({ kicker, kickerIcon, headline, body, figures, proof, proofIcon, action, backgroundUrl, tileUrls, className, }: MenuHubHeroProps): react_jsx_runtime.JSX.Element;
|
|
69
|
+
declare function MenuHubHero({ kicker, kickerIcon, headline, body, figures, proof, proofIcon, action, backgroundUrl, tileUrls, headingLevel: Heading, className, }: MenuHubHeroProps): react_jsx_runtime.JSX.Element;
|
|
64
70
|
|
|
65
71
|
export { MenuHubHero, type MenuHubHeroFigure, type MenuHubHeroProps };
|
|
@@ -15,6 +15,7 @@ function MenuHubHero({
|
|
|
15
15
|
action,
|
|
16
16
|
backgroundUrl,
|
|
17
17
|
tileUrls,
|
|
18
|
+
headingLevel: Heading = "h1",
|
|
18
19
|
className
|
|
19
20
|
}) {
|
|
20
21
|
return /* @__PURE__ */ jsxs(
|
|
@@ -85,7 +86,7 @@ function MenuHubHero({
|
|
|
85
86
|
kickerIcon,
|
|
86
87
|
kicker
|
|
87
88
|
] }),
|
|
88
|
-
/* @__PURE__ */ jsx(
|
|
89
|
+
/* @__PURE__ */ jsx(Heading, { className: "max-w-[640px] font-serif text-[28px] font-semibold leading-[1.06] tracking-[-0.025em] lg:text-[50px] lg:leading-[1.04]", children: headline }),
|
|
89
90
|
body && /* @__PURE__ */ jsx("p", { className: "hidden max-w-[540px] text-sm leading-[1.6] opacity-[0.84] lg:block lg:text-[15.5px]", children: body }),
|
|
90
91
|
figures && figures.length > 0 && // One row of four on the phone. It was two by two, which was the
|
|
91
92
|
// taller half of a 500px hero; at 20px the widest figure ("19,090")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { NEUTRAL_ICON_FILTER } from '../chunk-LQWGZIYA.js';
|
|
1
2
|
import { OVERLAY_PILL } from '../chunk-EZG2NELX.js';
|
|
2
3
|
import { HOVER_IMAGE_MOTION, HOVER_IMAGE_WRAPPER, HOVER_MOTION, HOVER_LIFT_GRID_GROUP } from '../chunk-XCB3F6SF.js';
|
|
3
|
-
import { NEUTRAL_ICON_FILTER } from '../chunk-LQWGZIYA.js';
|
|
4
4
|
import { GradeAtom } from '../chunk-DJD3EMPK.js';
|
|
5
5
|
import { showsGradeAtom } from '../chunk-I5V446BX.js';
|
|
6
6
|
import { Separator } from '../chunk-NWZGSLPU.js';
|