@bearmenu/ui 0.8.13 → 0.8.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (285) hide show
  1. package/dist/{chunk-66MAPLXS.js → chunk-2ADCZXZJ.js} +1 -1
  2. package/dist/{chunk-2DEIXTBN.js → chunk-2ZXIXQIU.js} +1 -1
  3. package/dist/{chunk-YOKHBAIH.js → chunk-4OF4T4AI.js} +12 -11
  4. package/dist/{chunk-MZL7CGCL.js → chunk-4XDJXMLF.js} +2 -2
  5. package/dist/{chunk-YYGB4G4Q.js → chunk-CZK2MRS2.js} +1 -1
  6. package/dist/chunk-DJD3EMPK.js +126 -0
  7. package/dist/chunk-EZG2NELX.js +5 -0
  8. package/dist/{chunk-VAXOQS7W.js → chunk-F76NCVVR.js} +1 -1
  9. package/dist/{chunk-VG3PND3D.js → chunk-FMTJGO76.js} +1 -1
  10. package/dist/{chunk-UXWAOISG.js → chunk-FVSKGCK3.js} +2 -2
  11. package/dist/{chunk-2XCZJWTV.js → chunk-GNCWJWFE.js} +1 -1
  12. package/dist/{chunk-X3WMRKOW.js → chunk-HDR7V6IZ.js} +1 -1
  13. package/dist/{chunk-HSIYYA2E.js → chunk-HQU6QVAN.js} +5 -5
  14. package/dist/chunk-I5V446BX.js +27 -0
  15. package/dist/{chunk-WCTHZKU7.js → chunk-ICOU5QHT.js} +1 -1
  16. package/dist/{chunk-G7IFL4Z4.js → chunk-L3VEMP5A.js} +57 -9
  17. package/dist/chunk-LQWGZIYA.js +5 -0
  18. package/dist/{chunk-XNA2INVQ.js → chunk-MCOHLWRS.js} +2 -2
  19. package/dist/{chunk-3GWWZKX7.js → chunk-MMUBH76A.js} +21 -1
  20. package/dist/{chunk-ZDAHKI3A.js → chunk-MWB7S54O.js} +1 -1
  21. package/dist/{chunk-D5QHEHXJ.js → chunk-NMEVJT5Q.js} +1 -1
  22. package/dist/{chunk-3TNPSN4D.js → chunk-NSNEANOQ.js} +2 -2
  23. package/dist/{chunk-WSRWFA6K.js → chunk-NWZGSLPU.js} +1 -1
  24. package/dist/{chunk-LEAYBICI.js → chunk-OIV7B44M.js} +1 -1
  25. package/dist/{chunk-2CXU5WZO.js → chunk-P43B3ONY.js} +1 -1
  26. package/dist/{chunk-FRPPIXMY.js → chunk-PDTPO6NG.js} +1 -1
  27. package/dist/{chunk-6XMYEZVR.js → chunk-PX6RICS5.js} +1 -1
  28. package/dist/{chunk-6TEEY5YA.js → chunk-S33K4RC5.js} +1 -1
  29. package/dist/{chunk-3VMDCCCX.js → chunk-SC2VUTUI.js} +1 -1
  30. package/dist/{chunk-3NMG3KNA.js → chunk-SXQZ7DLH.js} +3 -3
  31. package/dist/{chunk-UDTG3RSE.js → chunk-T6S2ZBV6.js} +2 -2
  32. package/dist/{chunk-FKNBZF5T.js → chunk-WYTJR2YU.js} +1 -1
  33. package/dist/{chunk-QXHMPJ35.js → chunk-XBBEX4SF.js} +1 -1
  34. package/dist/chunk-XCB3F6SF.js +16 -0
  35. package/dist/{chunk-7BWTYWM3.js → chunk-ZLMRVBD7.js} +1 -1
  36. package/dist/{chunk-P7XTW2CG.js → chunk-ZLPWEHZO.js} +1 -1
  37. package/dist/components/accordion.js +1 -1
  38. package/dist/components/alert-dialog.js +2 -2
  39. package/dist/components/alert.js +2 -2
  40. package/dist/components/aspect-ratio.js +2 -2
  41. package/dist/components/avatar.js +1 -1
  42. package/dist/components/badge.js +2 -2
  43. package/dist/components/breadcrumb.js +1 -1
  44. package/dist/components/button-group.js +1 -1
  45. package/dist/components/button.js +2 -2
  46. package/dist/components/calendar.js +2 -2
  47. package/dist/components/card.js +3 -3
  48. package/dist/components/carousel.js +2 -2
  49. package/dist/components/category-deck.js +2 -2
  50. package/dist/components/checkbox.js +2 -2
  51. package/dist/components/circular-progress.js +1 -1
  52. package/dist/components/collapsible.js +1 -1
  53. package/dist/components/combobox.js +5 -5
  54. package/dist/components/command.js +3 -3
  55. package/dist/components/concept-card.js +2 -2
  56. package/dist/components/concept-compare-rail.js +3 -3
  57. package/dist/components/concept-compare.d.ts +2 -1
  58. package/dist/components/concept-compare.js +5 -5
  59. package/dist/components/concept-convergence.js +4 -4
  60. package/dist/components/convergence.js +3 -3
  61. package/dist/components/coverflow.js +1 -1
  62. package/dist/components/currency-input.js +2 -2
  63. package/dist/components/date-input.js +2 -2
  64. package/dist/components/day-rail-chip.js +2 -2
  65. package/dist/components/description-list.js +1 -1
  66. package/dist/components/dialog.js +2 -2
  67. package/dist/components/doorway-tiles.d.ts +57 -0
  68. package/dist/components/doorway-tiles.js +86 -0
  69. package/dist/components/drawer.js +1 -1
  70. package/dist/components/drift-rail.js +1 -1
  71. package/dist/components/dropdown-menu.js +1 -1
  72. package/dist/components/empty-state.js +3 -3
  73. package/dist/components/event-card.js +4 -4
  74. package/dist/components/event-compact-row.js +4 -4
  75. package/dist/components/event-day-header.js +2 -2
  76. package/dist/components/event-day-separator.js +3 -3
  77. package/dist/components/event-detail-panel.js +4 -4
  78. package/dist/components/event-hub-hero.js +2 -2
  79. package/dist/components/event-index-row.js +4 -4
  80. package/dist/components/event-list-row-wide.js +5 -5
  81. package/dist/components/event-list-row.js +5 -5
  82. package/dist/components/event-poster-fallback.js +1 -1
  83. package/dist/components/event-types.js +1 -1
  84. package/dist/components/events-empty-state.js +1 -1
  85. package/dist/components/explainer-steps.js +1 -1
  86. package/dist/components/facet-group.js +2 -2
  87. package/dist/components/featured-rail-item.js +2 -2
  88. package/dist/components/field.js +2 -2
  89. package/dist/components/filter-category-row.js +3 -3
  90. package/dist/components/form.js +2 -2
  91. package/dist/components/glass-ground.js +2 -2
  92. package/dist/components/grade-atom.d.ts +3 -0
  93. package/dist/components/grade-atom.js +4 -0
  94. package/dist/components/haptic-button.js +2 -2
  95. package/dist/components/hour-band.js +4 -4
  96. package/dist/components/hour-group.js +4 -4
  97. package/dist/components/hub-surface.js +1 -1
  98. package/dist/components/hue-chip.js +1 -1
  99. package/dist/components/icon.js +1 -1
  100. package/dist/components/input-group.js +1 -1
  101. package/dist/components/input-otp.js +1 -1
  102. package/dist/components/input.js +2 -2
  103. package/dist/components/item.js +1 -1
  104. package/dist/components/kbd.js +1 -1
  105. package/dist/components/kitchen-dossier.d.ts +2 -1
  106. package/dist/components/kitchen-dossier.js +3 -3
  107. package/dist/components/label.js +2 -2
  108. package/dist/components/lazy-markdown.js +1 -1
  109. package/dist/components/link-button.js +2 -2
  110. package/dist/components/markdown-renderer.js +1 -1
  111. package/dist/components/menu-dead-end.d.ts +2 -1
  112. package/dist/components/menu-dead-end.js +1 -1
  113. package/dist/components/menu-family-block.d.ts +2 -1
  114. package/dist/components/menu-family-block.js +14 -8
  115. package/dist/components/menu-family-card.d.ts +2 -1
  116. package/dist/components/menu-family-card.js +1 -1
  117. package/dist/components/menu-field.js +1 -1
  118. package/dist/components/menu-filter-chips.d.ts +2 -1
  119. package/dist/components/menu-filter-chips.js +3 -3
  120. package/dist/components/menu-group-rows.d.ts +2 -1
  121. package/dist/components/menu-group-rows.js +2 -2
  122. package/dist/components/menu-hub-hero.js +1 -1
  123. package/dist/components/menu-index-header.d.ts +2 -1
  124. package/dist/components/menu-index-header.js +2 -2
  125. package/dist/components/menu-index-list.d.ts +2 -1
  126. package/dist/components/menu-index-list.js +4 -4
  127. package/dist/components/menu-intent-hero.d.ts +3 -2
  128. package/dist/components/menu-intent-hero.js +1 -1
  129. package/dist/components/menu-item.d.ts +2 -1
  130. package/dist/components/menu-item.js +4 -4
  131. package/dist/components/menu-kitchen-list.d.ts +2 -1
  132. package/dist/components/menu-kitchen-list.js +4 -4
  133. package/dist/components/menu-mocks.d.ts +16 -5
  134. package/dist/components/menu-mocks.js +23 -2
  135. package/dist/components/menu-types.d.ts +21 -34
  136. package/dist/components/motion-icon-actions.d.ts +27 -0
  137. package/dist/components/motion-icon-actions.js +336 -0
  138. package/dist/components/motion-icon-controls.js +3 -3
  139. package/dist/components/motion-icon-set.d.ts +13 -1
  140. package/dist/components/motion-icon-set.js +197 -6
  141. package/dist/components/motion-icon.d.ts +15 -1
  142. package/dist/components/motion-icon.js +2 -2
  143. package/dist/components/multi-select-combobox.js +7 -7
  144. package/dist/components/multi-select-menu.js +2 -2
  145. package/dist/components/native-select.js +1 -1
  146. package/dist/components/open-now-list.d.ts +2 -1
  147. package/dist/components/open-now-list.js +2 -2
  148. package/dist/components/pagination.js +2 -2
  149. package/dist/components/phone-frame.js +1 -1
  150. package/dist/components/photo-ground.js +1 -1
  151. package/dist/components/place-about-tab.d.ts +2 -1
  152. package/dist/components/place-about-tab.js +6 -6
  153. package/dist/components/place-card.d.ts +94 -12
  154. package/dist/components/place-card.js +203 -128
  155. package/dist/components/place-details-mobile.d.ts +2 -1
  156. package/dist/components/place-details-mobile.js +7 -7
  157. package/dist/components/place-schedule-week.d.ts +2 -1
  158. package/dist/components/place-schedule-week.js +3 -3
  159. package/dist/components/place-types.d.ts +3 -265
  160. package/dist/components/popover.js +2 -2
  161. package/dist/components/poster-stack.js +1 -1
  162. package/dist/components/poster-tape.js +1 -1
  163. package/dist/components/poster-wall.d.ts +22 -6
  164. package/dist/components/poster-wall.js +197 -12
  165. package/dist/components/progress.js +1 -1
  166. package/dist/components/qa-block.d.ts +2 -1
  167. package/dist/components/qa-block.js +1 -1
  168. package/dist/components/quiet-stretch.js +1 -1
  169. package/dist/components/radio-group.js +1 -1
  170. package/dist/components/rating.js +1 -1
  171. package/dist/components/scroll-area.js +1 -1
  172. package/dist/components/scroll-fade.js +2 -2
  173. package/dist/components/search-bar.js +1 -1
  174. package/dist/components/searchable-select.js +5 -5
  175. package/dist/components/section-header.js +2 -2
  176. package/dist/components/segmented-nav.d.ts +24 -4
  177. package/dist/components/segmented-nav.js +11 -2
  178. package/dist/components/select.js +1 -1
  179. package/dist/components/selection-bar.js +1 -1
  180. package/dist/components/separator.js +2 -2
  181. package/dist/components/sheet.js +1 -1
  182. package/dist/components/signature-chip.js +3 -3
  183. package/dist/components/skeleton-card.js +3 -3
  184. package/dist/components/skeleton.js +2 -2
  185. package/dist/components/slider.js +1 -1
  186. package/dist/components/sliding-panels.js +2 -2
  187. package/dist/components/snap-rail.js +2 -2
  188. package/dist/components/sonner.js +1 -1
  189. package/dist/components/sort-option-list.js +1 -1
  190. package/dist/components/spinner.js +1 -1
  191. package/dist/components/stat-banner.js +1 -1
  192. package/dist/components/stepper.js +1 -1
  193. package/dist/components/sticky-container.js +1 -1
  194. package/dist/components/story.js +1 -1
  195. package/dist/components/switch.js +1 -1
  196. package/dist/components/table.js +1 -1
  197. package/dist/components/tabs.js +2 -2
  198. package/dist/components/tag-group.js +2 -2
  199. package/dist/components/tag.js +2 -2
  200. package/dist/components/text.js +2 -2
  201. package/dist/components/textarea.js +1 -1
  202. package/dist/components/timeline-band.js +4 -4
  203. package/dist/components/timeline-playhead.js +4 -4
  204. package/dist/components/toggle-group.js +2 -2
  205. package/dist/components/toggle.js +2 -2
  206. package/dist/components/tooltip.js +1 -1
  207. package/dist/components/top-comparison.d.ts +2 -1
  208. package/dist/components/top-comparison.js +1 -1
  209. package/dist/components/top-cross-link.d.ts +2 -1
  210. package/dist/components/top-cross-link.js +1 -1
  211. package/dist/components/top-ranking-entry.d.ts +2 -1
  212. package/dist/components/top-ranking-entry.js +6 -6
  213. package/dist/components/top-ranking-hero.d.ts +2 -1
  214. package/dist/components/top-ranking-hero.js +1 -1
  215. package/dist/components/top-ranking-legend.js +1 -1
  216. package/dist/components/top-types.d.ts +3 -1
  217. package/dist/components/use-mobile.js +1 -1
  218. package/dist/components/venue-events-block.js +5 -5
  219. package/dist/components/venue-grade-dimensions.d.ts +2 -1
  220. package/dist/components/venue-grade-dimensions.js +1 -1
  221. package/dist/components/venue-quotes.d.ts +2 -1
  222. package/dist/components/venue-quotes.js +1 -1
  223. package/dist/components/venue-wall.d.ts +2 -1
  224. package/dist/components/venue-wall.js +1 -1
  225. package/dist/components/weekday-theme-banner.js +1 -1
  226. package/dist/components/weekend-panel.js +4 -4
  227. package/dist/hooks/use-drag-swipe.js +1 -1
  228. package/dist/hooks/use-haptic.js +1 -1
  229. package/dist/hooks/use-media-query.js +1 -1
  230. package/dist/hooks/use-mobile.js +1 -1
  231. package/dist/hooks/use-mounted-theme.js +1 -1
  232. package/dist/hooks/use-sticky.js +1 -1
  233. package/dist/lib/card-hover.js +2 -18
  234. package/dist/lib/grade.d.ts +54 -0
  235. package/dist/lib/grade.js +2 -0
  236. package/dist/lib/icon-depth.d.ts +35 -0
  237. package/dist/lib/icon-depth.js +2 -0
  238. package/dist/lib/icon-registry.js +1 -1
  239. package/dist/lib/motion.js +2 -2
  240. package/dist/lib/overlay-pill.d.ts +30 -0
  241. package/dist/lib/overlay-pill.js +2 -0
  242. package/dist/lib/utils.js +1 -1
  243. package/dist/place-types-BHdeMMiG.d.ts +317 -0
  244. package/package.json +1 -1
  245. package/src/components/colour-dictionary.stories.tsx +268 -82
  246. package/src/components/doorway-tiles.stories.tsx +83 -0
  247. package/src/components/doorway-tiles.test.tsx +51 -0
  248. package/src/components/doorway-tiles.tsx +225 -0
  249. package/src/components/grade-atom.stories.tsx +81 -0
  250. package/src/components/grade-atom.tsx +256 -0
  251. package/src/components/hue-chip.stories.tsx +2 -2
  252. package/src/components/menu-family-block.stories.tsx +29 -0
  253. package/src/components/menu-family-block.test.tsx +51 -0
  254. package/src/components/menu-family-block.tsx +20 -4
  255. package/src/components/menu-intent-hero.tsx +1 -1
  256. package/src/components/menu-mocks.ts +39 -3
  257. package/src/components/menu-types.ts +17 -33
  258. package/src/components/motion-icon-actions.test.tsx +115 -0
  259. package/src/components/motion-icon-actions.tsx +580 -0
  260. package/src/components/motion-icon-controls.test.tsx +1 -0
  261. package/src/components/motion-icon-controls.tsx +18 -13
  262. package/src/components/motion-icon-set.stories.tsx +62 -3
  263. package/src/components/motion-icon-set.tsx +349 -2
  264. package/src/components/motion-icon.test.tsx +180 -1
  265. package/src/components/motion-icon.tsx +97 -8
  266. package/src/components/place-card.stories.tsx +66 -76
  267. package/src/components/place-card.test.tsx +188 -100
  268. package/src/components/place-card.tsx +356 -148
  269. package/src/components/place-types.ts +17 -4
  270. package/src/components/poster-wall.stories.tsx +84 -0
  271. package/src/components/poster-wall.test.tsx +94 -3
  272. package/src/components/poster-wall.tsx +257 -9
  273. package/src/components/segmented-nav.stories.tsx +72 -1
  274. package/src/components/segmented-nav.test.tsx +96 -0
  275. package/src/components/segmented-nav.tsx +36 -4
  276. package/src/globals.css +26 -0
  277. package/src/lib/grade.test.ts +82 -0
  278. package/src/lib/grade.ts +76 -0
  279. package/src/lib/icon-depth.ts +35 -0
  280. package/src/lib/overlay-pill.ts +29 -0
  281. package/dist/components/menu-family-tiles.d.ts +0 -19
  282. package/dist/components/menu-family-tiles.js +0 -57
  283. package/src/components/menu-family-tiles.stories.tsx +0 -54
  284. package/src/components/menu-family-tiles.test.tsx +0 -30
  285. package/src/components/menu-family-tiles.tsx +0 -132
