@bearmenu/ui 0.8.8 → 0.8.10
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-2XCZJWTV.js +121 -0
- package/dist/chunk-5RWTOT5Y.js +72 -0
- package/dist/chunk-62ES6N32.js +27 -0
- package/dist/chunk-AKQ57NXY.js +58 -0
- 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-MZL7CGCL.js +109 -0
- package/dist/chunk-Q6QH52LJ.js +38 -0
- package/dist/chunk-SY6RV476.js +48 -0
- package/dist/chunk-TH6W2PWG.js +213 -0
- package/dist/chunk-UDN7UHJW.js +60 -0
- package/dist/{chunk-TT3WFS6S.js → chunk-YOKHBAIH.js} +11 -11
- package/dist/components/alert.d.ts +2 -2
- package/dist/components/badge.d.ts +1 -1
- 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 +110 -0
- package/dist/components/concept-compare.js +238 -0
- 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/date-input.d.ts +1 -1
- package/dist/components/event-card.js +1 -1
- package/dist/components/event-compact-row.d.ts +1 -1
- package/dist/components/event-compact-row.js +1 -1
- package/dist/components/event-detail-panel.d.ts +171 -25
- package/dist/components/event-detail-panel.js +472 -175
- 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/glass-ground.d.ts +66 -0
- package/dist/components/glass-ground.js +3 -0
- package/dist/components/hub-surface.d.ts +27 -0
- package/dist/components/hub-surface.js +30 -0
- package/dist/components/hue-chip.d.ts +70 -0
- package/dist/components/hue-chip.js +3 -0
- package/dist/components/kitchen-dossier.d.ts +88 -0
- package/dist/components/kitchen-dossier.js +187 -0
- 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 +99 -0
- package/dist/components/menu-family-block.js +155 -0
- package/dist/components/menu-family-card.d.ts +65 -0
- package/dist/components/menu-family-card.js +79 -0
- package/dist/components/menu-field.d.ts +57 -0
- package/dist/components/menu-field.js +93 -0
- 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.d.ts +65 -0
- package/dist/components/menu-hub-hero.js +103 -0
- 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-intent-tiles.d.ts +31 -0
- package/dist/components/menu-intent-tiles.js +50 -0
- package/dist/components/menu-item.d.ts +127 -0
- package/dist/components/menu-item.js +5 -0
- 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 +90 -0
- package/dist/components/menu-mocks.js +504 -0
- package/dist/components/menu-types.d.ts +421 -0
- package/dist/components/menu-types.js +1 -0
- package/dist/components/motion-icon-controls.js +2 -2
- package/dist/components/motion-icon-set.js +21 -9
- package/dist/components/motion-icon.d.ts +22 -1
- package/dist/components/motion-icon.js +1 -1
- package/dist/components/open-now-list.d.ts +40 -0
- package/dist/components/open-now-list.js +69 -0
- 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 +2 -2
- package/dist/components/place-types.d.ts +112 -1
- 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/signature-chip.d.ts +37 -0
- package/dist/components/signature-chip.js +4 -0
- package/dist/components/snap-rail.d.ts +48 -5
- package/dist/components/snap-rail.js +3 -51
- package/dist/components/stat-banner.d.ts +47 -0
- package/dist/components/stat-banner.js +61 -0
- package/dist/components/story.d.ts +34 -0
- package/dist/components/story.js +3 -0
- package/dist/components/tag.d.ts +2 -2
- package/dist/components/text.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 +38 -0
- package/dist/components/venue-grade-dimensions.js +3 -0
- package/dist/components/venue-quotes.d.ts +32 -0
- package/dist/components/venue-quotes.js +3 -0
- package/dist/components/venue-wall.d.ts +41 -0
- package/dist/components/venue-wall.js +118 -0
- package/dist/components/weekday-theme-banner.d.ts +70 -0
- package/dist/components/weekday-theme-banner.js +127 -0
- package/dist/components/weekend-panel.js +2 -2
- package/package.json +1 -1
- package/src/components/colour-dictionary.stories.tsx +227 -0
- package/src/components/concept-compare-rail.tsx +131 -0
- package/src/components/concept-compare.stories.tsx +172 -0
- package/src/components/concept-compare.tsx +443 -0
- 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/event-compact-row.tsx +1 -1
- package/src/components/event-detail-panel.stories.tsx +205 -97
- package/src/components/event-detail-panel.test.tsx +181 -0
- package/src/components/event-detail-panel.tsx +703 -185
- package/src/components/explainer-steps.stories.tsx +46 -0
- package/src/components/explainer-steps.tsx +74 -0
- package/src/components/glass-ground.tsx +129 -0
- package/src/components/hub-surface.stories.tsx +68 -0
- package/src/components/hub-surface.tsx +50 -0
- 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 +322 -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 +314 -0
- package/src/components/menu-family-card.stories.tsx +120 -0
- package/src/components/menu-family-card.tsx +191 -0
- package/src/components/menu-field.stories.tsx +74 -0
- package/src/components/menu-field.tsx +199 -0
- 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.stories.tsx +92 -0
- package/src/components/menu-hub-hero.tsx +187 -0
- 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-intent-tiles.stories.tsx +54 -0
- package/src/components/menu-intent-tiles.tsx +76 -0
- package/src/components/menu-item.tsx +416 -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 +592 -0
- package/src/components/menu-types.ts +442 -0
- package/src/components/motion-icon-controls.tsx +11 -11
- package/src/components/motion-icon-set.tsx +36 -8
- package/src/components/motion-icon.test.tsx +23 -1
- package/src/components/motion-icon.tsx +20 -0
- package/src/components/open-now-list.stories.tsx +76 -0
- package/src/components/open-now-list.tsx +127 -0
- package/src/components/photo-ground.tsx +147 -0
- package/src/components/place-types.ts +112 -0
- package/src/components/qa-block.stories.tsx +43 -0
- package/src/components/qa-block.tsx +60 -0
- package/src/components/signature-chip.tsx +77 -0
- package/src/components/snap-rail.tsx +133 -9
- package/src/components/stat-banner.stories.tsx +100 -0
- package/src/components/stat-banner.tsx +117 -0
- 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.stories.tsx +83 -0
- package/src/components/venue-grade-dimensions.tsx +91 -0
- package/src/components/venue-quotes.stories.tsx +59 -0
- package/src/components/venue-quotes.tsx +64 -0
- package/src/components/venue-wall.stories.tsx +78 -0
- package/src/components/venue-wall.tsx +185 -0
- package/src/components/weekday-theme-banner.stories.tsx +113 -0
- package/src/components/weekday-theme-banner.tsx +231 -0
- package/dist/{chunk-XWMRABZW.js → chunk-HSIYYA2E.js} +1 -1
|
@@ -0,0 +1,421 @@
|
|
|
1
|
+
import { ComponentType, CSSProperties, ReactNode } from 'react';
|
|
2
|
+
import { MenuSizeAndPrice, PlacePreview } from './place-types.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Structural mirror of the openapi `Dish` / `Drink` schemas, decoupled from any
|
|
6
|
+
* one app's generated types. FE, admin and post-generator map their domain
|
|
7
|
+
* shapes into these before passing data to the menu-hub surfaces.
|
|
8
|
+
*
|
|
9
|
+
* Same contract as `place-types.ts` and `event-types.ts`: no app schemas, no
|
|
10
|
+
* app hooks, no i18n. Every user-facing string arrives through a `*Labels`
|
|
11
|
+
* object, every destination through an `href`, and every locale-sensitive
|
|
12
|
+
* format through an injected formatter — the DS never owns a translation key
|
|
13
|
+
* or an `Intl` locale.
|
|
14
|
+
*
|
|
15
|
+
* ## One set of components, two tabs
|
|
16
|
+
*
|
|
17
|
+
* Dishes and drinks are the same shape. `MenuItemPreview.kind` is the only
|
|
18
|
+
* discriminator, and nothing in this file branches on it — a drinks hub is a
|
|
19
|
+
* different view model and a different `*Labels` object, never a second set of
|
|
20
|
+
* components. Anything that would need a `if (kind === 'drink')` inside a
|
|
21
|
+
* component belongs in the caller's mapper instead.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* The shape every menu surface's `linkComponent` must accept.
|
|
26
|
+
*
|
|
27
|
+
* One type rather than a clone per component: these surfaces nest each other —
|
|
28
|
+
* a family card renders a `<MenuItem>`, a spotlight renders a signature block —
|
|
29
|
+
* and a caller passes ONE `next/link` down through all of them. Subtly
|
|
30
|
+
* different clones made those hand-offs fail to typecheck for no design reason.
|
|
31
|
+
*/
|
|
32
|
+
type MenuLinkProps = {
|
|
33
|
+
href: string;
|
|
34
|
+
className?: string;
|
|
35
|
+
'aria-label'?: string;
|
|
36
|
+
'aria-current'?: 'page';
|
|
37
|
+
/** Intent tiles paint their hue on the link element itself. */
|
|
38
|
+
style?: CSSProperties;
|
|
39
|
+
children?: ReactNode;
|
|
40
|
+
};
|
|
41
|
+
type MenuLinkComponent = ComponentType<MenuLinkProps>;
|
|
42
|
+
/**
|
|
43
|
+
* A dish or a drink as every hub surface consumes it.
|
|
44
|
+
*
|
|
45
|
+
* `mentions` is the census count of review mentions and is populated on every
|
|
46
|
+
* item in the catalogue — it is the ranking signal the hub is built on. Do not
|
|
47
|
+
* confuse it with `review_count`, which counts full review objects and is zero
|
|
48
|
+
* for most items; a surface that ranks on `review_count` renders an empty page.
|
|
49
|
+
*/
|
|
50
|
+
type MenuItemPreview = {
|
|
51
|
+
id: string;
|
|
52
|
+
slug: string;
|
|
53
|
+
name: string;
|
|
54
|
+
kind: 'dish' | 'drink';
|
|
55
|
+
description?: string;
|
|
56
|
+
/** API category code (pizza, pasta, cocktail, non-alcoholic, …). Localised by the caller. */
|
|
57
|
+
category_slug?: string;
|
|
58
|
+
sizes_and_prices?: MenuSizeAndPrice[];
|
|
59
|
+
/** Censused review mentions. The hub's ranking signal. */
|
|
60
|
+
mentions?: number;
|
|
61
|
+
/** Full review objects citing this item. Sparse — not a ranking signal. */
|
|
62
|
+
review_count?: number;
|
|
63
|
+
/** 0–100 recommendation score. */
|
|
64
|
+
score?: number;
|
|
65
|
+
/** The venue serving it. Menu items are always venue-scoped. */
|
|
66
|
+
place?: MenuItemPlaceRef;
|
|
67
|
+
/**
|
|
68
|
+
* Pre-resolved photo URL. Menu-item photography is AI-generated and hidden in
|
|
69
|
+
* production, so hub surfaces draw a typographic `<MenuFieldTile>` instead
|
|
70
|
+
* and leave this unset. It exists for the surfaces that legitimately show one
|
|
71
|
+
* (concept detail) and for consumers outside the consumer app.
|
|
72
|
+
*/
|
|
73
|
+
photo_url?: string;
|
|
74
|
+
};
|
|
75
|
+
/** The venue attribution carried on a menu item. */
|
|
76
|
+
type MenuItemPlaceRef = {
|
|
77
|
+
slug: string;
|
|
78
|
+
name: string;
|
|
79
|
+
/** Pre-resolved venue thumbnail. Venue photography is real; item photography is not. */
|
|
80
|
+
thumbnail_url?: string;
|
|
81
|
+
};
|
|
82
|
+
/**
|
|
83
|
+
* A dish or drink concept — one item across every kitchen that makes it, under
|
|
84
|
+
* whatever name each of them uses. The page's keystone, and the one thing the
|
|
85
|
+
* catalogue can say that a single menu cannot.
|
|
86
|
+
*/
|
|
87
|
+
type MenuConceptPreview = {
|
|
88
|
+
slug: string;
|
|
89
|
+
/** Canonical concept name, e.g. "Margherita". */
|
|
90
|
+
title: string;
|
|
91
|
+
kind: 'dish' | 'drink';
|
|
92
|
+
/** Venues serving it. */
|
|
93
|
+
venue_count?: number;
|
|
94
|
+
price_low?: number;
|
|
95
|
+
price_high?: number;
|
|
96
|
+
/** Distinct names it appears under across those menus. */
|
|
97
|
+
alias_count?: number;
|
|
98
|
+
/** Summed censused mentions across every venue's version. */
|
|
99
|
+
mentions?: number;
|
|
100
|
+
rows?: MenuConceptRow[];
|
|
101
|
+
};
|
|
102
|
+
/** One venue's version of a concept, as drawn by `<ConceptCompare>`. */
|
|
103
|
+
type MenuConceptRow = {
|
|
104
|
+
place_slug: string;
|
|
105
|
+
place_name: string;
|
|
106
|
+
/** What this kitchen calls it on its own menu. */
|
|
107
|
+
alias: string;
|
|
108
|
+
price: number;
|
|
109
|
+
mentions?: number;
|
|
110
|
+
rating?: number;
|
|
111
|
+
/** Venue type line, e.g. "pub". Localised by the caller. */
|
|
112
|
+
place_type?: string;
|
|
113
|
+
/** Neighbourhood, e.g. "Centru". */
|
|
114
|
+
place_area?: string;
|
|
115
|
+
/**
|
|
116
|
+
* Pre-resolved VENUE photograph.
|
|
117
|
+
*
|
|
118
|
+
* The row is place-led: five restaurants that disagree about a name, not five
|
|
119
|
+
* bars of similar length. A price bar was drawn here first and it measured
|
|
120
|
+
* the one thing already written beside it in words, while the venue — the
|
|
121
|
+
* thing you are actually choosing between — had no image at all.
|
|
122
|
+
*/
|
|
123
|
+
place_thumbnail_url?: string;
|
|
124
|
+
open_now?: boolean;
|
|
125
|
+
/** Marks the row the surface should lead with (cheapest, or most named). */
|
|
126
|
+
featured?: boolean;
|
|
127
|
+
/** Portion, already formatted with its unit, e.g. "260g". */
|
|
128
|
+
portion_label?: string;
|
|
129
|
+
};
|
|
130
|
+
/**
|
|
131
|
+
* A browsable grouping of venues — a cuisine on the dishes tab, a drink family
|
|
132
|
+
* on the drinks tab. Both are sets of VENUES, never sets of items: cuisine
|
|
133
|
+
* exists only on places, so a card counting dishes cannot be built.
|
|
134
|
+
*/
|
|
135
|
+
type MenuFamilyPreview = {
|
|
136
|
+
slug: string;
|
|
137
|
+
/** Display name, already localised by the caller. */
|
|
138
|
+
name: string;
|
|
139
|
+
/** Venues in this family. */
|
|
140
|
+
venue_count: number;
|
|
141
|
+
/**
|
|
142
|
+
* Facet chips. Counts are VENUES, not items — they come from the places
|
|
143
|
+
* facet narrowed by this family. The drinks tab has no equivalent facet, so
|
|
144
|
+
* its chips ship without counts; `count` is optional for that reason.
|
|
145
|
+
*/
|
|
146
|
+
chips?: MenuFamilyChip[];
|
|
147
|
+
/** Price band across the family's venues. */
|
|
148
|
+
price_low?: number;
|
|
149
|
+
price_high?: number;
|
|
150
|
+
price_median?: number;
|
|
151
|
+
/** Pre-resolved venue photos for the mosaic. Venue photography only. */
|
|
152
|
+
venue_photo_urls?: string[];
|
|
153
|
+
};
|
|
154
|
+
type MenuFamilyChip = {
|
|
155
|
+
slug: string;
|
|
156
|
+
label: string;
|
|
157
|
+
/** Venue count. Absent where no facet backs the chip. */
|
|
158
|
+
count?: number;
|
|
159
|
+
};
|
|
160
|
+
/**
|
|
161
|
+
* A venue as the hub's venue-scoped surfaces consume it: the place itself plus
|
|
162
|
+
* the pre-resolved bits a hub row needs and `PlacePreview` does not carry —
|
|
163
|
+
* which signature to lead with, and where the row points.
|
|
164
|
+
*/
|
|
165
|
+
type MenuVenuePreview = {
|
|
166
|
+
place: PlacePreview;
|
|
167
|
+
href: string;
|
|
168
|
+
/**
|
|
169
|
+
* The signature to draw. Callers pick it from `reviews_digest.must_try`
|
|
170
|
+
* rather than the surface re-deriving the rule on every row.
|
|
171
|
+
*/
|
|
172
|
+
signature?: MenuVenueSignature;
|
|
173
|
+
/** Closing time as an already-formatted wall clock, e.g. "23:00". */
|
|
174
|
+
closes_at_label?: string;
|
|
175
|
+
};
|
|
176
|
+
/**
|
|
177
|
+
* The resolved signature on a venue row.
|
|
178
|
+
*
|
|
179
|
+
* It carries a whole `MenuItemPreview` rather than a flattened name and price,
|
|
180
|
+
* because every surface that draws one renders `<MenuItem>` — the same row a
|
|
181
|
+
* venue's own menu uses — and that component takes an item. Flattening it here
|
|
182
|
+
* was what let the hub's version drift into a different-looking row.
|
|
183
|
+
*/
|
|
184
|
+
type MenuVenueSignature = {
|
|
185
|
+
item: MenuItemPreview;
|
|
186
|
+
/** The reviewers' reason, from the digest. Rendered in place of the description. */
|
|
187
|
+
why?: string;
|
|
188
|
+
href?: string;
|
|
189
|
+
/** The digest called this the venue's signature rather than merely frequent. */
|
|
190
|
+
isSignature?: boolean;
|
|
191
|
+
};
|
|
192
|
+
/**
|
|
193
|
+
* An intent tile — a family plus a constraint, resolving to a category page.
|
|
194
|
+
*
|
|
195
|
+
* Only about thirty-two of the thirty-eight cuisines have a page, so `href` is
|
|
196
|
+
* required: a tile that cannot resolve to a real destination is not rendered
|
|
197
|
+
* rather than linking nowhere.
|
|
198
|
+
*/
|
|
199
|
+
type MenuIntentTile = {
|
|
200
|
+
slug: string;
|
|
201
|
+
label: string;
|
|
202
|
+
/** Supporting line, e.g. "247 kitchens". Already formatted by the caller. */
|
|
203
|
+
caption?: string;
|
|
204
|
+
href: string;
|
|
205
|
+
/**
|
|
206
|
+
* Finished CSS gradient for the tile's ground, e.g.
|
|
207
|
+
* `linear-gradient(150deg, …)`.
|
|
208
|
+
*
|
|
209
|
+
* A gradient rather than a token name, for the same reason `<MenuField>`
|
|
210
|
+
* takes one: the hue tables are app-owned, and a component referencing
|
|
211
|
+
* `var(--h-pizza)` renders an invisible tile in any consumer that does not
|
|
212
|
+
* happen to define that variable. A missing gradient falls back to `bg-muted`
|
|
213
|
+
* — visible and obviously unstyled, rather than white text on white.
|
|
214
|
+
*/
|
|
215
|
+
gradient?: string;
|
|
216
|
+
/** Icon node, resolved by the caller — the DS does not pick icons for it. */
|
|
217
|
+
icon?: ReactNode;
|
|
218
|
+
};
|
|
219
|
+
/**
|
|
220
|
+
* One figure in the intent hero — a number the reader can tap to filter by.
|
|
221
|
+
*
|
|
222
|
+
* The value arrives pre-formatted because the figures are not all the same
|
|
223
|
+
* quantity: "247" is a count and "58" is a count of a subset, but a caller may
|
|
224
|
+
* also want "4 past midnight", and a component that formatted numbers itself
|
|
225
|
+
* would need the locale it is forbidden to own.
|
|
226
|
+
*/
|
|
227
|
+
type MenuIntentFigure = {
|
|
228
|
+
key: string;
|
|
229
|
+
/** Pre-formatted, e.g. "247". */
|
|
230
|
+
value: string;
|
|
231
|
+
/** The unit, e.g. "kitchens" or "make a pizza". */
|
|
232
|
+
label: string;
|
|
233
|
+
/**
|
|
234
|
+
* The state this figure applies, as a URL.
|
|
235
|
+
*
|
|
236
|
+
* An `href` rather than only a callback because the filter state lives in the
|
|
237
|
+
* query string: a link is server-rendered, crawlable, middle-clickable and
|
|
238
|
+
* survives with no JavaScript, and `?open=now` is an indexable state we
|
|
239
|
+
* actively want followed. `onSelect` remains for consumers holding the state
|
|
240
|
+
* in memory; `href` wins when both are set.
|
|
241
|
+
*/
|
|
242
|
+
href?: string;
|
|
243
|
+
/**
|
|
244
|
+
* Applies this figure as a filter without navigating. A figure with neither
|
|
245
|
+
* `href` nor `onSelect` renders as a static fact rather than as a control
|
|
246
|
+
* that does nothing — the total is usually one, since filtering to "all of
|
|
247
|
+
* them" is not a state.
|
|
248
|
+
*/
|
|
249
|
+
onSelect?: () => void;
|
|
250
|
+
/** The filter this figure applies is already on. */
|
|
251
|
+
active?: boolean;
|
|
252
|
+
};
|
|
253
|
+
/**
|
|
254
|
+
* A filter chip on the intent page.
|
|
255
|
+
*
|
|
256
|
+
* Every chip counts KITCHENS, whatever facet backs it — a dietary chip reads
|
|
257
|
+
* "has a vegetarian dish" and counts venues, not dishes. Mixing venue counts
|
|
258
|
+
* and item counts in one row is what made the earlier version unreadable, so
|
|
259
|
+
* the type carries no unit: there is only ever one.
|
|
260
|
+
*/
|
|
261
|
+
type MenuFilterChip = {
|
|
262
|
+
key: string;
|
|
263
|
+
label: string;
|
|
264
|
+
/** Kitchens this chip would leave. Absent where no facet backs it. */
|
|
265
|
+
count?: number;
|
|
266
|
+
active?: boolean;
|
|
267
|
+
/**
|
|
268
|
+
* Zero kitchens under the current filters — dimmed, but STILL TAPPABLE.
|
|
269
|
+
*
|
|
270
|
+
* Tapping swaps it in and drops whichever active filter conflicts, with a
|
|
271
|
+
* line saying which. A disabled chip would leave the reader guessing why the
|
|
272
|
+
* thing they want is unavailable; this one answers it by being pressed.
|
|
273
|
+
*/
|
|
274
|
+
empty?: boolean;
|
|
275
|
+
/** Icon node, resolved by the caller. */
|
|
276
|
+
icon?: ReactNode;
|
|
277
|
+
};
|
|
278
|
+
/**
|
|
279
|
+
* A venue row on the intent page — the page's spine.
|
|
280
|
+
*
|
|
281
|
+
* Distinct from `MenuVenuePreview` (the hub's ranked row) in one structural
|
|
282
|
+
* way that is the whole point of the surface: the dish carries its own `href`,
|
|
283
|
+
* so the row has TWO targets. The row goes to the venue; the dish name opens
|
|
284
|
+
* the item drawer. `RankedVenueList` wraps its entire row in one link and
|
|
285
|
+
* therefore cannot express this, which is why this is a separate shape rather
|
|
286
|
+
* than a flag on that one.
|
|
287
|
+
*/
|
|
288
|
+
type MenuKitchenPreview = {
|
|
289
|
+
place: PlacePreview;
|
|
290
|
+
/** The venue page. The row's primary target. */
|
|
291
|
+
href: string;
|
|
292
|
+
/** Venue type and district, composed and localised by the caller, e.g. "pizzeria · Centru". */
|
|
293
|
+
metaLabel?: string;
|
|
294
|
+
/**
|
|
295
|
+
* "kitchen until 23:00", or "closed · opens 12:00". Composed by the caller
|
|
296
|
+
* because the wording differs by scope — a bar is not a kitchen.
|
|
297
|
+
*/
|
|
298
|
+
hoursLabel?: string;
|
|
299
|
+
dish?: MenuKitchenDish;
|
|
300
|
+
/** Pre-formatted, e.g. "62 lei". */
|
|
301
|
+
priceLabel?: string;
|
|
302
|
+
};
|
|
303
|
+
/** The dish a kitchen row leads with. The row's second target. */
|
|
304
|
+
type MenuKitchenDish = {
|
|
305
|
+
name: string;
|
|
306
|
+
/**
|
|
307
|
+
* Opens the item drawer. Omit for an inert dish line — a must-try the
|
|
308
|
+
* reviews named that has no live menu row behind it.
|
|
309
|
+
*/
|
|
310
|
+
href?: string;
|
|
311
|
+
/** Kicker over the name: "MOST NAMED HERE", or "WHY IT MATCHES" when filtered. */
|
|
312
|
+
kicker: string;
|
|
313
|
+
/** Under the name: "17 reviews name it", or "mid-range · 300g" when matched. */
|
|
314
|
+
meta?: string;
|
|
315
|
+
/**
|
|
316
|
+
* This row is answering a filter rather than volunteering its most-named
|
|
317
|
+
* dish. Draws the match mark and tints the block, so a reader scanning a
|
|
318
|
+
* filtered list can see that every row changed its reason for being there.
|
|
319
|
+
*/
|
|
320
|
+
matched?: boolean;
|
|
321
|
+
/** The filter it matched, e.g. "vegetarian". */
|
|
322
|
+
matchLabel?: string;
|
|
323
|
+
};
|
|
324
|
+
/**
|
|
325
|
+
* One row in a browse index — the complete set, at a glance.
|
|
326
|
+
*
|
|
327
|
+
* The index is the page's whole SEO job: it carries a crawlable link to every
|
|
328
|
+
* destination the city has, which is what frees the richer blocks below it to
|
|
329
|
+
* be progressive. So it holds nothing but what a reader needs to choose — a
|
|
330
|
+
* name, a count and a colour.
|
|
331
|
+
*/
|
|
332
|
+
type MenuIndexEntry = {
|
|
333
|
+
slug: string;
|
|
334
|
+
name: string;
|
|
335
|
+
count: number;
|
|
336
|
+
href: string;
|
|
337
|
+
/** Finished CSS gradient for the mark. Falls back to the border colour. */
|
|
338
|
+
gradient?: string;
|
|
339
|
+
};
|
|
340
|
+
/**
|
|
341
|
+
* A family on the cuisines index — the hub's card, opened out sideways.
|
|
342
|
+
*
|
|
343
|
+
* Deliberately thin. The reader here is choosing a cuisine, not a venue, and
|
|
344
|
+
* every field the block does NOT carry is a decision the next page owns.
|
|
345
|
+
*/
|
|
346
|
+
type MenuFamilyBlockPreview = {
|
|
347
|
+
slug: string;
|
|
348
|
+
name: string;
|
|
349
|
+
venueCount: number;
|
|
350
|
+
/** Facet chips. Labels only — the block never prints their counts. */
|
|
351
|
+
chips?: MenuFamilyChip[];
|
|
352
|
+
/** Composed price band, e.g. "22–86 lei". */
|
|
353
|
+
priceBandLabel?: string;
|
|
354
|
+
/** Venue photography only — item photography is AI-generated and never shown. */
|
|
355
|
+
photoUrls?: string[];
|
|
356
|
+
/**
|
|
357
|
+
* Venues, as kitchen rows. The block draws every one it is given — the caller
|
|
358
|
+
* decides how many, because how much of a claim the block makes is the page's
|
|
359
|
+
* decision, not the component's.
|
|
360
|
+
*
|
|
361
|
+
* The same `MenuKitchenPreview` the intent page's list takes, rendered by the
|
|
362
|
+
* same component — a family block that grew its own venue row would be the
|
|
363
|
+
* fourth near-identical row in the package, and they drift the moment one of
|
|
364
|
+
* them gets a fix.
|
|
365
|
+
*/
|
|
366
|
+
venues?: MenuKitchenPreview[];
|
|
367
|
+
/** The family's own page. */
|
|
368
|
+
href: string;
|
|
369
|
+
};
|
|
370
|
+
/**
|
|
371
|
+
* One "loosen this" row on a dead end — a filter, and what dropping it returns.
|
|
372
|
+
*
|
|
373
|
+
* The count is the point: "drop Open now" is a guess, "+8 kitchens" is an
|
|
374
|
+
* answer, and a dead end that cannot say which filter costs the most is just
|
|
375
|
+
* an empty state with extra steps.
|
|
376
|
+
*/
|
|
377
|
+
type MenuLoosenOption = {
|
|
378
|
+
key: string;
|
|
379
|
+
/** e.g. 'Drop "Open now"'. */
|
|
380
|
+
label: string;
|
|
381
|
+
/** e.g. "+8 kitchens". Pre-formatted. */
|
|
382
|
+
gainLabel: string;
|
|
383
|
+
/**
|
|
384
|
+
* The state without this filter, as a URL.
|
|
385
|
+
*
|
|
386
|
+
* An `href` because dropping a filter IS a navigation wherever the filter set
|
|
387
|
+
* lives in the query string — which makes it server-renderable, shareable and
|
|
388
|
+
* undoable with the back button. `onSelect` remains for consumers holding the
|
|
389
|
+
* state in memory; `href` wins when both are set.
|
|
390
|
+
*/
|
|
391
|
+
href?: string;
|
|
392
|
+
onSelect?: () => void;
|
|
393
|
+
};
|
|
394
|
+
/** One question and its answer. Static prose — never an accordion; see `<QaBlock>`. */
|
|
395
|
+
type QaItem = {
|
|
396
|
+
question: string;
|
|
397
|
+
answer: string;
|
|
398
|
+
};
|
|
399
|
+
/** Formatters injected into every menu surface. The DS owns no `Intl` locale. */
|
|
400
|
+
type MenuFormatters = {
|
|
401
|
+
/** 24 → "24 lei". */
|
|
402
|
+
price: (value: number) => string;
|
|
403
|
+
/** 1717 → "1,717". */
|
|
404
|
+
number: (value: number) => string;
|
|
405
|
+
};
|
|
406
|
+
/** Strings shared by every menu-hub surface. */
|
|
407
|
+
type MenuHubLabels = {
|
|
408
|
+
/** Mention-count badge. Pass a translator that handles ICU pluralization. */
|
|
409
|
+
mentions: (count: number) => string;
|
|
410
|
+
/** Venue-count line. Pass a translator that handles ICU pluralization. */
|
|
411
|
+
venues: (count: number) => string;
|
|
412
|
+
/** "See all" affordance on a section header. */
|
|
413
|
+
seeAll: string;
|
|
414
|
+
/** Badge on a must-try row the digest called `signature`. */
|
|
415
|
+
signature: string;
|
|
416
|
+
openNow: string;
|
|
417
|
+
/** Prefixes a closing time, e.g. "until". */
|
|
418
|
+
until: string;
|
|
419
|
+
};
|
|
420
|
+
|
|
421
|
+
export { type MenuConceptPreview, type MenuConceptRow, type MenuFamilyBlockPreview, type MenuFamilyChip, type MenuFamilyPreview, type MenuFilterChip, type MenuFormatters, type MenuHubLabels, type MenuIndexEntry, type MenuIntentFigure, type MenuIntentTile, type MenuItemPlaceRef, type MenuItemPreview, type MenuKitchenDish, type MenuKitchenPreview, type MenuLinkComponent, type MenuLinkProps, type MenuLoosenOption, MenuSizeAndPrice, type MenuVenuePreview, type MenuVenueSignature, type QaItem };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { MotionBookmark, MotionCheck, MotionChevron, MotionCopy, MotionEye, MotionHeart, MotionPlayPause, MotionRefresh, MotionSearch, MotionShare, MotionSliders, MotionSort, MotionStar, MotionTheme } from '../chunk-
|
|
2
|
-
import '../chunk-
|
|
1
|
+
export { MotionBookmark, MotionCheck, MotionChevron, MotionCopy, MotionEye, MotionHeart, MotionPlayPause, MotionRefresh, MotionSearch, MotionShare, MotionSliders, MotionSort, MotionStar, MotionTheme } from '../chunk-YOKHBAIH.js';
|
|
2
|
+
import '../chunk-G7IFL4Z4.js';
|
|
3
3
|
import '../chunk-FEK5XGZW.js';
|
|
4
4
|
import '../chunk-3GWWZKX7.js';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { beats, MotionIcon, returnTransition } from '../chunk-
|
|
1
|
+
import { beats, pivot, MotionIcon, returnTransition } from '../chunk-G7IFL4Z4.js';
|
|
2
2
|
import '../chunk-FEK5XGZW.js';
|
|
3
3
|
import { __spreadProps, __spreadValues } from '../chunk-3GWWZKX7.js';
|
|
4
4
|
import * as React from 'react';
|
|
@@ -28,11 +28,11 @@ function MotionHouse(props) {
|
|
|
28
28
|
motion.path,
|
|
29
29
|
{
|
|
30
30
|
variants: houseRoof,
|
|
31
|
-
style:
|
|
31
|
+
style: pivot(12, 10),
|
|
32
32
|
d: "M3 10a2 2 0 0 1 .709-1.528l7-6a2 2 0 0 1 2.582 0l7 6A2 2 0 0 1 21 10"
|
|
33
33
|
}
|
|
34
34
|
),
|
|
35
|
-
/* @__PURE__ */ jsxs(motion.g, { variants: houseWalls, style:
|
|
35
|
+
/* @__PURE__ */ jsxs(motion.g, { variants: houseWalls, style: pivot(12, 21), children: [
|
|
36
36
|
/* @__PURE__ */ jsx("path", { d: "M21 10v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V10" }),
|
|
37
37
|
/* @__PURE__ */ jsx("path", { d: "M15 21v-8a1 1 0 0 0-1-1h-4a1 1 0 0 0-1 1v8" })
|
|
38
38
|
] })
|
|
@@ -40,12 +40,14 @@ function MotionHouse(props) {
|
|
|
40
40
|
}
|
|
41
41
|
var UTENSILS_TIMES = [0, 0.18, 0.72, 1];
|
|
42
42
|
var SEPARATION = 5;
|
|
43
|
+
var FORK_LIFT = -1.5;
|
|
43
44
|
var fork = {
|
|
44
|
-
rest: { rotate: 0, x: 0, transition: returnTransition },
|
|
45
|
-
active: { rotate: -44, x: -SEPARATION, transition: returnTransition },
|
|
45
|
+
rest: { rotate: 0, x: 0, y: 0, transition: returnTransition },
|
|
46
|
+
active: { rotate: -44, x: -SEPARATION, y: FORK_LIFT, transition: returnTransition },
|
|
46
47
|
play: {
|
|
47
48
|
rotate: [0, 6, -50, -44],
|
|
48
49
|
x: [0, 0.8, -6, -SEPARATION],
|
|
50
|
+
y: [0, 0, FORK_LIFT - 0.3, FORK_LIFT],
|
|
49
51
|
transition: beats(UTENSILS_TIMES, 0.6)
|
|
50
52
|
}
|
|
51
53
|
};
|
|
@@ -58,7 +60,17 @@ var knife = {
|
|
|
58
60
|
transition: beats(UTENSILS_TIMES, 0.6)
|
|
59
61
|
}
|
|
60
62
|
};
|
|
61
|
-
var CROSSING =
|
|
63
|
+
var CROSSING = pivot(12, 12);
|
|
64
|
+
var HANDLE_CROSSED = "M2.1 21.8 8.5 15.4";
|
|
65
|
+
var HANDLE_CLEAR = "M2.1 21.8 12 12";
|
|
66
|
+
var forkHandle = {
|
|
67
|
+
rest: { d: HANDLE_CROSSED, transition: returnTransition },
|
|
68
|
+
active: { d: HANDLE_CLEAR, transition: returnTransition },
|
|
69
|
+
play: {
|
|
70
|
+
d: [HANDLE_CROSSED, HANDLE_CROSSED, HANDLE_CLEAR, HANDLE_CLEAR],
|
|
71
|
+
transition: beats(UTENSILS_TIMES, 0.6)
|
|
72
|
+
}
|
|
73
|
+
};
|
|
62
74
|
function MotionUtensils(props) {
|
|
63
75
|
return /* @__PURE__ */ jsxs(MotionIcon, __spreadProps(__spreadValues({}, props), { children: [
|
|
64
76
|
/* @__PURE__ */ jsx(
|
|
@@ -71,7 +83,7 @@ function MotionUtensils(props) {
|
|
|
71
83
|
),
|
|
72
84
|
/* @__PURE__ */ jsxs(motion.g, { variants: fork, style: CROSSING, children: [
|
|
73
85
|
/* @__PURE__ */ jsx("path", { d: "m16 2-2.3 2.3a3 3 0 0 0 0 4.2l1.8 1.8a3 3 0 0 0 4.2 0L22 8" }),
|
|
74
|
-
/* @__PURE__ */ jsx(
|
|
86
|
+
/* @__PURE__ */ jsx(motion.path, { variants: forkHandle, d: HANDLE_CROSSED }),
|
|
75
87
|
/* @__PURE__ */ jsx("path", { d: "m19 5-7 7" })
|
|
76
88
|
] })
|
|
77
89
|
] }));
|
|
@@ -96,13 +108,13 @@ var pinDot = {
|
|
|
96
108
|
}
|
|
97
109
|
};
|
|
98
110
|
function MotionMapPin(props) {
|
|
99
|
-
return /* @__PURE__ */ jsx(MotionIcon, __spreadProps(__spreadValues({}, props), { children: /* @__PURE__ */ jsxs(motion.g, { variants: pinBody, style:
|
|
111
|
+
return /* @__PURE__ */ jsx(MotionIcon, __spreadProps(__spreadValues({}, props), { children: /* @__PURE__ */ jsxs(motion.g, { variants: pinBody, style: pivot(12, 22), children: [
|
|
100
112
|
/* @__PURE__ */ jsx("path", { d: "M20 10c0 4.993-5.539 10.193-7.399 11.799a1 1 0 0 1-1.202 0C9.539 20.193 4 14.993 4 10a8 8 0 0 1 16 0" }),
|
|
101
113
|
/* @__PURE__ */ jsx(
|
|
102
114
|
motion.circle,
|
|
103
115
|
{
|
|
104
116
|
variants: pinDot,
|
|
105
|
-
style:
|
|
117
|
+
style: pivot(12, 10),
|
|
106
118
|
fill: "currentColor",
|
|
107
119
|
fillOpacity: 0,
|
|
108
120
|
cx: "12",
|
|
@@ -23,6 +23,27 @@ type MotionIconVariant = "rest" | "active" | "play";
|
|
|
23
23
|
declare function beats(times: number[], duration?: number): Transition;
|
|
24
24
|
/** Leaving is not a performance — it returns the glyph, firmly and fast. */
|
|
25
25
|
declare const returnTransition: Transition;
|
|
26
|
+
/**
|
|
27
|
+
* A moving part's pivot, in the icon's 24-unit VIEWBOX coordinates.
|
|
28
|
+
*
|
|
29
|
+
* Every part in this family wrote `style={{ originX: "12px", originY: "12px" }}`
|
|
30
|
+
* meaning "the centre of the icon" — and every one of them was pivoting
|
|
31
|
+
* somewhere else. Framer Motion sets `transform-box: fill-box` on any SVG
|
|
32
|
+
* element it transforms, which makes `transform-origin` relative to that
|
|
33
|
+
* element's OWN bounding box, not the viewBox. The chevron `m6 9 6 6 6-6` has
|
|
34
|
+
* its box at (6, 9), so "12px 12px" resolved to user-space (18, 21): a 180°
|
|
35
|
+
* flip around a point outside the arrow, which is why it visibly slid as it
|
|
36
|
+
* turned. A house's walls pivoting at its floor, a pin at its tip, a lens at
|
|
37
|
+
* its centre — all of them missed by the same offset for the same reason.
|
|
38
|
+
*
|
|
39
|
+
* `transform-box: view-box` is the single switch that makes px origins mean
|
|
40
|
+
* what they say. Declared once here so the next icon cannot get it wrong.
|
|
41
|
+
*/
|
|
42
|
+
declare function pivot(x: number, y: number): {
|
|
43
|
+
readonly originX: `${number}px`;
|
|
44
|
+
readonly originY: `${number}px`;
|
|
45
|
+
readonly transformBox: "view-box";
|
|
46
|
+
};
|
|
26
47
|
type PoseValues = Record<string, number>;
|
|
27
48
|
/**
|
|
28
49
|
* Builds the three variants for a moving part.
|
|
@@ -41,4 +62,4 @@ declare function MotionIcon({ active, replayToken, className, children, }: Motio
|
|
|
41
62
|
children: React.ReactNode;
|
|
42
63
|
}): react_jsx_runtime.JSX.Element;
|
|
43
64
|
|
|
44
|
-
export { MotionIcon, type MotionIconProps, type MotionIconVariant, beats, poses, returnTransition, useIconGesture };
|
|
65
|
+
export { MotionIcon, type MotionIconProps, type MotionIconVariant, beats, pivot, poses, returnTransition, useIconGesture };
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { MenuVenuePreview, MenuLinkComponent, MenuLinkProps } from './menu-types.js';
|
|
3
|
+
import 'react';
|
|
4
|
+
import './place-types.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* OpenNowList — who is still serving, and until when.
|
|
8
|
+
*
|
|
9
|
+
* At half past ten this beats every other sort, which is why it is its own
|
|
10
|
+
* surface rather than a filter chip: the question is not "what is good" but
|
|
11
|
+
* "what is possible", and every other module on a hub answers the first one.
|
|
12
|
+
*
|
|
13
|
+
* The closing time arrives pre-formatted on the view model (`closes_at_label`)
|
|
14
|
+
* because the venue's own trading day can cross midnight — a bar closing at
|
|
15
|
+
* 04:00 closes on the following date — and resolving that needs the venue's
|
|
16
|
+
* timezone and the reader's clock, neither of which the DS has.
|
|
17
|
+
*
|
|
18
|
+
* The live dot is never the only signal that a venue is open: the closing pill
|
|
19
|
+
* carries the same fact in words, so the row reads without colour.
|
|
20
|
+
*
|
|
21
|
+
* Server-safe.
|
|
22
|
+
*/
|
|
23
|
+
/** @deprecated Alias of `MenuLinkProps`; kept so existing imports keep working. */
|
|
24
|
+
type OpenNowListLinkProps = MenuLinkProps;
|
|
25
|
+
type OpenNowListProps = {
|
|
26
|
+
venues: MenuVenuePreview[];
|
|
27
|
+
/** Prefix on the closing pill, e.g. "until". */
|
|
28
|
+
untilLabel: string;
|
|
29
|
+
/** Composed meta line per venue, keyed by place slug, e.g. "pub · Centru". */
|
|
30
|
+
metaLabels?: Record<string, string>;
|
|
31
|
+
/** What the kitchen is still sending out, keyed by place slug. */
|
|
32
|
+
summaryLabels?: Record<string, string>;
|
|
33
|
+
/** Composed price band per venue, keyed by place slug, e.g. "19—62 lei". */
|
|
34
|
+
priceBandLabels?: Record<string, string>;
|
|
35
|
+
linkComponent?: MenuLinkComponent;
|
|
36
|
+
className?: string;
|
|
37
|
+
};
|
|
38
|
+
declare function OpenNowList({ venues, untilLabel, metaLabels, summaryLabels, priceBandLabels, linkComponent: LinkComponent, className, }: OpenNowListProps): react_jsx_runtime.JSX.Element;
|
|
39
|
+
|
|
40
|
+
export { OpenNowList, type OpenNowListLinkProps, type OpenNowListProps };
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { Separator } from '../chunk-WSRWFA6K.js';
|
|
2
|
+
import { cn } from '../chunk-FEK5XGZW.js';
|
|
3
|
+
import { __objRest, __spreadValues } from '../chunk-3GWWZKX7.js';
|
|
4
|
+
import { Clock, ChevronRight } from 'lucide-react';
|
|
5
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
6
|
+
|
|
7
|
+
function DefaultLink(_a) {
|
|
8
|
+
var _b = _a, { href } = _b, props = __objRest(_b, ["href"]);
|
|
9
|
+
return /* @__PURE__ */ jsx("a", __spreadValues({ href }, props));
|
|
10
|
+
}
|
|
11
|
+
function OpenNowList({
|
|
12
|
+
venues,
|
|
13
|
+
untilLabel,
|
|
14
|
+
metaLabels,
|
|
15
|
+
summaryLabels,
|
|
16
|
+
priceBandLabels,
|
|
17
|
+
linkComponent: LinkComponent = DefaultLink,
|
|
18
|
+
className
|
|
19
|
+
}) {
|
|
20
|
+
return /* @__PURE__ */ jsx("ul", { className: cn("flex flex-col", className), children: venues.map((venue, index) => {
|
|
21
|
+
const { place } = venue;
|
|
22
|
+
return /* @__PURE__ */ jsxs("li", { children: [
|
|
23
|
+
index > 0 && /* @__PURE__ */ jsx(Separator, {}),
|
|
24
|
+
/* @__PURE__ */ jsxs(
|
|
25
|
+
LinkComponent,
|
|
26
|
+
{
|
|
27
|
+
href: venue.href,
|
|
28
|
+
className: "flex flex-wrap items-center gap-x-3 gap-y-1.5 py-3 transition-colors hover:bg-muted/40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring",
|
|
29
|
+
children: [
|
|
30
|
+
/* @__PURE__ */ jsxs("span", { className: "relative h-14 w-14 shrink-0 overflow-hidden rounded-xl bg-[repeating-linear-gradient(135deg,oklch(0.78_0.018_55)_0_8px,oklch(0.84_0.012_58)_8px_16px)]", children: [
|
|
31
|
+
place.thumbnail_url && // eslint-disable-next-line @next/next/no-img-element
|
|
32
|
+
/* @__PURE__ */ jsx(
|
|
33
|
+
"img",
|
|
34
|
+
{
|
|
35
|
+
src: place.thumbnail_url,
|
|
36
|
+
alt: "",
|
|
37
|
+
className: "h-full w-full object-cover",
|
|
38
|
+
loading: "lazy"
|
|
39
|
+
}
|
|
40
|
+
),
|
|
41
|
+
/* @__PURE__ */ jsx(
|
|
42
|
+
"i",
|
|
43
|
+
{
|
|
44
|
+
"aria-hidden": true,
|
|
45
|
+
className: "absolute right-1 top-1 h-[7px] w-[7px] rounded-full bg-[var(--free,currentColor)] ring-2 ring-white/80"
|
|
46
|
+
}
|
|
47
|
+
)
|
|
48
|
+
] }),
|
|
49
|
+
/* @__PURE__ */ jsxs("span", { className: "min-w-0 flex-1", children: [
|
|
50
|
+
/* @__PURE__ */ jsx("span", { className: "block text-sm font-bold leading-snug tracking-[-0.01em]", children: place.name }),
|
|
51
|
+
(metaLabels == null ? void 0 : metaLabels[place.slug]) && /* @__PURE__ */ jsx("span", { className: "mt-px block truncate text-[11.5px] text-muted-foreground", children: metaLabels[place.slug] })
|
|
52
|
+
] }),
|
|
53
|
+
(summaryLabels == null ? void 0 : summaryLabels[place.slug]) && /* @__PURE__ */ jsx("span", { className: "order-last w-full truncate text-[12.5px] text-muted-foreground lg:order-none lg:w-auto lg:flex-1", children: summaryLabels[place.slug] }),
|
|
54
|
+
(venue.closes_at_label || (priceBandLabels == null ? void 0 : priceBandLabels[place.slug])) && /* @__PURE__ */ jsxs("span", { className: "flex shrink-0 flex-col items-end gap-1 lg:flex-row lg:items-center lg:gap-3", children: [
|
|
55
|
+
(priceBandLabels == null ? void 0 : priceBandLabels[place.slug]) && /* @__PURE__ */ jsx("span", { className: "text-[12.5px] tabular-nums text-muted-foreground", children: priceBandLabels[place.slug] }),
|
|
56
|
+
venue.closes_at_label && /* @__PURE__ */ jsxs("span", { className: "inline-flex h-6 items-center gap-1 rounded-full bg-[var(--free-subtle,var(--accent-subtle))] px-2.5 text-[11.5px] font-semibold tabular-nums", children: [
|
|
57
|
+
/* @__PURE__ */ jsx(Clock, { className: "h-[11px] w-[11px]", "aria-hidden": true }),
|
|
58
|
+
`${untilLabel} ${venue.closes_at_label}`
|
|
59
|
+
] })
|
|
60
|
+
] }),
|
|
61
|
+
/* @__PURE__ */ jsx(ChevronRight, { className: "h-4 w-4 shrink-0 text-muted-foreground", "aria-hidden": true })
|
|
62
|
+
]
|
|
63
|
+
}
|
|
64
|
+
)
|
|
65
|
+
] }, place.slug);
|
|
66
|
+
}) });
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export { OpenNowList };
|