@bearmenu/ui 0.8.9 → 0.8.11
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-24RFI4RQ.js +167 -0
- package/dist/{chunk-UKBNQO6Q.js → chunk-2CXU5WZO.js} +1 -1
- package/dist/chunk-2XCZJWTV.js +121 -0
- package/dist/{chunk-62ES6N32.js → chunk-43RC7MHY.js} +1 -1
- package/dist/chunk-5RWTOT5Y.js +72 -0
- package/dist/{chunk-Q6QH52LJ.js → chunk-6TEEY5YA.js} +2 -2
- package/dist/{chunk-AXV5O2IZ.js → chunk-AKQ57NXY.js} +3 -3
- package/dist/chunk-CLLY6XB3.js +41 -0
- package/dist/chunk-DTXNDLSE.js +125 -0
- package/dist/chunk-F6FMZJDG.js +113 -0
- package/dist/{chunk-2WDJR6K7.js → chunk-G7IFL4Z4.js} +4 -1
- package/dist/chunk-H3ZZDX5L.js +61 -0
- package/dist/{chunk-H3BV4VAG.js → chunk-JS3EINZD.js} +2 -2
- package/dist/chunk-MZL7CGCL.js +109 -0
- package/dist/{chunk-TH6W2PWG.js → chunk-UDTG3RSE.js} +1 -1
- package/dist/chunk-WCTHZKU7.js +37 -0
- package/dist/{chunk-TT3WFS6S.js → chunk-YOKHBAIH.js} +11 -11
- package/dist/components/alert-dialog.js +1 -1
- package/dist/components/alert.d.ts +2 -2
- package/dist/components/avatar.d.ts +3 -3
- package/dist/components/badge.d.ts +1 -1
- package/dist/components/badge.js +1 -1
- package/dist/components/button.d.ts +2 -2
- package/dist/components/button.js +1 -1
- package/dist/components/calendar.js +1 -1
- package/dist/components/card.d.ts +4 -4
- package/dist/components/carousel.js +1 -1
- package/dist/components/combobox.js +1 -1
- package/dist/components/command.d.ts +2 -2
- package/dist/components/concept-compare-rail.d.ts +50 -0
- package/dist/components/concept-compare-rail.js +5 -0
- package/dist/components/concept-compare.d.ts +75 -38
- package/dist/components/concept-compare.js +209 -133
- package/dist/components/concept-convergence.d.ts +41 -0
- package/dist/components/concept-convergence.js +53 -0
- package/dist/components/convergence.d.ts +25 -0
- package/dist/components/convergence.js +4 -0
- package/dist/components/dialog.d.ts +2 -2
- package/dist/components/empty-state.js +1 -1
- package/dist/components/event-card.js +1 -1
- package/dist/components/event-compact-row.js +1 -1
- package/dist/components/event-detail-panel.js +2 -2
- package/dist/components/event-index-row.js +1 -1
- package/dist/components/event-list-row-wide.js +2 -2
- package/dist/components/event-list-row.js +2 -2
- package/dist/components/explainer-steps.d.ts +34 -0
- package/dist/components/explainer-steps.js +36 -0
- package/dist/components/filter-category-row.js +1 -1
- package/dist/components/glass-ground.d.ts +61 -0
- package/dist/components/glass-ground.js +3 -0
- package/dist/components/haptic-button.js +1 -1
- package/dist/components/hub-surface.js +11 -1
- package/dist/components/hue-chip.d.ts +70 -0
- package/dist/components/hue-chip.js +3 -0
- package/dist/components/input-group.d.ts +2 -2
- package/dist/components/input.d.ts +1 -1
- package/dist/components/item.d.ts +4 -4
- package/dist/components/kbd.d.ts +2 -2
- package/dist/components/kitchen-dossier.d.ts +88 -0
- package/dist/components/kitchen-dossier.js +186 -0
- package/dist/components/link-button.js +1 -1
- package/dist/components/menu-dead-end.d.ts +56 -0
- package/dist/components/menu-dead-end.js +49 -0
- package/dist/components/menu-family-block.d.ts +98 -0
- package/dist/components/menu-family-block.js +153 -0
- package/dist/components/menu-family-card.d.ts +33 -27
- package/dist/components/menu-family-card.js +41 -85
- package/dist/components/menu-field.js +92 -2
- package/dist/components/menu-filter-chips.d.ts +86 -0
- package/dist/components/menu-filter-chips.js +122 -0
- package/dist/components/menu-group-rows.d.ts +53 -0
- package/dist/components/menu-group-rows.js +67 -0
- package/dist/components/menu-hub-hero.js +10 -5
- package/dist/components/menu-index-header.d.ts +37 -0
- package/dist/components/menu-index-header.js +68 -0
- package/dist/components/menu-index-list.d.ts +48 -0
- package/dist/components/menu-index-list.js +96 -0
- package/dist/components/menu-intent-hero.d.ts +69 -0
- package/dist/components/menu-intent-hero.js +87 -0
- package/dist/components/menu-item.js +3 -3
- package/dist/components/menu-kitchen-list.d.ts +82 -0
- package/dist/components/menu-kitchen-list.js +5 -0
- package/dist/components/menu-mocks.d.ts +37 -2
- package/dist/components/menu-mocks.js +152 -27
- package/dist/components/menu-types.d.ts +182 -6
- package/dist/components/motion-icon-controls.js +2 -2
- package/dist/components/motion-icon-set.js +6 -6
- package/dist/components/motion-icon.d.ts +22 -1
- package/dist/components/motion-icon.js +1 -1
- package/dist/components/multi-select-combobox.js +2 -2
- package/dist/components/native-select.d.ts +2 -2
- package/dist/components/pagination.js +1 -1
- package/dist/components/photo-ground.d.ts +64 -0
- package/dist/components/photo-ground.js +3 -0
- package/dist/components/place-about-tab.js +2 -2
- package/dist/components/place-details-mobile.js +3 -3
- package/dist/components/place-types.d.ts +2 -2
- package/dist/components/qa-block.d.ts +36 -0
- package/dist/components/qa-block.js +24 -0
- package/dist/components/scroll-fade.js +3 -121
- package/dist/components/searchable-select.js +1 -1
- package/dist/components/select.d.ts +1 -1
- package/dist/components/signature-chip.d.ts +2 -2
- package/dist/components/signature-chip.js +2 -2
- package/dist/components/sliding-panels.js +1 -1
- package/dist/components/snap-rail.d.ts +26 -3
- package/dist/components/snap-rail.js +3 -109
- package/dist/components/spinner.d.ts +4 -4
- package/dist/components/story.d.ts +34 -0
- package/dist/components/story.js +3 -0
- package/dist/components/tag.d.ts +4 -4
- package/dist/components/text.d.ts +5 -5
- package/dist/components/toggle.d.ts +1 -1
- package/dist/components/top-comparison.d.ts +28 -0
- package/dist/components/top-comparison.js +76 -0
- package/dist/components/top-cross-link.d.ts +34 -0
- package/dist/components/top-cross-link.js +101 -0
- package/dist/components/top-ranking-entry.d.ts +53 -0
- package/dist/components/top-ranking-entry.js +217 -0
- package/dist/components/top-ranking-hero.d.ts +61 -0
- package/dist/components/top-ranking-hero.js +99 -0
- package/dist/components/top-ranking-legend.d.ts +69 -0
- package/dist/components/top-ranking-legend.js +124 -0
- package/dist/components/top-types.d.ts +109 -0
- package/dist/components/top-types.js +1 -0
- package/dist/components/venue-grade-dimensions.d.ts +3 -1
- package/dist/components/venue-grade-dimensions.js +1 -1
- package/dist/components/venue-quotes.d.ts +1 -1
- package/dist/components/venue-quotes.js +1 -1
- package/dist/components/venue-wall.js +5 -3
- package/dist/components/weekday-theme-banner.js +1 -1
- package/dist/components/weekend-panel.js +2 -2
- package/package.json +1 -1
- package/src/components/badge.tsx +1 -1
- package/src/components/button.tsx +2 -2
- package/src/components/concept-compare-rail.tsx +132 -0
- package/src/components/concept-compare.stories.tsx +122 -54
- package/src/components/concept-compare.tsx +368 -196
- package/src/components/concept-convergence.stories.tsx +53 -0
- package/src/components/concept-convergence.tsx +90 -0
- package/src/components/convergence.stories.tsx +54 -0
- package/src/components/convergence.tsx +177 -0
- package/src/components/explainer-steps.stories.tsx +46 -0
- package/src/components/explainer-steps.tsx +74 -0
- package/src/components/glass-ground.tsx +104 -0
- package/src/components/hub-surface.tsx +11 -1
- package/src/components/hue-chip.stories.tsx +76 -0
- package/src/components/hue-chip.test.tsx +62 -0
- package/src/components/hue-chip.tsx +99 -0
- package/src/components/kitchen-dossier.stories.tsx +108 -0
- package/src/components/kitchen-dossier.tsx +321 -0
- package/src/components/menu-dead-end.stories.tsx +67 -0
- package/src/components/menu-dead-end.tsx +127 -0
- package/src/components/menu-family-block.stories.tsx +91 -0
- package/src/components/menu-family-block.tsx +303 -0
- package/src/components/menu-family-card.stories.tsx +43 -35
- package/src/components/menu-family-card.tsx +115 -142
- package/src/components/menu-filter-chips.stories.tsx +107 -0
- package/src/components/menu-filter-chips.tsx +238 -0
- package/src/components/menu-group-rows.stories.tsx +54 -0
- package/src/components/menu-group-rows.tsx +133 -0
- package/src/components/menu-hub-hero.test.tsx +39 -0
- package/src/components/menu-hub-hero.tsx +17 -5
- package/src/components/menu-index-header.stories.tsx +72 -0
- package/src/components/menu-index-header.tsx +125 -0
- package/src/components/menu-index-list.stories.tsx +50 -0
- package/src/components/menu-index-list.tsx +149 -0
- package/src/components/menu-intent-hero.stories.tsx +67 -0
- package/src/components/menu-intent-hero.tsx +189 -0
- package/src/components/menu-kitchen-list.stories.tsx +67 -0
- package/src/components/menu-kitchen-list.test.tsx +78 -0
- package/src/components/menu-kitchen-list.tsx +284 -0
- package/src/components/menu-mocks.ts +198 -23
- package/src/components/menu-types.ts +189 -6
- package/src/components/motion-icon-controls.tsx +11 -11
- package/src/components/motion-icon-set.tsx +6 -5
- package/src/components/motion-icon.test.tsx +23 -1
- package/src/components/motion-icon.tsx +20 -0
- package/src/components/photo-ground.tsx +147 -0
- package/src/components/place-types.ts +2 -2
- package/src/components/qa-block.stories.tsx +43 -0
- package/src/components/qa-block.tsx +60 -0
- package/src/components/signature-chip.tsx +3 -3
- package/src/components/snap-rail.tsx +58 -15
- package/src/components/story.tsx +225 -0
- package/src/components/top-comparison.stories.tsx +78 -0
- package/src/components/top-comparison.tsx +151 -0
- package/src/components/top-cross-link.stories.tsx +84 -0
- package/src/components/top-cross-link.tsx +171 -0
- package/src/components/top-ranking-entry.stories.tsx +171 -0
- package/src/components/top-ranking-entry.test.tsx +105 -0
- package/src/components/top-ranking-entry.tsx +425 -0
- package/src/components/top-ranking-hero.stories.tsx +76 -0
- package/src/components/top-ranking-hero.tsx +218 -0
- package/src/components/top-ranking-legend.stories.tsx +78 -0
- package/src/components/top-ranking-legend.tsx +239 -0
- package/src/components/top-types.ts +110 -0
- package/src/components/venue-grade-dimensions.tsx +2 -2
- package/src/components/venue-quotes.stories.tsx +1 -1
- package/src/components/venue-quotes.tsx +4 -2
- package/src/components/venue-wall.tsx +9 -4
- package/src/components/weekday-theme-banner.tsx +1 -1
- package/dist/chunk-THOVPZNN.js +0 -37
- package/dist/chunk-YS6BHKZO.js +0 -92
- package/dist/components/ranked-venue-list.d.ts +0 -49
- package/dist/components/ranked-venue-list.js +0 -87
- package/dist/components/signature-line-list.d.ts +0 -45
- package/dist/components/signature-line-list.js +0 -99
- package/dist/components/venue-busyness-strip.d.ts +0 -35
- package/dist/components/venue-busyness-strip.js +0 -37
- package/dist/components/venue-signature-block.d.ts +0 -39
- package/dist/components/venue-signature-block.js +0 -6
- package/dist/components/venue-spotlight.d.ts +0 -74
- package/dist/components/venue-spotlight.js +0 -138
- package/src/components/ranked-venue-list.stories.tsx +0 -80
- package/src/components/ranked-venue-list.tsx +0 -177
- package/src/components/signature-line-list.stories.tsx +0 -85
- package/src/components/signature-line-list.tsx +0 -136
- package/src/components/venue-busyness-strip.stories.tsx +0 -66
- package/src/components/venue-busyness-strip.tsx +0 -64
- package/src/components/venue-signature-block.stories.tsx +0 -68
- package/src/components/venue-signature-block.tsx +0 -71
- package/src/components/venue-spotlight.stories.tsx +0 -141
- package/src/components/venue-spotlight.tsx +0 -236
- package/dist/{chunk-XWMRABZW.js → chunk-HSIYYA2E.js} +1 -1
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import { DIMENSION_ORDER, VenueGradeDimensions } from '../chunk-AKQ57NXY.js';
|
|
2
|
+
import { VenueQuotes } from '../chunk-43RC7MHY.js';
|
|
3
|
+
import { StoryProvider, StorySegments, StoryPanel } from '../chunk-F6FMZJDG.js';
|
|
4
|
+
import { GlassGround } from '../chunk-WCTHZKU7.js';
|
|
5
|
+
import { Separator } from '../chunk-WSRWFA6K.js';
|
|
6
|
+
import { PhotoGround, PHOTO_GROUND } from '../chunk-5RWTOT5Y.js';
|
|
7
|
+
import { cn } from '../chunk-FEK5XGZW.js';
|
|
8
|
+
import { __objRest, __spreadValues } from '../chunk-3GWWZKX7.js';
|
|
9
|
+
import { MessageSquareQuote, Star } from 'lucide-react';
|
|
10
|
+
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
11
|
+
|
|
12
|
+
function DefaultLink(_a) {
|
|
13
|
+
var _b = _a, { href } = _b, props = __objRest(_b, ["href"]);
|
|
14
|
+
return /* @__PURE__ */ jsx("a", __spreadValues({ href }, props));
|
|
15
|
+
}
|
|
16
|
+
var GLASS_LEAD = "128px";
|
|
17
|
+
var DIMENSION_LIMIT = 3;
|
|
18
|
+
var QUOTE_LIMIT = 2;
|
|
19
|
+
var initialOf = (name) => {
|
|
20
|
+
var _a, _b;
|
|
21
|
+
return ((_b = (_a = name.match(new RegExp("\\p{L}", "u"))) == null ? void 0 : _a[0]) != null ? _b : "").toLocaleUpperCase();
|
|
22
|
+
};
|
|
23
|
+
function KitchenDossier({
|
|
24
|
+
place,
|
|
25
|
+
href,
|
|
26
|
+
meta,
|
|
27
|
+
signature,
|
|
28
|
+
quotes,
|
|
29
|
+
grade,
|
|
30
|
+
labels,
|
|
31
|
+
formatPrice,
|
|
32
|
+
formatScore,
|
|
33
|
+
partDurationMs,
|
|
34
|
+
action,
|
|
35
|
+
linkComponent: LinkComponent = DefaultLink,
|
|
36
|
+
className
|
|
37
|
+
}) {
|
|
38
|
+
var _a, _b, _c, _d;
|
|
39
|
+
const rating = (_a = place.rating) == null ? void 0 : _a.rating;
|
|
40
|
+
const parts = [];
|
|
41
|
+
if (signature) {
|
|
42
|
+
const item = signature.item;
|
|
43
|
+
const firstEntry = (_b = item.sizes_and_prices) == null ? void 0 : _b[0];
|
|
44
|
+
const mentions = (_d = (_c = item.mentions) != null ? _c : item.review_count) != null ? _d : 0;
|
|
45
|
+
parts.push(
|
|
46
|
+
/* @__PURE__ */ jsxs(Part, { kicker: labels.knownFor, children: [
|
|
47
|
+
signature.href ? /* @__PURE__ */ jsx(
|
|
48
|
+
LinkComponent,
|
|
49
|
+
{
|
|
50
|
+
href: signature.href,
|
|
51
|
+
className: "w-max max-w-full text-[16px] font-bold leading-tight tracking-[-0.01em] text-white hover:underline focus-visible:outline-none focus-visible:underline",
|
|
52
|
+
children: item.name
|
|
53
|
+
}
|
|
54
|
+
) : /* @__PURE__ */ jsx("span", { className: "text-[16px] font-bold leading-tight tracking-[-0.01em] text-white", children: item.name }),
|
|
55
|
+
signature.why && /* @__PURE__ */ jsx("p", { className: "mt-1 line-clamp-2 font-serif text-[13px] leading-snug text-white/75", children: signature.why }),
|
|
56
|
+
(firstEntry || mentions > 0) && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
57
|
+
/* @__PURE__ */ jsx(Separator, { className: "my-2.5 via-white/20" }),
|
|
58
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-end justify-between gap-3", children: [
|
|
59
|
+
/* @__PURE__ */ jsx("span", { className: "flex items-baseline gap-1.5", children: firstEntry && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
60
|
+
/* @__PURE__ */ jsxs("span", { className: "text-[18px] font-extrabold leading-none tabular-nums text-white", children: [
|
|
61
|
+
formatPrice(firstEntry.price),
|
|
62
|
+
/* @__PURE__ */ jsx("span", { className: "ml-1 text-[12px] font-bold", children: labels.currency })
|
|
63
|
+
] }),
|
|
64
|
+
firstEntry.size > 0 && /* @__PURE__ */ jsxs("span", { className: "text-[11.5px] tabular-nums text-white/60", children: [
|
|
65
|
+
firstEntry.size,
|
|
66
|
+
item.kind === "dish" ? "g" : "ml"
|
|
67
|
+
] })
|
|
68
|
+
] }) }),
|
|
69
|
+
mentions > 0 && /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1 text-[12px] font-semibold tabular-nums text-white/85", children: [
|
|
70
|
+
/* @__PURE__ */ jsx(MessageSquareQuote, { className: "h-3.5 w-3.5", "aria-hidden": true }),
|
|
71
|
+
labels.mentions(mentions)
|
|
72
|
+
] })
|
|
73
|
+
] })
|
|
74
|
+
] })
|
|
75
|
+
] }, "signature")
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
if (quotes && quotes.length > 0) {
|
|
79
|
+
parts.push(
|
|
80
|
+
/* @__PURE__ */ jsx(Part, { kicker: labels.whatPeopleSay, children: /* @__PURE__ */ jsx(VenueQuotes, { quotes, limit: QUOTE_LIMIT, className: "lg:grid-cols-1" }) }, "quotes")
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
const dimensions = grade == null ? void 0 : grade.dimensions;
|
|
84
|
+
const strongest = dimensions ? DIMENSION_ORDER.filter((key) => typeof dimensions[key] === "number").sort((a, b) => {
|
|
85
|
+
var _a2, _b2;
|
|
86
|
+
return ((_a2 = dimensions[b]) != null ? _a2 : 0) - ((_b2 = dimensions[a]) != null ? _b2 : 0);
|
|
87
|
+
}).slice(0, DIMENSION_LIMIT) : [];
|
|
88
|
+
if (grade && (grade.overall || strongest.length > 0)) {
|
|
89
|
+
parts.push(
|
|
90
|
+
/* @__PURE__ */ jsx(Part, { kicker: labels.theGrade, children: /* @__PURE__ */ jsxs("div", { className: "flex items-end gap-4", children: [
|
|
91
|
+
grade.overall && /* @__PURE__ */ jsx("span", { className: "text-[34px] font-extrabold leading-none tracking-[-0.03em] tabular-nums text-[oklch(0.82_0.15_85)]", children: grade.overall }),
|
|
92
|
+
dimensions && strongest.length > 0 && /* @__PURE__ */ jsx(
|
|
93
|
+
VenueGradeDimensions,
|
|
94
|
+
{
|
|
95
|
+
className: "min-w-0 flex-1",
|
|
96
|
+
dimensions,
|
|
97
|
+
labels: labels.dimensions,
|
|
98
|
+
order: strongest,
|
|
99
|
+
formatScore
|
|
100
|
+
}
|
|
101
|
+
)
|
|
102
|
+
] }) }, "grade")
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
return /* @__PURE__ */ jsxs(
|
|
106
|
+
"article",
|
|
107
|
+
{
|
|
108
|
+
className: cn(
|
|
109
|
+
/* `flex flex-col` so the story fills the card's height: in a rail
|
|
110
|
+
the cards are stretched to the tallest, and a story that stopped
|
|
111
|
+
at its own min-height left a band of bare photograph under the
|
|
112
|
+
action on every shorter card. */
|
|
113
|
+
"dark relative isolate flex flex-col overflow-hidden rounded-[18px] border border-border text-foreground shadow-[var(--shadow-card)]",
|
|
114
|
+
PHOTO_GROUND,
|
|
115
|
+
className
|
|
116
|
+
),
|
|
117
|
+
children: [
|
|
118
|
+
/* @__PURE__ */ jsx(PhotoGround, { className: "-z-20", photos: [place.thumbnail_url], initials: initialOf(place.name), loading: "eager" }),
|
|
119
|
+
/* @__PURE__ */ jsxs(
|
|
120
|
+
StoryProvider,
|
|
121
|
+
{
|
|
122
|
+
count: parts.length,
|
|
123
|
+
durationMs: partDurationMs,
|
|
124
|
+
nextLabel: labels.next,
|
|
125
|
+
className: "relative flex min-h-[560px] flex-1 flex-col p-4 lg:min-h-[440px] lg:p-5",
|
|
126
|
+
children: [
|
|
127
|
+
/* @__PURE__ */ jsx(
|
|
128
|
+
StorySegments,
|
|
129
|
+
{
|
|
130
|
+
labels: parts.map((_, index) => labels.part(index, parts.length)),
|
|
131
|
+
className: "-mt-1"
|
|
132
|
+
}
|
|
133
|
+
),
|
|
134
|
+
/* @__PURE__ */ jsx("div", { className: "relative min-h-[184px] flex-1", children: labels.openUntil && /* @__PURE__ */ jsxs("span", { className: "absolute left-0 top-3 inline-flex h-[30px] items-center gap-[7px] rounded-full bg-[oklch(0.16_0.012_55_/_0.78)] px-3 text-[11.5px] font-bold text-white", children: [
|
|
135
|
+
/* @__PURE__ */ jsx("i", { className: "h-[7px] w-[7px] rounded-full bg-success", "aria-hidden": true }),
|
|
136
|
+
labels.openUntil
|
|
137
|
+
] }) }),
|
|
138
|
+
/* @__PURE__ */ jsxs("div", { className: "relative flex flex-col gap-3.5", children: [
|
|
139
|
+
/* @__PURE__ */ jsx(
|
|
140
|
+
"div",
|
|
141
|
+
{
|
|
142
|
+
"aria-hidden": true,
|
|
143
|
+
className: "pointer-events-none absolute -inset-x-4 -bottom-4 -top-32 -z-10 lg:-inset-x-5 lg:-bottom-5",
|
|
144
|
+
children: /* @__PURE__ */ jsx(
|
|
145
|
+
GlassGround,
|
|
146
|
+
{
|
|
147
|
+
stops: { direction: "to bottom", clear: "0px", full: GLASS_LEAD },
|
|
148
|
+
peak: 0.6
|
|
149
|
+
}
|
|
150
|
+
)
|
|
151
|
+
}
|
|
152
|
+
),
|
|
153
|
+
/* @__PURE__ */ jsxs("div", { className: "[text-shadow:0_1px_12px_oklch(0_0_0/0.55)]", children: [
|
|
154
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-baseline justify-between gap-4", children: [
|
|
155
|
+
/* @__PURE__ */ jsx("h3", { className: "min-w-0 font-serif text-[26px] font-semibold leading-tight tracking-[-0.02em] text-white lg:text-[32px]", children: /* @__PURE__ */ jsx(LinkComponent, { href, className: "focus-visible:outline-none focus-visible:underline", children: place.name }) }),
|
|
156
|
+
rating !== void 0 && /* @__PURE__ */ jsxs("span", { className: "inline-flex shrink-0 items-center gap-1 text-[18px] font-extrabold leading-none tabular-nums text-white lg:text-[22px]", children: [
|
|
157
|
+
rating.toFixed(1),
|
|
158
|
+
/* @__PURE__ */ jsx(Star, { className: "h-3.5 w-3.5 lg:h-4 lg:w-4", "aria-hidden": true })
|
|
159
|
+
] })
|
|
160
|
+
] }),
|
|
161
|
+
(meta || labels.reviewCount) && /* @__PURE__ */ jsx("p", { className: "mt-1 truncate text-[12.5px] text-white/70", children: [meta, labels.reviewCount].filter(Boolean).join(" \xB7 ") })
|
|
162
|
+
] }),
|
|
163
|
+
parts.length > 0 && /* @__PURE__ */ jsx(
|
|
164
|
+
StoryPanel,
|
|
165
|
+
{
|
|
166
|
+
parts,
|
|
167
|
+
className: "rounded-[14px] border border-white/10 bg-[oklch(0.16_0.012_55_/_0.62)] p-4"
|
|
168
|
+
}
|
|
169
|
+
),
|
|
170
|
+
action && /* @__PURE__ */ jsx("div", { className: "flex [&>*]:w-full lg:[&>*]:w-auto", children: action })
|
|
171
|
+
] })
|
|
172
|
+
]
|
|
173
|
+
}
|
|
174
|
+
)
|
|
175
|
+
]
|
|
176
|
+
}
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
function Part({ kicker, children }) {
|
|
180
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
|
|
181
|
+
/* @__PURE__ */ jsx("span", { className: "mb-1.5 block text-[9.5px] font-bold uppercase tracking-[0.12em] text-white/60", children: kicker }),
|
|
182
|
+
children
|
|
183
|
+
] });
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export { KitchenDossier };
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { MenuLinkComponent, MenuLoosenOption } from './menu-types.js';
|
|
3
|
+
import 'react';
|
|
4
|
+
import './place-types.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* MenuDeadEnd — what dropped out, and the one press that brings it back.
|
|
8
|
+
*
|
|
9
|
+
* A filter combination that returns nothing is the intent page's worst state,
|
|
10
|
+
* and an empty-state illustration is the worst answer to it: the reader knows
|
|
11
|
+
* the list is empty, what they do not know is WHICH of their four filters did
|
|
12
|
+
* it. So this says what dropped out, why, and what widening one specific filter
|
|
13
|
+
* would restore — with the count, because "+5 kitchens, including the
|
|
14
|
+
* highest-rated Italian room in the city" is an answer and "try removing a
|
|
15
|
+
* filter" is a shrug.
|
|
16
|
+
*
|
|
17
|
+
* It is also drawn on a NON-empty list, whenever a filter has cost the reader
|
|
18
|
+
* something notable. The board's case is 34 kitchens still standing and Casa
|
|
19
|
+
* Boema — the one they came for — missing.
|
|
20
|
+
*
|
|
21
|
+
* Interactive: the consumer marks its own module `"use client"`.
|
|
22
|
+
*/
|
|
23
|
+
type MenuDeadEndProps = {
|
|
24
|
+
/**
|
|
25
|
+
* The explanation, composed by the caller.
|
|
26
|
+
*
|
|
27
|
+
* A node rather than a string because it emphasises real names ("**Casa
|
|
28
|
+
* Boema** and six others dropped out"), and assembling that from fragments
|
|
29
|
+
* here would produce translated word-salad in one of the two locales.
|
|
30
|
+
*/
|
|
31
|
+
children: React.ReactNode;
|
|
32
|
+
/** The widening this offers, e.g. "Raise to 60 lei · 16 kitchens". */
|
|
33
|
+
actionLabel?: string;
|
|
34
|
+
/** Where the widening leads. Preferred over `onAction` — see `MenuLoosenOption.href`. */
|
|
35
|
+
actionHref?: string;
|
|
36
|
+
onAction?: () => void;
|
|
37
|
+
linkComponent?: MenuLinkComponent;
|
|
38
|
+
className?: string;
|
|
39
|
+
};
|
|
40
|
+
declare function MenuDeadEnd({ children, actionLabel, actionHref, onAction, linkComponent: LinkComponent, className, }: MenuDeadEndProps): react_jsx_runtime.JSX.Element;
|
|
41
|
+
/**
|
|
42
|
+
* MenuLoosenList — one row per active filter, each priced in kitchens.
|
|
43
|
+
*
|
|
44
|
+
* The companion to the panel above: the panel names the single best widening,
|
|
45
|
+
* this lists every one of them so the reader can choose which constraint they
|
|
46
|
+
* care least about. Every row states what it would return, because a filter the
|
|
47
|
+
* reader cannot price is a filter they will not drop.
|
|
48
|
+
*/
|
|
49
|
+
type MenuLoosenListProps = {
|
|
50
|
+
options: MenuLoosenOption[];
|
|
51
|
+
linkComponent?: MenuLinkComponent;
|
|
52
|
+
className?: string;
|
|
53
|
+
};
|
|
54
|
+
declare function MenuLoosenList({ options, linkComponent: LinkComponent, className, }: MenuLoosenListProps): react_jsx_runtime.JSX.Element;
|
|
55
|
+
|
|
56
|
+
export { MenuDeadEnd, type MenuDeadEndProps, MenuLoosenList, type MenuLoosenListProps };
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { cn } from '../chunk-FEK5XGZW.js';
|
|
2
|
+
import { __objRest, __spreadValues } from '../chunk-3GWWZKX7.js';
|
|
3
|
+
import { Info } from 'lucide-react';
|
|
4
|
+
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
5
|
+
|
|
6
|
+
function DefaultLink(_a) {
|
|
7
|
+
var _b = _a, { href } = _b, props = __objRest(_b, ["href"]);
|
|
8
|
+
return /* @__PURE__ */ jsx("a", __spreadValues({ href }, props));
|
|
9
|
+
}
|
|
10
|
+
function MenuDeadEnd({
|
|
11
|
+
children,
|
|
12
|
+
actionLabel,
|
|
13
|
+
actionHref,
|
|
14
|
+
onAction,
|
|
15
|
+
linkComponent: LinkComponent = DefaultLink,
|
|
16
|
+
className
|
|
17
|
+
}) {
|
|
18
|
+
const actionClass = "inline-flex h-7 shrink-0 items-center rounded-full bg-foreground px-3 text-[12.5px] font-bold text-background transition-opacity hover:opacity-90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 motion-reduce:transition-none lg:ml-auto";
|
|
19
|
+
return /* @__PURE__ */ jsxs(
|
|
20
|
+
"div",
|
|
21
|
+
{
|
|
22
|
+
className: cn(
|
|
23
|
+
"mt-4 flex flex-wrap items-center gap-3 rounded-[14px] bg-muted p-3.5 lg:flex-nowrap lg:gap-4 lg:px-[18px] lg:py-4",
|
|
24
|
+
className
|
|
25
|
+
),
|
|
26
|
+
children: [
|
|
27
|
+
/* @__PURE__ */ jsx(Info, { className: "h-[18px] w-[18px] shrink-0 text-muted-foreground", "aria-hidden": true }),
|
|
28
|
+
/* @__PURE__ */ jsx("p", { className: "max-w-[560px] text-[13px] leading-[1.55] text-muted-foreground [&_b]:font-bold [&_b]:text-foreground", children }),
|
|
29
|
+
actionLabel && (actionHref ? /* @__PURE__ */ jsx(LinkComponent, { href: actionHref, className: actionClass, children: actionLabel }) : onAction && /* @__PURE__ */ jsx("button", { type: "button", onClick: onAction, className: actionClass, children: actionLabel }))
|
|
30
|
+
]
|
|
31
|
+
}
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
function MenuLoosenList({
|
|
35
|
+
options,
|
|
36
|
+
linkComponent: LinkComponent = DefaultLink,
|
|
37
|
+
className
|
|
38
|
+
}) {
|
|
39
|
+
const rowClass = "inline-flex h-9 items-center justify-between gap-3 rounded-full border border-border bg-card px-4 text-sm font-semibold transition-colors hover:border-accent/40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring motion-reduce:transition-none";
|
|
40
|
+
return /* @__PURE__ */ jsx("div", { className: cn("flex flex-col gap-2.5", className), children: options.map((option) => {
|
|
41
|
+
const body = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
42
|
+
/* @__PURE__ */ jsx("span", { className: "min-w-0 truncate", children: option.label }),
|
|
43
|
+
/* @__PURE__ */ jsx("span", { className: "shrink-0 font-bold tabular-nums text-accent", children: option.gainLabel })
|
|
44
|
+
] });
|
|
45
|
+
return option.href ? /* @__PURE__ */ jsx(LinkComponent, { href: option.href, className: rowClass, children: body }, option.key) : /* @__PURE__ */ jsx("button", { type: "button", onClick: option.onSelect, className: rowClass, children: body }, option.key);
|
|
46
|
+
}) });
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export { MenuDeadEnd, MenuLoosenList };
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { MenuKitchenListLabels } from './menu-kitchen-list.js';
|
|
3
|
+
import { MenuFamilyBlockPreview, MenuLinkComponent, MenuLinkProps } from './menu-types.js';
|
|
4
|
+
import 'react';
|
|
5
|
+
import './place-types.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* MenuFamilyBlock — the cuisines index's family card. The mosaic is the ground.
|
|
9
|
+
*
|
|
10
|
+
* ## Three layers, and everything floats on them
|
|
11
|
+
*
|
|
12
|
+
* The grid of venue photographs is the background for the ENTIRE card, edge to
|
|
13
|
+
* edge, and the facts sit on it. Three layers do the work:
|
|
14
|
+
*
|
|
15
|
+
* 1. **The mosaic**, full-bleed, wearing the family's hue through a
|
|
16
|
+
* `mix-blend-color` layer.
|
|
17
|
+
* 2. **An ink gradient** — absent where the photography is the subject, full
|
|
18
|
+
* where the rows begin. This is what buys the contrast. There used to be a
|
|
19
|
+
* masked backdrop-filter under it; it was removed for the measurement
|
|
20
|
+
* recorded in `GlassGround` (73 blur layers cost `/menu` 9 ms per scrolled
|
|
21
|
+
* frame on a phone).
|
|
22
|
+
*
|
|
23
|
+
* On the phone the ink runs top to bottom: the name and the action sit on
|
|
24
|
+
* clear photography, the chips and the rows on the inked foot. On desktop the
|
|
25
|
+
* same layers turn ninety degrees — the photo column on the left stays clear,
|
|
26
|
+
* the row column on the right is the inked one. The mosaic
|
|
27
|
+
* runs 3×2 there rather than 2×2, because at 1110px a four-tile grid makes each
|
|
28
|
+
* photograph too big to read as texture.
|
|
29
|
+
*
|
|
30
|
+
* ## The dark scope
|
|
31
|
+
*
|
|
32
|
+
* The card's content region carries the `dark` class, so every token inside —
|
|
33
|
+
* `text-foreground`, `bg-muted`, `border-border` — resolves to its dark-theme
|
|
34
|
+
* value. That is how `MenuKitchenList` and `HueChip` render correctly on the
|
|
35
|
+
* ink without a fork or a "tone" prop: the mechanism the app already uses to
|
|
36
|
+
* mark a region as dark. It also means the card looks the same in both themes,
|
|
37
|
+
* which is right — it is built on photography, and photography is not themed.
|
|
38
|
+
*
|
|
39
|
+
* ## The two costs, stated
|
|
40
|
+
*
|
|
41
|
+
* A `backdrop-filter` over a mask is expensive to composite, and this card is
|
|
42
|
+
* repeated ten times down a scroll. And the thumbnails now sit on a
|
|
43
|
+
* photograph; they survive only because the ink is heavy under them, and on a
|
|
44
|
+
* busy interior shot they will fight. Both were accepted with the design.
|
|
45
|
+
*
|
|
46
|
+
* ## No "most named" item, no venue count badge
|
|
47
|
+
*
|
|
48
|
+
* The family-level "most named" was the first venue's signature relabelled —
|
|
49
|
+
* Italian, Pizza and Mediterranean all claimed "Turkish Poached Eggs" — and
|
|
50
|
+
* the count used to print three times with two values. One count, under the
|
|
51
|
+
* name; the overflow tile says only how many photographs it stands for.
|
|
52
|
+
*/
|
|
53
|
+
/** @deprecated Alias of `MenuLinkProps`; kept for symmetry with the other menu surfaces. */
|
|
54
|
+
type MenuFamilyBlockLinkProps = MenuLinkProps;
|
|
55
|
+
type MenuFamilyBlockLabels = {
|
|
56
|
+
/** Unit under the family name, e.g. "247 kitchens". Pluralised by the caller. */
|
|
57
|
+
venueUnit: (count: number) => string;
|
|
58
|
+
/** The overflow pill over the mosaic, e.g. "+311 more". */
|
|
59
|
+
more: (count: number) => string;
|
|
60
|
+
/**
|
|
61
|
+
* Price-band caption, e.g. "kitchen price band".
|
|
62
|
+
*
|
|
63
|
+
* Never drawn: the band reads as a wallet and a range, and a caption on a
|
|
64
|
+
* pill that short is longer than the fact. It names the pill for a screen
|
|
65
|
+
* reader, which would otherwise be read two numbers and a currency.
|
|
66
|
+
*/
|
|
67
|
+
priceBand: string;
|
|
68
|
+
/** Main action, e.g. "Browse Italian". Sits on the photography — keep it short. */
|
|
69
|
+
browse: string;
|
|
70
|
+
/** Passed through to the venue rows. */
|
|
71
|
+
kitchens: MenuKitchenListLabels;
|
|
72
|
+
};
|
|
73
|
+
type MenuFamilyBlockProps = {
|
|
74
|
+
family: MenuFamilyBlockPreview;
|
|
75
|
+
labels: MenuFamilyBlockLabels;
|
|
76
|
+
formatCount?: (value: number) => string;
|
|
77
|
+
/** Finished CSS colour for the chips. */
|
|
78
|
+
hue?: string;
|
|
79
|
+
/** Finished CSS gradient — the family's colour over the mosaic. */
|
|
80
|
+
gradient?: string;
|
|
81
|
+
linkComponent?: MenuLinkComponent;
|
|
82
|
+
className?: string;
|
|
83
|
+
};
|
|
84
|
+
declare function MenuFamilyBlock({ family, labels, formatCount, hue, gradient, linkComponent: LinkComponent, className, }: MenuFamilyBlockProps): react_jsx_runtime.JSX.Element;
|
|
85
|
+
/**
|
|
86
|
+
* The block's own loading state, at the block's footprint.
|
|
87
|
+
*
|
|
88
|
+
* The ground is the card's own, so the placeholder is already the dark
|
|
89
|
+
* surface the photographs will land on — nothing flashes from paper to ink.
|
|
90
|
+
* `venues` matches the rows the caller will render, so a block showing three
|
|
91
|
+
* does not settle from a placeholder showing one.
|
|
92
|
+
*/
|
|
93
|
+
declare function MenuFamilyBlockSkeleton({ venues, className, }: {
|
|
94
|
+
venues?: number;
|
|
95
|
+
className?: string;
|
|
96
|
+
}): react_jsx_runtime.JSX.Element;
|
|
97
|
+
|
|
98
|
+
export { MenuFamilyBlock, type MenuFamilyBlockLabels, type MenuFamilyBlockLinkProps, type MenuFamilyBlockProps, MenuFamilyBlockSkeleton };
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { MenuKitchenList } from '../chunk-24RFI4RQ.js';
|
|
2
|
+
import { HueChip } from '../chunk-CLLY6XB3.js';
|
|
3
|
+
import { Separator } from '../chunk-WSRWFA6K.js';
|
|
4
|
+
import { PhotoGround, PHOTO_GROUND } from '../chunk-5RWTOT5Y.js';
|
|
5
|
+
import { Skeleton } from '../chunk-QXHMPJ35.js';
|
|
6
|
+
import { cn } from '../chunk-FEK5XGZW.js';
|
|
7
|
+
import { __objRest, __spreadValues } from '../chunk-3GWWZKX7.js';
|
|
8
|
+
import { Wallet } from 'lucide-react';
|
|
9
|
+
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
10
|
+
|
|
11
|
+
function DefaultLink(_a) {
|
|
12
|
+
var _b = _a, { href } = _b, props = __objRest(_b, ["href"]);
|
|
13
|
+
return /* @__PURE__ */ jsx("a", __spreadValues({ href }, props));
|
|
14
|
+
}
|
|
15
|
+
var MOSAIC_TILES = 6;
|
|
16
|
+
var PHONE_TILES = 4;
|
|
17
|
+
var GROUND = PHOTO_GROUND;
|
|
18
|
+
var INK = "bg-[linear-gradient(to_bottom,oklch(0.16_0.012_55_/_0.42)_0%,oklch(0.16_0.012_55_/_0.6)_30%,oklch(0.16_0.012_55_/_0.9)_48%,oklch(0.16_0.012_55_/_0.96)_100%)] lg:bg-[linear-gradient(to_right,oklch(0.16_0.012_55_/_0.38)_0%,oklch(0.16_0.012_55_/_0.6)_30%,oklch(0.16_0.012_55_/_0.92)_46%,oklch(0.16_0.012_55_/_0.96)_100%)]";
|
|
19
|
+
var TEXT_SCRIM = (
|
|
20
|
+
/* `isolate` + `before:-z-10`: a positioned pseudo-element paints ABOVE the
|
|
21
|
+
column's in-flow text unless the column is its own stacking context and
|
|
22
|
+
the layer is ordered beneath. Without `isolate`, `-z-10` would instead
|
|
23
|
+
drop it behind the card's mosaic — inside the article's context. */
|
|
24
|
+
"isolate before:pointer-events-none before:absolute before:inset-x-0 before:bottom-0 before:top-[30%] before:-z-10 before:bg-[linear-gradient(to_bottom,oklch(0.16_0.012_55_/_0)_0%,oklch(0.16_0.012_55_/_0.55)_45%,oklch(0.16_0.012_55_/_0.8)_100%)] before:content-['']"
|
|
25
|
+
);
|
|
26
|
+
function MenuFamilyBlock({
|
|
27
|
+
family,
|
|
28
|
+
labels,
|
|
29
|
+
formatCount = String,
|
|
30
|
+
hue,
|
|
31
|
+
gradient,
|
|
32
|
+
linkComponent: LinkComponent = DefaultLink,
|
|
33
|
+
className
|
|
34
|
+
}) {
|
|
35
|
+
var _a, _b, _c, _d, _e;
|
|
36
|
+
const photos = ((_a = family.photoUrls) != null ? _a : []).slice(0, MOSAIC_TILES);
|
|
37
|
+
const overflow = Math.max(family.venueCount - photos.length, 0);
|
|
38
|
+
const initial = ((_c = (_b = family.name.match(new RegExp("\\p{L}", "u"))) == null ? void 0 : _b[0]) != null ? _c : "").toLocaleUpperCase();
|
|
39
|
+
const venues = (_d = family.venues) != null ? _d : [];
|
|
40
|
+
return /* @__PURE__ */ jsxs(
|
|
41
|
+
"article",
|
|
42
|
+
{
|
|
43
|
+
className: cn(
|
|
44
|
+
/* `isolate` confines the blend and the backdrop-filter to this card.
|
|
45
|
+
`dark` scopes every token inside to its dark-theme value. */
|
|
46
|
+
"dark relative isolate overflow-hidden rounded-[14px] text-foreground",
|
|
47
|
+
GROUND,
|
|
48
|
+
className
|
|
49
|
+
),
|
|
50
|
+
children: [
|
|
51
|
+
/* @__PURE__ */ jsx(
|
|
52
|
+
PhotoGround,
|
|
53
|
+
{
|
|
54
|
+
photos: photos.length > 0 ? photos : [void 0],
|
|
55
|
+
initials: initial,
|
|
56
|
+
mosaicClassName: photos.length > 0 ? "grid-cols-2 grid-rows-2 lg:grid-cols-3" : "grid-cols-1",
|
|
57
|
+
phoneTiles: PHONE_TILES,
|
|
58
|
+
gradient
|
|
59
|
+
}
|
|
60
|
+
),
|
|
61
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": true, className: cn("absolute inset-0", INK) }),
|
|
62
|
+
/* @__PURE__ */ jsxs("div", { className: "relative grid grid-cols-1 lg:grid-cols-[38%_1fr]", children: [
|
|
63
|
+
/* @__PURE__ */ jsxs("div", { className: cn("relative flex flex-col p-3.5 lg:min-h-[320px] lg:justify-end lg:p-5", TEXT_SCRIM), children: [
|
|
64
|
+
overflow > 0 && /* @__PURE__ */ jsx("span", { className: "mb-16 self-end rounded-full bg-[oklch(0.16_0.012_55_/_0.78)] px-2.5 py-1 text-[11px] font-semibold text-white lg:mb-3 lg:self-start", children: labels.more(overflow) }),
|
|
65
|
+
/* @__PURE__ */ jsx("h3", { className: "text-balance font-serif text-[26px] font-semibold leading-tight tracking-[-0.02em] text-white lg:text-[30px]", children: family.name }),
|
|
66
|
+
/* @__PURE__ */ jsx("span", { className: "mt-0.5 block text-[13px] font-medium tabular-nums text-white/70", children: labels.venueUnit(family.venueCount) }),
|
|
67
|
+
/* @__PURE__ */ jsx(
|
|
68
|
+
LinkComponent,
|
|
69
|
+
{
|
|
70
|
+
href: family.href,
|
|
71
|
+
className: "mt-3.5 inline-flex h-10 w-max items-center rounded-full border border-white/20 bg-white/[0.16] px-4 text-[13.5px] font-bold text-white transition-colors hover:bg-white/25 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white motion-reduce:transition-none",
|
|
72
|
+
children: labels.browse
|
|
73
|
+
}
|
|
74
|
+
)
|
|
75
|
+
] }),
|
|
76
|
+
/* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-col px-3.5 pb-3.5 lg:p-5", children: [
|
|
77
|
+
/* @__PURE__ */ jsxs("ul", { className: "flex flex-wrap items-center gap-1.5", children: [
|
|
78
|
+
family.priceBandLabel && /* No hue: a fact about money, not about the family. In the dark
|
|
79
|
+
scope `HueChip`'s `--muted` fallback is the dark pill the design
|
|
80
|
+
draws. */
|
|
81
|
+
/* @__PURE__ */ jsx(
|
|
82
|
+
HueChip,
|
|
83
|
+
{
|
|
84
|
+
as: "li",
|
|
85
|
+
className: "font-bold tabular-nums",
|
|
86
|
+
"aria-label": `${labels.priceBand}: ${family.priceBandLabel}`,
|
|
87
|
+
icon: /* @__PURE__ */ jsx(Wallet, { className: "h-3.5 w-3.5 shrink-0 text-muted-foreground", "aria-hidden": true }),
|
|
88
|
+
children: family.priceBandLabel
|
|
89
|
+
}
|
|
90
|
+
),
|
|
91
|
+
(_e = family.chips) == null ? void 0 : _e.map((chip) => (
|
|
92
|
+
/* Label only. The counts are VENUES beside a dish group's name,
|
|
93
|
+
which every reader takes for dishes. */
|
|
94
|
+
/* @__PURE__ */ jsx(HueChip, { as: "li", hue, children: chip.label }, chip.slug)
|
|
95
|
+
))
|
|
96
|
+
] }),
|
|
97
|
+
venues.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
98
|
+
/* @__PURE__ */ jsx(Separator, { className: "my-3" }),
|
|
99
|
+
/* @__PURE__ */ jsx(
|
|
100
|
+
MenuKitchenList,
|
|
101
|
+
{
|
|
102
|
+
kitchens: venues,
|
|
103
|
+
labels: labels.kitchens,
|
|
104
|
+
linkComponent: LinkComponent
|
|
105
|
+
}
|
|
106
|
+
)
|
|
107
|
+
] })
|
|
108
|
+
] })
|
|
109
|
+
] })
|
|
110
|
+
]
|
|
111
|
+
}
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
function MenuFamilyBlockSkeleton({
|
|
115
|
+
venues = 3,
|
|
116
|
+
className
|
|
117
|
+
}) {
|
|
118
|
+
return /* @__PURE__ */ jsx(
|
|
119
|
+
"div",
|
|
120
|
+
{
|
|
121
|
+
className: cn("dark relative overflow-hidden rounded-[14px]", GROUND, className),
|
|
122
|
+
"aria-hidden": true,
|
|
123
|
+
children: /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 lg:grid-cols-[38%_1fr]", children: [
|
|
124
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col p-3.5 lg:min-h-[320px] lg:justify-end lg:p-5", children: [
|
|
125
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "mb-16 h-6 w-[72px] self-end rounded-full lg:mb-3 lg:self-start" }),
|
|
126
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-7 w-[60%]" }),
|
|
127
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "mt-2 h-3.5 w-[90px]" }),
|
|
128
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "mt-3.5 h-10 w-[160px] rounded-full" })
|
|
129
|
+
] }),
|
|
130
|
+
/* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-col px-3.5 pb-3.5 lg:p-5", children: [
|
|
131
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-1.5", children: [
|
|
132
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-7 w-[86px] rounded-full" }),
|
|
133
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-7 w-[74px] rounded-full" }),
|
|
134
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-7 w-[66px] rounded-full" })
|
|
135
|
+
] }),
|
|
136
|
+
/* @__PURE__ */ jsx(Separator, { className: "my-3" }),
|
|
137
|
+
Array.from({ length: venues }, (_, index) => /* @__PURE__ */ jsxs("div", { children: [
|
|
138
|
+
index > 0 && /* @__PURE__ */ jsx(Separator, {}),
|
|
139
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 py-2.5", children: [
|
|
140
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-[46px] w-[46px] rounded-[11px]" }),
|
|
141
|
+
/* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 flex-col gap-1.5", children: [
|
|
142
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-3.5 w-[46%]" }),
|
|
143
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-3 w-[58%]" })
|
|
144
|
+
] })
|
|
145
|
+
] })
|
|
146
|
+
] }, index))
|
|
147
|
+
] })
|
|
148
|
+
] })
|
|
149
|
+
}
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export { MenuFamilyBlock, MenuFamilyBlockSkeleton };
|
|
@@ -1,59 +1,65 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
-
import { MenuItemLabels } from './menu-item.js';
|
|
3
2
|
import { MenuFamilyPreview, MenuLinkComponent, MenuLinkProps } from './menu-types.js';
|
|
4
3
|
import 'react';
|
|
5
4
|
import './place-types.js';
|
|
6
5
|
|
|
7
6
|
/**
|
|
8
7
|
* MenuFamilyCard — a browsable grouping of venues: a cuisine on the dishes tab,
|
|
9
|
-
* a drink family on the drinks tab.
|
|
8
|
+
* a drink family on the drinks tab. The hub's doorway to a family.
|
|
10
9
|
*
|
|
11
|
-
* ##
|
|
10
|
+
* ## A doorway, not a summary
|
|
12
11
|
*
|
|
13
|
-
* The
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
12
|
+
* The cuisines index's `MenuFamilyBlock` is the family's SUMMARY: photography,
|
|
13
|
+
* chips, a price band and three venues. This card is the same family drawn
|
|
14
|
+
* one step earlier, in a row of three on the hub, where the reader is choosing
|
|
15
|
+
* a family and not yet a venue — so it carries the same photography, the same
|
|
16
|
+
* chips and the same "Browse" action, and lists no venue. A venue on the hub
|
|
17
|
+
* card was tried and it was the first venue's signature relabelled as the
|
|
18
|
+
* family's most named, which was a lie at three-card width.
|
|
19
19
|
*
|
|
20
|
-
* ##
|
|
20
|
+
* ## The count does the scanning
|
|
21
21
|
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
22
|
+
* The card is one number, one name and the groups the family consists of,
|
|
23
|
+
* over a mosaic of its venues. The count is VENUES: cuisine exists on places
|
|
24
|
+
* and nowhere else — no listing endpoint will tell you how many dishes are
|
|
25
|
+
* Italian — so a card counting dishes cannot be built and this one does not
|
|
26
|
+
* try. The chips are plain labels: at three cards across, chips carrying
|
|
27
|
+
* numbers turned the panel into a table, and the numbers were kitchens, which
|
|
28
|
+
* nobody reads a chip as.
|
|
27
29
|
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
30
|
+
* ## One link
|
|
31
|
+
*
|
|
32
|
+
* The whole card is the link and the "Browse" pill is its visible affordance,
|
|
33
|
+
* not a second link — a pill inside a link is a nested anchor, and a pill that
|
|
34
|
+
* is the only link leaves the photography, the number and the name inert.
|
|
31
35
|
*/
|
|
32
36
|
/** @deprecated Alias of `MenuLinkProps`; kept so existing imports keep working. */
|
|
33
37
|
type MenuFamilyCardLinkProps = MenuLinkProps;
|
|
34
38
|
type MenuFamilyCardLabels = {
|
|
35
39
|
/** Unit under the count, e.g. "KITCHENS". */
|
|
36
40
|
venueUnit: string;
|
|
37
|
-
/**
|
|
38
|
-
|
|
39
|
-
|
|
41
|
+
/**
|
|
42
|
+
* Price-band caption, e.g. "kitchen price band". Never drawn — it names the
|
|
43
|
+
* pill for a screen reader, which would otherwise be read two numbers and a
|
|
44
|
+
* currency.
|
|
45
|
+
*/
|
|
40
46
|
priceBand: string;
|
|
47
|
+
/** The action pill, e.g. "Browse Italian". Sits on the photography — keep it short. */
|
|
48
|
+
browse: string;
|
|
41
49
|
};
|
|
42
50
|
type MenuFamilyCardProps = {
|
|
43
51
|
family: MenuFamilyPreview;
|
|
44
52
|
labels: MenuFamilyCardLabels;
|
|
45
|
-
/** Passed through to the featured `<MenuItem>`. */
|
|
46
|
-
itemLabels: MenuItemLabels;
|
|
47
|
-
formatPrice: (amount: number) => string;
|
|
48
|
-
formatCount?: (value: number) => string;
|
|
49
53
|
href: string;
|
|
50
|
-
/** Finished CSS colour for the
|
|
54
|
+
/** Finished CSS colour for the chips. */
|
|
51
55
|
hue?: string;
|
|
56
|
+
/** Finished CSS gradient — the family's colour over the mosaic. */
|
|
57
|
+
gradient?: string;
|
|
52
58
|
/** Composed price band, e.g. "32–84 lei". */
|
|
53
59
|
priceBandLabel?: string;
|
|
54
60
|
linkComponent?: MenuLinkComponent;
|
|
55
61
|
className?: string;
|
|
56
62
|
};
|
|
57
|
-
declare function MenuFamilyCard({ family, labels,
|
|
63
|
+
declare function MenuFamilyCard({ family, labels, href, hue, gradient, priceBandLabel, linkComponent: LinkComponent, className, }: MenuFamilyCardProps): react_jsx_runtime.JSX.Element;
|
|
58
64
|
|
|
59
65
|
export { MenuFamilyCard, type MenuFamilyCardLabels, type MenuFamilyCardLinkProps, type MenuFamilyCardProps };
|