@@ -1,265 +1,3 @@
1
- /**
2
- * Structural mirror of the openapi `Place` schema, decoupled from any one app's
3
- * generated types. Both FE and admin map their domain shapes into this type
4
- * before passing data to `<PlaceCard>` / `<PlaceAboutTab>`.
5
- */
6
- type PlacePreview = {
7
- id: string;
8
- slug: string;
9
- name: string;
10
- description?: string;
11
- generative_summary?: string;
12
- review_summary?: string;
13
- open_now?: boolean;
14
- rating?: {
15
- rating: number;
16
- count: number;
17
- };
18
- review_count?: number;
19
- hours?: PlaceHourBlock[];
20
- today_schedule?: PlaceHourBlock[];
21
- schedule?: PlaceSchedule;
22
- location?: {
23
- address?: string;
24
- geolocation?: {
25
- lat: number | string;
26
- lon: number | string;
27
- };
28
- distance?: {
29
- meters?: number;
30
- };
31
- };
32
- price_dishes?: PlacePriceRange;
33
- price_drinks?: PlacePriceRange;
34
- cuisines?: string[];
35
- amenities?: string[];
36
- payment_options?: string[];
37
- accessibility_features?: string[];
38
- parking_options?: string[];
39
- landmarks?: Array<{
40
- name: string;
41
- distance_meters?: number;
42
- }>;
43
- areas?: Array<{
44
- name: string;
45
- }>;
46
- busyness?: Array<{
47
- hour: number;
48
- score: number;
49
- }>;
50
- /**
51
- * The review distillation. Drives every menu-hub surface that speaks for a
52
- * venue — `<KitchenDossier>`, `<MenuKitchenList>`, `<OpenNowList>` —
53
- * because the signature dish, the pull-quotes and the graded dimensions all
54
- * live here rather than on the menu itself.
55
- */
56
- reviews_digest?: PlaceReviewsDigest;
57
- type?: string;
58
- business_status?: 'operational' | 'closed_temporarily' | 'closed_permanently';
59
- phone?: string;
60
- website_url?: string;
61
- socials?: {
62
- instagram_url?: string;
63
- facebook_url?: string;
64
- };
65
- /**
66
- * Pre-resolved thumbnail URL. Callers compute this themselves
67
- * (FE: `https://cdn.bear.menu/places/${slug}-thumbnail.jpg`,
68
- * admin: form's primary photo URL or blob URL).
69
- */
70
- thumbnail_url?: string;
71
- };
72
- type PlaceHourBlock = {
73
- opens_at: number;
74
- closes_at: number;
75
- };
76
- type WeekdayKey = 'monday' | 'tuesday' | 'wednesday' | 'thursday' | 'friday' | 'saturday' | 'sunday';
77
- type PlaceSchedule = Record<WeekdayKey, PlaceHourBlock[]>;
78
- type PlacePriceRange = {
79
- price_range_low?: number;
80
- price_range_high?: number;
81
- /** Median price across the place's menu, in the local currency. */
82
- median_price?: number;
83
- };
84
- /**
85
- * Menu items are priced per size; a single-size item still ships one entry.
86
- *
87
- * Defined here rather than in `menu-types.ts` because `PlaceMustTry` carries
88
- * it, and `menu-types.ts` already imports from this file — keeping the
89
- * definition here leaves that import one-directional. `menu-types.ts`
90
- * re-exports it, so menu code never reaches into place types for it.
91
- */
92
- type MenuSizeAndPrice = {
93
- /** Grams or millilitres. */
94
- size: number;
95
- price: number;
96
- discounted_price?: number;
97
- discount_percentage?: number;
98
- };
99
- /** A digest string with the reviews that back it. `review_ids.length` is the mention count. */
100
- type CitedText = {
101
- text: string;
102
- review_ids?: string[];
103
- };
104
- /** A digest enum value with the reviews that back it. The value is a language-neutral code. */
105
- type CitedValue = {
106
- value: string;
107
- review_ids?: string[];
108
- };
109
- /**
110
- * One dish or drink the reviews single out.
111
- *
112
- * `mentions` and `score` come from the item census and are only served by the
113
- * place DETAIL endpoint — a listing carries the item without them. Surfaces
114
- * that need a count from a list fall back to `review_ids.length`, which is
115
- * present in both. `strength`, `why` and `review_ids` are absent for census
116
- * items the prose digest never mentioned, so none of them can be assumed.
117
- */
118
- type PlaceMustTry = {
119
- name: string;
120
- why?: string;
121
- strength?: 'signature' | 'frequent' | 'occasional';
122
- /** Deep-link slug. Present only when the item matched a live menu row. */
123
- slug?: string;
124
- sizes_and_prices?: MenuSizeAndPrice[];
125
- review_ids?: string[];
126
- /** 0–100 census sentiment. Detail endpoint only; 3+ mentions required. */
127
- score?: number;
128
- /** Censused review mentions of this item. Detail endpoint only. */
129
- mentions?: number;
130
- };
131
- /**
132
- * Per-dimension 0–100 review scores.
133
- *
134
- * A dimension the reviews never mention is ABSENT, not zero — so a surface
135
- * drawing these must render what it is given rather than a fixed row of seven.
136
- * Exactly one of `food` / `coffee_quality` / `drinks_quality` is scored per
137
- * place: a coffee bar has no food score and a restaurant has no coffee one.
138
- */
139
- type PlaceGradeDimensions = {
140
- food?: number;
141
- coffee_quality?: number;
142
- drinks_quality?: number;
143
- value?: number;
144
- service?: number;
145
- cleanliness?: number;
146
- ambiance?: number;
147
- portion?: number;
148
- consistency?: number;
149
- };
150
- type PlaceGrade = {
151
- /** 1.0–10.0. */
152
- overall?: number;
153
- /**
154
- * Backend recommendation to show the number at all. When false the caller
155
- * should draw a "building confidence" state instead — the DS renders what it
156
- * is told, it does not apply the rule.
157
- */
158
- show_number?: boolean;
159
- /** Locale-resolved band label, e.g. "Foarte bun". Already translated upstream. */
160
- descriptor?: string;
161
- confidence?: 'low' | 'medium' | 'high';
162
- capped?: boolean;
163
- dimensions?: PlaceGradeDimensions;
164
- };
165
- type PlaceReviewsDigest = {
166
- grade?: PlaceGrade;
167
- grade_note?: string;
168
- tagline?: CitedText;
169
- verdict?: CitedText;
170
- caveat?: CitedText;
171
- /** 1–3 standout positives. The pull-quotes on `<KitchenDossier>`. */
172
- highlights?: CitedText[];
173
- must_try?: PlaceMustTry[];
174
- /** Vibe codes (cozy, quiet, family_friendly, …). Localised by the caller. */
175
- tags?: CitedValue[];
176
- dietary?: CitedValue[];
177
- /** small | moderate | generous */
178
- portion_size?: CitedValue;
179
- /** cheap | fair | mid_range | splurge */
180
- value?: CitedValue;
181
- /** quiet | moderate | loud */
182
- noise_level?: CitedValue;
183
- /** Text reviews behind the digest. */
184
- review_count?: number;
185
- };
186
- /** Labels passed to `<PlaceCard>`. All translation happens in the caller. */
187
- type PlaceCardLabels = {
188
- open: string;
189
- closed: string;
190
- until: string;
191
- opensAt: string;
192
- currency: string;
193
- };
194
- /**
195
- * Labels passed to `<PlaceAboutTab>`. All translation happens in the caller.
196
- *
197
- * Dynamic suffix maps (`amenityLabels`, etc.) translate API enum values like
198
- * `wheelchair_accessible_entrance` into human strings. Missing keys fall back
199
- * to the raw enum value (with underscores → spaces).
200
- */
201
- type PlaceAboutTabLabels = {
202
- /** Section heading for the editorial prose block (generative summary + review summary + description). */
203
- description: string;
204
- contactAndLocation: string;
205
- instagram: string;
206
- facebook: string;
207
- /** Section heading for the inline weekly schedule. */
208
- todayHours: string;
209
- /** Used by the inline schedule's closed-day line-through. */
210
- statusClosed: string;
211
- typicalBusyness: string;
212
- cuisines: string;
213
- amenities: string;
214
- paymentOptions: string;
215
- accessibility: string;
216
- parking: string;
217
- nearbyLandmarks: string;
218
- /** Weekday key → translated day name, consumed by the inline `<PlaceScheduleWeek>`. */
219
- scheduleDays: Record<WeekdayKey, string>;
220
- /** Fallback shown by `<PlaceScheduleWeek>` when no schedule data is present. */
221
- noHoursAvailable: string;
222
- amenityLabels: Record<string, string>;
223
- paymentLabels: Record<string, string>;
224
- accessibilityLabels: Record<string, string>;
225
- parkingLabels: Record<string, string>;
226
- typeLabels: Record<string, string>;
227
- statusLabels: Record<string, string>;
228
- };
229
- /** Discriminator for `<PlaceAboutTab>`'s `onLinkClick` callback. */
230
- type PlaceLinkKind = 'phone' | 'website' | 'instagram' | 'facebook' | 'address';
231
- /** Labels passed to `<PlaceScheduleWeek>`. */
232
- type PlaceScheduleWeekLabels = {
233
- days: Record<WeekdayKey, string>;
234
- closed: string;
235
- /** Shown when the `schedule` prop is missing or empty. */
236
- noHoursAvailable: string;
237
- };
238
- /**
239
- * Labels passed to `<PlaceDetailsMobile>`. Extends `PlaceAboutTabLabels` with
240
- * the header/tab-strip strings unique to the mobile place-details surface.
241
- */
242
- type PlaceDetailsMobileLabels = PlaceAboutTabLabels & {
243
- /** Tab strip — "About" is highlighted, the rest are visual placeholders. */
244
- tabs: {
245
- menu: string;
246
- about: string;
247
- reviews: string;
248
- events: string;
249
- };
250
- /** Status badge over the hero image and the inline open/closed pill in the info row. */
251
- statusBadge: {
252
- openNow: string;
253
- closed: string;
254
- /** Inserted with surrounding " · " separators. e.g. "Closes at" */
255
- closesAt: string;
256
- opensAt: string;
257
- opensTomorrow: string;
258
- };
259
- /** Review count formatter. Pass a translator that handles ICU pluralization. */
260
- reviewCount: (count: number) => string;
261
- /** "No image available" fallback shown when no thumbnail is set. */
262
- noImageAvailable: string;
263
- };
264
-
265
- export type { CitedText, CitedValue, MenuSizeAndPrice, PlaceAboutTabLabels, PlaceCardLabels, PlaceDetailsMobileLabels, PlaceGrade, PlaceGradeDimensions, PlaceHourBlock, PlaceLinkKind, PlaceMustTry, PlacePreview, PlacePriceRange, PlaceReviewsDigest, PlaceSchedule, PlaceScheduleWeekLabels, WeekdayKey };
1
+ export { C as CitedText, k as CitedValue, M as MenuSizeAndPrice, b as PlaceAboutTabLabels, d as PlaceCardLabels, e as PlaceDetailsMobileLabels, l as PlaceGrade, a as PlaceGradeDimensions, m as PlaceHourBlock, c as PlaceLinkKind, n as PlaceMustTry, P as PlacePreview, o as PlacePriceRange, p as PlaceReviewsDigest, f as PlaceSchedule, g as PlaceScheduleWeekLabels, W as WeekdayKey } from '../place-types-BHdeMMiG.js';
2
+ import 'react/jsx-runtime';
3
+ import '../lib/grade.js';
@@ -1,3 +1,3 @@
1
- export { Popover, PopoverAnchor, PopoverArrow, PopoverClose, PopoverContent, PopoverDescription, PopoverHeader, PopoverTitle, PopoverTrigger } from '../chunk-66MAPLXS.js';
1
+ export { Popover, PopoverAnchor, PopoverArrow, PopoverClose, PopoverContent, PopoverDescription, PopoverHeader, PopoverTitle, PopoverTrigger } from '../chunk-2ADCZXZJ.js';
2
2
  import '../chunk-FEK5XGZW.js';
3
- import '../chunk-3GWWZKX7.js';
3
+ import '../chunk-MMUBH76A.js';
@@ -1,5 +1,5 @@
1
1
  import { cn } from '../chunk-FEK5XGZW.js';
2
- import '../chunk-3GWWZKX7.js';
2
+ import '../chunk-MMUBH76A.js';
3
3
  import { useRef, useState, useEffect } from 'react';
4
4
  import { jsxs, jsx } from 'react/jsx-runtime';
5
5
 
@@ -1,7 +1,7 @@
1
1
  import { DriftRail } from '../chunk-AHL45DD2.js';
2
2
  import { EventPosterFallback } from '../chunk-FDITZ2VM.js';
3
3
  import { cn } from '../chunk-FEK5XGZW.js';
4
- import { __objRest, __spreadValues } from '../chunk-3GWWZKX7.js';
4
+ import { __objRest, __spreadValues } from '../chunk-MMUBH76A.js';
5
5
  import { useState } from 'react';
6
6
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
7
7
 
@@ -1,25 +1,41 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import * as React from 'react';
3
3
 
4
+ /** Tile geometry for a measured strip box: 4:5 tiles in two rows, as many columns as cover the width. */
5
+ declare function stripGeometry(width: number, height: number): {
6
+ tileWidth: number;
7
+ cols: number;
8
+ slotCount: number;
9
+ };
4
10
  type PosterWallTile = {
5
11
  src?: string;
6
12
  };
13
+ type PosterWallVariant = "default" | "compact" | "strip";
7
14
  type PosterWallProps = {
8
15
  /** Background tiles. The grid sizes itself to how many there are (see above); with none, a placeholder grid renders. */
9
16
  tiles?: PosterWallTile[];
10
- /** `compact` is the bottom-aligned mobile treatment (fewer, larger tiles, a top-fading veil). */
11
- variant?: "default" | "compact";
17
+ /**
18
+ * `compact` is the bottom-aligned mobile treatment (fewer, larger tiles, a
19
+ * top-fading veil). `strip` fills the caller's box as a banner backdrop —
20
+ * two rows of portrait tiles filled from the right edge, soonest-first.
21
+ */
22
+ variant?: PosterWallVariant;
23
+ /**
24
+ * Whether the light sweeps. Defaults on; a caller turns it off for a wall
25
+ * with nothing on it (an empty strip is a status surface, not a stage).
26
+ */
27
+ sweep?: boolean;
12
28
  /** The overlay content — fully caller-composed. */
13
29
  children?: React.ReactNode;
14
30
  className?: string;
15
31
  };
16
- declare function PosterWall({ tiles, variant, children, className, }: PosterWallProps): react_jsx_runtime.JSX.Element;
32
+ declare function PosterWall({ tiles, variant, sweep, children, className, }: PosterWallProps): react_jsx_runtime.JSX.Element;
17
33
  type PosterWallSkeletonProps = {
18
- /** `compact` is the bottom-aligned mobile treatment (fewer, larger tiles, a top-fading veil). */
19
- variant?: "default" | "compact";
34
+ /** Same meaning as on `PosterWall`; `strip` fills the caller's box. */
35
+ variant?: PosterWallVariant;
20
36
  className?: string;
21
37
  };
22
38
  /** The tile grid at rest — the sweeping light never renders in the loading state. */
23
39
  declare function PosterWallSkeleton({ variant, className, }: PosterWallSkeletonProps): react_jsx_runtime.JSX.Element;
24
40
 
25
- export { PosterWall, type PosterWallProps, PosterWallSkeleton, type PosterWallSkeletonProps, type PosterWallTile };
41
+ export { PosterWall, type PosterWallProps, PosterWallSkeleton, type PosterWallSkeletonProps, type PosterWallTile, type PosterWallVariant, stripGeometry };
@@ -1,12 +1,37 @@
1
- import { Skeleton } from '../chunk-QXHMPJ35.js';
1
+ import { Skeleton } from '../chunk-XBBEX4SF.js';
2
2
  import { cn } from '../chunk-FEK5XGZW.js';
3
- import '../chunk-3GWWZKX7.js';
3
+ import '../chunk-MMUBH76A.js';
4
4
  import * as React from 'react';
5
- import { jsxs, jsx } from 'react/jsx-runtime';
5
+ import { jsx, jsxs } from 'react/jsx-runtime';
6
6
 
7
7
  var POSTER_WALL_DESKTOP_TILE_COUNT = 48;
8
8
  var POSTER_WALL_COMPACT_TILE_COUNT = 36;
9
+ var POSTER_WALL_STRIP_TILE_COUNT = 32;
10
+ var POSTER_WALL_STRIP_ROWS = 2;
11
+ var POSTER_WALL_STRIP_TILE_ASPECT = 4 / 5;
12
+ var POSTER_WALL_STRIP_GAP_PX = 2;
9
13
  var POSTER_WALL_SWEEP_KEYFRAMES_ID = "ds-poster-wall-sweep-keyframes";
14
+ var POSTER_WALL_BASE = "oklch(0.22 0.012 60)";
15
+ var POSTER_WALL_VEIL = "oklch(0.2 0.01 60";
16
+ var STRIP_VEIL = `linear-gradient(90deg, ${POSTER_WALL_VEIL} / 0.92) 0%, ${POSTER_WALL_VEIL} / 0.78) 50%, ${POSTER_WALL_VEIL} / 0.4) 75%, ${POSTER_WALL_VEIL} / 0.25) 100%)`;
17
+ var STRIP_VEIL_WIDE = `linear-gradient(90deg, ${POSTER_WALL_VEIL} / 0.92) 0%, ${POSTER_WALL_VEIL} / 0.8) 40%, ${POSTER_WALL_VEIL} / 0.45) 62%, ${POSTER_WALL_VEIL} / 0.22) 100%)`;
18
+ var PLACEHOLDER_TILE_CLASS = "h-full w-full bg-[repeating-linear-gradient(45deg,color-mix(in_oklch,white_10%,transparent)_0px,color-mix(in_oklch,white_10%,transparent)_2px,transparent_2px,transparent_8px)]";
19
+ function stripGeometry(width, height) {
20
+ const rowHeight = (height - POSTER_WALL_STRIP_GAP_PX * (POSTER_WALL_STRIP_ROWS - 1)) / POSTER_WALL_STRIP_ROWS;
21
+ const tileWidth = Math.max(
22
+ 1,
23
+ Math.round(rowHeight * POSTER_WALL_STRIP_TILE_ASPECT)
24
+ );
25
+ const cols = Math.max(
26
+ 1,
27
+ Math.ceil(width / (tileWidth + POSTER_WALL_STRIP_GAP_PX))
28
+ );
29
+ const slotCount = Math.min(
30
+ cols * POSTER_WALL_STRIP_ROWS,
31
+ POSTER_WALL_STRIP_TILE_COUNT
32
+ );
33
+ return { tileWidth, cols, slotCount };
34
+ }
10
35
  function usePrefersReducedMotion() {
11
36
  const [reduced, setReduced] = React.useState(false);
12
37
  React.useEffect(() => {
@@ -39,6 +64,135 @@ function useInViewport() {
39
64
  function PosterWall({
40
65
  tiles,
41
66
  variant = "default",
67
+ sweep = true,
68
+ children,
69
+ className
70
+ }) {
71
+ if (variant === "strip") {
72
+ return /* @__PURE__ */ jsx(PosterStrip, { tiles, sweep, className, children });
73
+ }
74
+ return /* @__PURE__ */ jsx(
75
+ PosterWallMasthead,
76
+ {
77
+ tiles,
78
+ variant,
79
+ sweep,
80
+ className,
81
+ children
82
+ }
83
+ );
84
+ }
85
+ function PosterStrip({
86
+ tiles,
87
+ sweep,
88
+ children,
89
+ className
90
+ }) {
91
+ const pictures = React.useMemo(
92
+ () => (tiles != null ? tiles : []).filter((tile) => !!tile.src),
93
+ [tiles]
94
+ );
95
+ const prefersReducedMotion = usePrefersReducedMotion();
96
+ const [wallRef, inView] = useInViewport();
97
+ const isSweepPaused = !sweep || prefersReducedMotion || !inView;
98
+ const [box, setBox] = React.useState(null);
99
+ React.useEffect(() => {
100
+ const node = wallRef.current;
101
+ if (!node || typeof ResizeObserver === "undefined") return;
102
+ const observer = new ResizeObserver(
103
+ ([entry]) => setBox({
104
+ width: entry.contentRect.width,
105
+ height: entry.contentRect.height
106
+ })
107
+ );
108
+ observer.observe(node);
109
+ return () => observer.disconnect();
110
+ }, [wallRef]);
111
+ const geometry = box ? stripGeometry(box.width, box.height) : null;
112
+ const slots = geometry ? Array.from(
113
+ { length: geometry.slotCount },
114
+ (_, index) => {
115
+ var _a;
116
+ return (_a = pictures[index]) != null ? _a : {};
117
+ }
118
+ ) : [];
119
+ return /* @__PURE__ */ jsxs(
120
+ "div",
121
+ {
122
+ ref: wallRef,
123
+ className: cn("absolute inset-0 overflow-hidden", className),
124
+ style: { backgroundColor: POSTER_WALL_BASE },
125
+ children: [
126
+ /* @__PURE__ */ jsx("style", { children: `
127
+ @keyframes ${POSTER_WALL_SWEEP_KEYFRAMES_ID} {
128
+ 0% { transform: translateX(-30%); }
129
+ 100% { transform: translateX(30%); }
130
+ }
131
+ ` }),
132
+ geometry ? /* @__PURE__ */ jsx(
133
+ "div",
134
+ {
135
+ className: "motion-image-enter absolute inset-0 grid grid-flow-col grid-rows-2 gap-[2px] [direction:rtl]",
136
+ style: { gridAutoColumns: `${geometry.tileWidth}px` },
137
+ "aria-hidden": true,
138
+ children: slots.map(
139
+ (tile, index) => tile.src ? (
140
+ // eslint-disable-next-line @next/next/no-img-element
141
+ /* @__PURE__ */ jsx(
142
+ "img",
143
+ {
144
+ src: tile.src,
145
+ alt: "",
146
+ loading: "lazy",
147
+ decoding: "async",
148
+ className: "h-full w-full object-cover"
149
+ },
150
+ index
151
+ )
152
+ ) : /* @__PURE__ */ jsx("div", { className: PLACEHOLDER_TILE_CLASS }, index)
153
+ )
154
+ }
155
+ ) : null,
156
+ /* @__PURE__ */ jsx(
157
+ "div",
158
+ {
159
+ className: "absolute -inset-[20%]",
160
+ style: {
161
+ background: "radial-gradient(circle at 30% 50%, oklch(1 0 0 / 0.22) 0%, transparent 42%)",
162
+ animationName: POSTER_WALL_SWEEP_KEYFRAMES_ID,
163
+ animationDuration: "18s",
164
+ animationTimingFunction: "linear",
165
+ animationIterationCount: "infinite",
166
+ animationPlayState: isSweepPaused ? "paused" : "running"
167
+ },
168
+ "aria-hidden": true
169
+ }
170
+ ),
171
+ /* @__PURE__ */ jsx(
172
+ "div",
173
+ {
174
+ className: "absolute inset-0 lg:hidden",
175
+ style: { background: STRIP_VEIL },
176
+ "aria-hidden": true
177
+ }
178
+ ),
179
+ /* @__PURE__ */ jsx(
180
+ "div",
181
+ {
182
+ className: "absolute inset-0 hidden lg:block",
183
+ style: { background: STRIP_VEIL_WIDE },
184
+ "aria-hidden": true
185
+ }
186
+ ),
187
+ children ? /* @__PURE__ */ jsx("div", { className: "relative h-full min-w-0 text-white", children }) : null
188
+ ]
189
+ }
190
+ );
191
+ }
192
+ function PosterWallMasthead({
193
+ tiles,
194
+ variant,
195
+ sweep,
42
196
  children,
43
197
  className
44
198
  }) {
@@ -52,7 +206,7 @@ function PosterWall({
52
206
  );
53
207
  const prefersReducedMotion = usePrefersReducedMotion();
54
208
  const [wallRef, inView] = useInViewport();
55
- const isSweepPaused = prefersReducedMotion || !inView;
209
+ const isSweepPaused = !sweep || prefersReducedMotion || !inView;
56
210
  const [width, setWidth] = React.useState(isCompact ? 390 : 1200);
57
211
  React.useEffect(() => {
58
212
  const node = wallRef.current;
@@ -115,13 +269,7 @@ function PosterWall({
115
269
  },
116
270
  index
117
271
  )
118
- ) : /* @__PURE__ */ jsx(
119
- "div",
120
- {
121
- className: "h-full w-full bg-[repeating-linear-gradient(45deg,color-mix(in_oklch,white_10%,transparent)_0px,color-mix(in_oklch,white_10%,transparent)_2px,transparent_2px,transparent_8px)]"
122
- },
123
- index
124
- )
272
+ ) : /* @__PURE__ */ jsx("div", { className: PLACEHOLDER_TILE_CLASS }, index)
125
273
  )
126
274
  }
127
275
  ),
@@ -164,10 +312,47 @@ function PosterWall({
164
312
  }
165
313
  );
166
314
  }
315
+ var POSTER_WALL_STRIP_SKELETON_TILE_COUNT = 12;
167
316
  function PosterWallSkeleton({
168
317
  variant = "default",
169
318
  className
170
319
  }) {
320
+ if (variant === "strip") {
321
+ return /* @__PURE__ */ jsxs(
322
+ "div",
323
+ {
324
+ "aria-hidden": "true",
325
+ className: cn("absolute inset-0 overflow-hidden", className),
326
+ style: { backgroundColor: POSTER_WALL_BASE },
327
+ children: [
328
+ /* @__PURE__ */ jsx("div", { className: "absolute inset-0 grid auto-cols-fr grid-flow-col grid-rows-2 gap-[2px] [direction:rtl]", children: Array.from(
329
+ { length: POSTER_WALL_STRIP_SKELETON_TILE_COUNT },
330
+ (_, index) => /* @__PURE__ */ jsx(
331
+ Skeleton,
332
+ {
333
+ className: "h-full w-full rounded-none bg-white/10"
334
+ },
335
+ index
336
+ )
337
+ ) }),
338
+ /* @__PURE__ */ jsx(
339
+ "div",
340
+ {
341
+ className: "absolute inset-0 lg:hidden",
342
+ style: { background: STRIP_VEIL }
343
+ }
344
+ ),
345
+ /* @__PURE__ */ jsx(
346
+ "div",
347
+ {
348
+ className: "absolute inset-0 hidden lg:block",
349
+ style: { background: STRIP_VEIL_WIDE }
350
+ }
351
+ )
352
+ ]
353
+ }
354
+ );
355
+ }
171
356
  const isCompact = variant === "compact";
172
357
  const tileCount = isCompact ? POSTER_WALL_COMPACT_TILE_COUNT : POSTER_WALL_DESKTOP_TILE_COUNT;
173
358
  return /* @__PURE__ */ jsxs(
@@ -236,4 +421,4 @@ function PosterWallSkeleton({
236
421
  );
237
422
  }
238
423
 
239
- export { PosterWall, PosterWallSkeleton };
424
+ export { PosterWall, PosterWallSkeleton, stripGeometry };
@@ -1,5 +1,5 @@
1
1
  import { cn } from '../chunk-FEK5XGZW.js';
2
- import { __objRest, __spreadProps, __spreadValues } from '../chunk-3GWWZKX7.js';
2
+ import { __objRest, __spreadProps, __spreadValues } from '../chunk-MMUBH76A.js';
3
3
  import * as React from 'react';
4
4
  import * as ProgressPrimitive from '@radix-ui/react-progress';
5
5
  import { jsx } from 'react/jsx-runtime';
@@ -1,7 +1,8 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { QaItem } from './menu-types.js';
3
3
  import 'react';
4
- import './place-types.js';
4
+ import '../place-types-BHdeMMiG.js';
5
+ import '../lib/grade.js';
5
6
 
6
7
  /**
7
8
  * QaBlock — questions answered from the numbers, as static prose.
@@ -1,5 +1,5 @@
1
1
  import { cn } from '../chunk-FEK5XGZW.js';
2
- import '../chunk-3GWWZKX7.js';
2
+ import '../chunk-MMUBH76A.js';
3
3
  import { jsxs, jsx } from 'react/jsx-runtime';
4
4
 
5
5
  function QaBlock({ items, heading, questionAs: Question = "h3", className }) {
@@ -1,5 +1,5 @@
1
1
  import { cn } from '../chunk-FEK5XGZW.js';
2
- import { __objRest, __spreadValues } from '../chunk-3GWWZKX7.js';
2
+ import { __objRest, __spreadValues } from '../chunk-MMUBH76A.js';
3
3
  import { ChevronDown } from 'lucide-react';
4
4
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
5
5
 
@@ -1,5 +1,5 @@
1
1
  import { cn } from '../chunk-FEK5XGZW.js';
2
- import { __objRest, __spreadProps, __spreadValues } from '../chunk-3GWWZKX7.js';
2
+ import { __objRest, __spreadProps, __spreadValues } from '../chunk-MMUBH76A.js';
3
3
  import * as React from 'react';
4
4
  import * as RadioGroupPrimitive from '@radix-ui/react-radio-group';
5
5
  import { Circle } from 'lucide-react';
@@ -1,5 +1,5 @@
1
1
  import { cn } from '../chunk-FEK5XGZW.js';
2
- import { __objRest, __spreadProps, __spreadValues } from '../chunk-3GWWZKX7.js';
2
+ import { __objRest, __spreadProps, __spreadValues } from '../chunk-MMUBH76A.js';
3
3
  import * as React from 'react';
4
4
  import { Star, StarHalf } from 'lucide-react';
5
5
  import { jsxs, jsx } from 'react/jsx-runtime';
@@ -1,5 +1,5 @@
1
1
  import { cn } from '../chunk-FEK5XGZW.js';
2
- import { __objRest, __spreadProps, __spreadValues } from '../chunk-3GWWZKX7.js';
2
+ import { __objRest, __spreadProps, __spreadValues } from '../chunk-MMUBH76A.js';
3
3
  import * as React from 'react';
4
4
  import * as ScrollAreaPrimitive from '@radix-ui/react-scroll-area';
5
5
  import { jsxs, jsx } from 'react/jsx-runtime';
@@ -1,3 +1,3 @@
1
- export { ScrollFade } from '../chunk-2XCZJWTV.js';
1
+ export { ScrollFade } from '../chunk-GNCWJWFE.js';
2
2
  import '../chunk-FEK5XGZW.js';
3
- import '../chunk-3GWWZKX7.js';
3
+ import '../chunk-MMUBH76A.js';
@@ -1,5 +1,5 @@
1
1
  import { cn } from '../chunk-FEK5XGZW.js';
2
- import '../chunk-3GWWZKX7.js';
2
+ import '../chunk-MMUBH76A.js';
3
3
  import { Search, Loader2 } from 'lucide-react';
4
4
  import { jsx, jsxs } from 'react/jsx-runtime';
5
5