@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.
Files changed (225) hide show
  1. package/dist/chunk-24RFI4RQ.js +167 -0
  2. package/dist/{chunk-UKBNQO6Q.js → chunk-2CXU5WZO.js} +1 -1
  3. package/dist/chunk-2XCZJWTV.js +121 -0
  4. package/dist/{chunk-62ES6N32.js → chunk-43RC7MHY.js} +1 -1
  5. package/dist/chunk-5RWTOT5Y.js +72 -0
  6. package/dist/{chunk-Q6QH52LJ.js → chunk-6TEEY5YA.js} +2 -2
  7. package/dist/{chunk-AXV5O2IZ.js → chunk-AKQ57NXY.js} +3 -3
  8. package/dist/chunk-CLLY6XB3.js +41 -0
  9. package/dist/chunk-DTXNDLSE.js +125 -0
  10. package/dist/chunk-F6FMZJDG.js +113 -0
  11. package/dist/{chunk-2WDJR6K7.js → chunk-G7IFL4Z4.js} +4 -1
  12. package/dist/chunk-H3ZZDX5L.js +61 -0
  13. package/dist/{chunk-H3BV4VAG.js → chunk-JS3EINZD.js} +2 -2
  14. package/dist/chunk-MZL7CGCL.js +109 -0
  15. package/dist/{chunk-TH6W2PWG.js → chunk-UDTG3RSE.js} +1 -1
  16. package/dist/chunk-WCTHZKU7.js +37 -0
  17. package/dist/{chunk-TT3WFS6S.js → chunk-YOKHBAIH.js} +11 -11
  18. package/dist/components/alert-dialog.js +1 -1
  19. package/dist/components/alert.d.ts +2 -2
  20. package/dist/components/avatar.d.ts +3 -3
  21. package/dist/components/badge.d.ts +1 -1
  22. package/dist/components/badge.js +1 -1
  23. package/dist/components/button.d.ts +2 -2
  24. package/dist/components/button.js +1 -1
  25. package/dist/components/calendar.js +1 -1
  26. package/dist/components/card.d.ts +4 -4
  27. package/dist/components/carousel.js +1 -1
  28. package/dist/components/combobox.js +1 -1
  29. package/dist/components/command.d.ts +2 -2
  30. package/dist/components/concept-compare-rail.d.ts +50 -0
  31. package/dist/components/concept-compare-rail.js +5 -0
  32. package/dist/components/concept-compare.d.ts +75 -38
  33. package/dist/components/concept-compare.js +209 -133
  34. package/dist/components/concept-convergence.d.ts +41 -0
  35. package/dist/components/concept-convergence.js +53 -0
  36. package/dist/components/convergence.d.ts +25 -0
  37. package/dist/components/convergence.js +4 -0
  38. package/dist/components/dialog.d.ts +2 -2
  39. package/dist/components/empty-state.js +1 -1
  40. package/dist/components/event-card.js +1 -1
  41. package/dist/components/event-compact-row.js +1 -1
  42. package/dist/components/event-detail-panel.js +2 -2
  43. package/dist/components/event-index-row.js +1 -1
  44. package/dist/components/event-list-row-wide.js +2 -2
  45. package/dist/components/event-list-row.js +2 -2
  46. package/dist/components/explainer-steps.d.ts +34 -0
  47. package/dist/components/explainer-steps.js +36 -0
  48. package/dist/components/filter-category-row.js +1 -1
  49. package/dist/components/glass-ground.d.ts +61 -0
  50. package/dist/components/glass-ground.js +3 -0
  51. package/dist/components/haptic-button.js +1 -1
  52. package/dist/components/hub-surface.js +11 -1
  53. package/dist/components/hue-chip.d.ts +70 -0
  54. package/dist/components/hue-chip.js +3 -0
  55. package/dist/components/input-group.d.ts +2 -2
  56. package/dist/components/input.d.ts +1 -1
  57. package/dist/components/item.d.ts +4 -4
  58. package/dist/components/kbd.d.ts +2 -2
  59. package/dist/components/kitchen-dossier.d.ts +88 -0
  60. package/dist/components/kitchen-dossier.js +186 -0
  61. package/dist/components/link-button.js +1 -1
  62. package/dist/components/menu-dead-end.d.ts +56 -0
  63. package/dist/components/menu-dead-end.js +49 -0
  64. package/dist/components/menu-family-block.d.ts +98 -0
  65. package/dist/components/menu-family-block.js +153 -0
  66. package/dist/components/menu-family-card.d.ts +33 -27
  67. package/dist/components/menu-family-card.js +41 -85
  68. package/dist/components/menu-field.js +92 -2
  69. package/dist/components/menu-filter-chips.d.ts +86 -0
  70. package/dist/components/menu-filter-chips.js +122 -0
  71. package/dist/components/menu-group-rows.d.ts +53 -0
  72. package/dist/components/menu-group-rows.js +67 -0
  73. package/dist/components/menu-hub-hero.js +10 -5
  74. package/dist/components/menu-index-header.d.ts +37 -0
  75. package/dist/components/menu-index-header.js +68 -0
  76. package/dist/components/menu-index-list.d.ts +48 -0
  77. package/dist/components/menu-index-list.js +96 -0
  78. package/dist/components/menu-intent-hero.d.ts +69 -0
  79. package/dist/components/menu-intent-hero.js +87 -0
  80. package/dist/components/menu-item.js +3 -3
  81. package/dist/components/menu-kitchen-list.d.ts +82 -0
  82. package/dist/components/menu-kitchen-list.js +5 -0
  83. package/dist/components/menu-mocks.d.ts +37 -2
  84. package/dist/components/menu-mocks.js +152 -27
  85. package/dist/components/menu-types.d.ts +182 -6
  86. package/dist/components/motion-icon-controls.js +2 -2
  87. package/dist/components/motion-icon-set.js +6 -6
  88. package/dist/components/motion-icon.d.ts +22 -1
  89. package/dist/components/motion-icon.js +1 -1
  90. package/dist/components/multi-select-combobox.js +2 -2
  91. package/dist/components/native-select.d.ts +2 -2
  92. package/dist/components/pagination.js +1 -1
  93. package/dist/components/photo-ground.d.ts +64 -0
  94. package/dist/components/photo-ground.js +3 -0
  95. package/dist/components/place-about-tab.js +2 -2
  96. package/dist/components/place-details-mobile.js +3 -3
  97. package/dist/components/place-types.d.ts +2 -2
  98. package/dist/components/qa-block.d.ts +36 -0
  99. package/dist/components/qa-block.js +24 -0
  100. package/dist/components/scroll-fade.js +3 -121
  101. package/dist/components/searchable-select.js +1 -1
  102. package/dist/components/select.d.ts +1 -1
  103. package/dist/components/signature-chip.d.ts +2 -2
  104. package/dist/components/signature-chip.js +2 -2
  105. package/dist/components/sliding-panels.js +1 -1
  106. package/dist/components/snap-rail.d.ts +26 -3
  107. package/dist/components/snap-rail.js +3 -109
  108. package/dist/components/spinner.d.ts +4 -4
  109. package/dist/components/story.d.ts +34 -0
  110. package/dist/components/story.js +3 -0
  111. package/dist/components/tag.d.ts +4 -4
  112. package/dist/components/text.d.ts +5 -5
  113. package/dist/components/toggle.d.ts +1 -1
  114. package/dist/components/top-comparison.d.ts +28 -0
  115. package/dist/components/top-comparison.js +76 -0
  116. package/dist/components/top-cross-link.d.ts +34 -0
  117. package/dist/components/top-cross-link.js +101 -0
  118. package/dist/components/top-ranking-entry.d.ts +53 -0
  119. package/dist/components/top-ranking-entry.js +217 -0
  120. package/dist/components/top-ranking-hero.d.ts +61 -0
  121. package/dist/components/top-ranking-hero.js +99 -0
  122. package/dist/components/top-ranking-legend.d.ts +69 -0
  123. package/dist/components/top-ranking-legend.js +124 -0
  124. package/dist/components/top-types.d.ts +109 -0
  125. package/dist/components/top-types.js +1 -0
  126. package/dist/components/venue-grade-dimensions.d.ts +3 -1
  127. package/dist/components/venue-grade-dimensions.js +1 -1
  128. package/dist/components/venue-quotes.d.ts +1 -1
  129. package/dist/components/venue-quotes.js +1 -1
  130. package/dist/components/venue-wall.js +5 -3
  131. package/dist/components/weekday-theme-banner.js +1 -1
  132. package/dist/components/weekend-panel.js +2 -2
  133. package/package.json +1 -1
  134. package/src/components/badge.tsx +1 -1
  135. package/src/components/button.tsx +2 -2
  136. package/src/components/concept-compare-rail.tsx +132 -0
  137. package/src/components/concept-compare.stories.tsx +122 -54
  138. package/src/components/concept-compare.tsx +368 -196
  139. package/src/components/concept-convergence.stories.tsx +53 -0
  140. package/src/components/concept-convergence.tsx +90 -0
  141. package/src/components/convergence.stories.tsx +54 -0
  142. package/src/components/convergence.tsx +177 -0
  143. package/src/components/explainer-steps.stories.tsx +46 -0
  144. package/src/components/explainer-steps.tsx +74 -0
  145. package/src/components/glass-ground.tsx +104 -0
  146. package/src/components/hub-surface.tsx +11 -1
  147. package/src/components/hue-chip.stories.tsx +76 -0
  148. package/src/components/hue-chip.test.tsx +62 -0
  149. package/src/components/hue-chip.tsx +99 -0
  150. package/src/components/kitchen-dossier.stories.tsx +108 -0
  151. package/src/components/kitchen-dossier.tsx +321 -0
  152. package/src/components/menu-dead-end.stories.tsx +67 -0
  153. package/src/components/menu-dead-end.tsx +127 -0
  154. package/src/components/menu-family-block.stories.tsx +91 -0
  155. package/src/components/menu-family-block.tsx +303 -0
  156. package/src/components/menu-family-card.stories.tsx +43 -35
  157. package/src/components/menu-family-card.tsx +115 -142
  158. package/src/components/menu-filter-chips.stories.tsx +107 -0
  159. package/src/components/menu-filter-chips.tsx +238 -0
  160. package/src/components/menu-group-rows.stories.tsx +54 -0
  161. package/src/components/menu-group-rows.tsx +133 -0
  162. package/src/components/menu-hub-hero.test.tsx +39 -0
  163. package/src/components/menu-hub-hero.tsx +17 -5
  164. package/src/components/menu-index-header.stories.tsx +72 -0
  165. package/src/components/menu-index-header.tsx +125 -0
  166. package/src/components/menu-index-list.stories.tsx +50 -0
  167. package/src/components/menu-index-list.tsx +149 -0
  168. package/src/components/menu-intent-hero.stories.tsx +67 -0
  169. package/src/components/menu-intent-hero.tsx +189 -0
  170. package/src/components/menu-kitchen-list.stories.tsx +67 -0
  171. package/src/components/menu-kitchen-list.test.tsx +78 -0
  172. package/src/components/menu-kitchen-list.tsx +284 -0
  173. package/src/components/menu-mocks.ts +198 -23
  174. package/src/components/menu-types.ts +189 -6
  175. package/src/components/motion-icon-controls.tsx +11 -11
  176. package/src/components/motion-icon-set.tsx +6 -5
  177. package/src/components/motion-icon.test.tsx +23 -1
  178. package/src/components/motion-icon.tsx +20 -0
  179. package/src/components/photo-ground.tsx +147 -0
  180. package/src/components/place-types.ts +2 -2
  181. package/src/components/qa-block.stories.tsx +43 -0
  182. package/src/components/qa-block.tsx +60 -0
  183. package/src/components/signature-chip.tsx +3 -3
  184. package/src/components/snap-rail.tsx +58 -15
  185. package/src/components/story.tsx +225 -0
  186. package/src/components/top-comparison.stories.tsx +78 -0
  187. package/src/components/top-comparison.tsx +151 -0
  188. package/src/components/top-cross-link.stories.tsx +84 -0
  189. package/src/components/top-cross-link.tsx +171 -0
  190. package/src/components/top-ranking-entry.stories.tsx +171 -0
  191. package/src/components/top-ranking-entry.test.tsx +105 -0
  192. package/src/components/top-ranking-entry.tsx +425 -0
  193. package/src/components/top-ranking-hero.stories.tsx +76 -0
  194. package/src/components/top-ranking-hero.tsx +218 -0
  195. package/src/components/top-ranking-legend.stories.tsx +78 -0
  196. package/src/components/top-ranking-legend.tsx +239 -0
  197. package/src/components/top-types.ts +110 -0
  198. package/src/components/venue-grade-dimensions.tsx +2 -2
  199. package/src/components/venue-quotes.stories.tsx +1 -1
  200. package/src/components/venue-quotes.tsx +4 -2
  201. package/src/components/venue-wall.tsx +9 -4
  202. package/src/components/weekday-theme-banner.tsx +1 -1
  203. package/dist/chunk-THOVPZNN.js +0 -37
  204. package/dist/chunk-YS6BHKZO.js +0 -92
  205. package/dist/components/ranked-venue-list.d.ts +0 -49
  206. package/dist/components/ranked-venue-list.js +0 -87
  207. package/dist/components/signature-line-list.d.ts +0 -45
  208. package/dist/components/signature-line-list.js +0 -99
  209. package/dist/components/venue-busyness-strip.d.ts +0 -35
  210. package/dist/components/venue-busyness-strip.js +0 -37
  211. package/dist/components/venue-signature-block.d.ts +0 -39
  212. package/dist/components/venue-signature-block.js +0 -6
  213. package/dist/components/venue-spotlight.d.ts +0 -74
  214. package/dist/components/venue-spotlight.js +0 -138
  215. package/src/components/ranked-venue-list.stories.tsx +0 -80
  216. package/src/components/ranked-venue-list.tsx +0 -177
  217. package/src/components/signature-line-list.stories.tsx +0 -85
  218. package/src/components/signature-line-list.tsx +0 -136
  219. package/src/components/venue-busyness-strip.stories.tsx +0 -66
  220. package/src/components/venue-busyness-strip.tsx +0 -64
  221. package/src/components/venue-signature-block.stories.tsx +0 -68
  222. package/src/components/venue-signature-block.tsx +0 -71
  223. package/src/components/venue-spotlight.stories.tsx +0 -141
  224. package/src/components/venue-spotlight.tsx +0 -236
  225. package/dist/{chunk-XWMRABZW.js → chunk-HSIYYA2E.js} +1 -1
@@ -0,0 +1,37 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { MenuIndexEntry, MenuLinkComponent } from './menu-types.js';
3
+ import 'react';
4
+ import './place-types.js';
5
+
6
+ type MenuIndexHeaderProps = {
7
+ /** Small caps over the title, e.g. "MENU · CLUJ-NAPOCA". */
8
+ eyebrow?: string;
9
+ title: string;
10
+ /** Sits beside the title, e.g. "38 cuisines". Pluralised by the caller. */
11
+ count?: string;
12
+ subtitle?: string;
13
+ /** The Food / Drinks control. A `<SegmentedNav>`, composed by the caller. */
14
+ scopeSwitch?: React.ReactNode;
15
+ className?: string;
16
+ };
17
+ declare function MenuIndexHeader({ eyebrow, title, count, subtitle, scopeSwitch, className, }: MenuIndexHeaderProps): react_jsx_runtime.JSX.Element;
18
+ /**
19
+ * MenuJumpStrip — the page's contents, as chips.
20
+ *
21
+ * It answers "move between families without going back to the hub", and it
22
+ * doubles as a table of contents: each chip carries the family's mark and its
23
+ * count, so the strip says how long the page is before the reader scrolls it.
24
+ *
25
+ * Only the families drawn in full belong here. A strip listing every value in
26
+ * the index below would be the index, twice.
27
+ */
28
+ type MenuJumpStripProps = {
29
+ entries: MenuIndexEntry[];
30
+ formatCount?: (value: number) => string;
31
+ ariaLabel?: string;
32
+ linkComponent?: MenuLinkComponent;
33
+ className?: string;
34
+ };
35
+ declare function MenuJumpStrip({ entries, formatCount, ariaLabel, linkComponent: LinkComponent, className, }: MenuJumpStripProps): react_jsx_runtime.JSX.Element;
36
+
37
+ export { MenuIndexHeader, type MenuIndexHeaderProps, MenuJumpStrip, type MenuJumpStripProps };
@@ -0,0 +1,68 @@
1
+ import { ScrollFade } from '../chunk-2XCZJWTV.js';
2
+ import { cn } from '../chunk-FEK5XGZW.js';
3
+ import { __objRest, __spreadValues } from '../chunk-3GWWZKX7.js';
4
+ import { jsxs, jsx } 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 MenuIndexHeader({
11
+ eyebrow,
12
+ title,
13
+ count,
14
+ subtitle,
15
+ scopeSwitch,
16
+ className
17
+ }) {
18
+ return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col gap-4 lg:flex-row lg:items-start lg:gap-8", className), children: [
19
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
20
+ eyebrow && /* @__PURE__ */ jsx("p", { className: "text-[11px] font-bold uppercase tracking-[0.1em] text-muted-foreground", children: eyebrow }),
21
+ /* @__PURE__ */ jsxs("h1", { className: "mt-1.5 text-[30px] font-black leading-[1.05] tracking-[-0.035em] lg:text-[40px]", children: [
22
+ title,
23
+ count && /* @__PURE__ */ jsx("span", { className: "ml-2.5 align-middle text-base font-semibold text-muted-foreground lg:text-lg", children: count })
24
+ ] }),
25
+ subtitle && /* @__PURE__ */ jsx("p", { className: "mt-2 max-w-[60ch] text-sm text-muted-foreground", children: subtitle })
26
+ ] }),
27
+ scopeSwitch && /* @__PURE__ */ jsx("div", { className: "sticky top-0 z-10 -mx-[var(--page-gutter)] bg-background px-[var(--page-gutter)] py-2 lg:static lg:mx-0 lg:bg-transparent lg:px-0 lg:py-0", children: scopeSwitch })
28
+ ] });
29
+ }
30
+ function MenuJumpStrip({
31
+ entries,
32
+ formatCount = String,
33
+ ariaLabel,
34
+ linkComponent: LinkComponent = DefaultLink,
35
+ className
36
+ }) {
37
+ return /* @__PURE__ */ jsx(
38
+ ScrollFade,
39
+ {
40
+ orientation: "horizontal",
41
+ size: "sm",
42
+ className,
43
+ scrollClassName: "scrollbar-hide",
44
+ children: /* @__PURE__ */ jsx("nav", { "aria-label": ariaLabel, className: "flex items-center gap-2", children: entries.map((entry) => /* @__PURE__ */ jsxs(
45
+ LinkComponent,
46
+ {
47
+ href: entry.href,
48
+ className: "inline-flex h-9 shrink-0 items-center gap-2 whitespace-nowrap rounded-full border border-border bg-card px-4 text-sm transition-colors hover:border-accent/40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring motion-reduce:transition-none",
49
+ children: [
50
+ /* @__PURE__ */ jsx(
51
+ "span",
52
+ {
53
+ "aria-hidden": true,
54
+ className: cn("h-2.5 w-2.5 shrink-0 rounded-[3px]", !entry.gradient && "bg-border"),
55
+ style: entry.gradient ? { backgroundImage: entry.gradient } : void 0
56
+ }
57
+ ),
58
+ entry.name,
59
+ /* @__PURE__ */ jsx("span", { className: "tabular-nums text-muted-foreground", children: formatCount(entry.count) })
60
+ ]
61
+ },
62
+ entry.slug
63
+ )) })
64
+ }
65
+ );
66
+ }
67
+
68
+ export { MenuIndexHeader, MenuJumpStrip };
@@ -0,0 +1,48 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { MenuIndexEntry, MenuLinkComponent, MenuLinkProps } from './menu-types.js';
3
+ import 'react';
4
+ import './place-types.js';
5
+
6
+ /**
7
+ * MenuIndexList — the complete set, four across.
8
+ *
9
+ * ## This block is why the page can afford to be progressive
10
+ *
11
+ * It carries a crawlable link to EVERY destination the city has — all
12
+ * thirty-eight cuisines, not the ten the page draws in full below. That makes
13
+ * it the page's entire link-equity job, which in turn means the rich blocks
14
+ * underneath are free to load ten at a time behind a "show more": nothing a
15
+ * crawler needs is hidden behind that control, because it is all up here.
16
+ *
17
+ * Get this backwards — put the whole set behind the disclosure and only ten
18
+ * links in the markup — and the page silently stops linking to twenty-eight
19
+ * pages it is supposed to be the index for.
20
+ *
21
+ * ## Ordered by count, and the counts overlap
22
+ *
23
+ * Cuisines are non-exclusive tags: Cluj's thirty-eight sum to 3,182 against 767
24
+ * places, because a trattoria is Italian AND pizza AND international. So this
25
+ * is a ranked list of ways in, never a partition, and no copy near it may total
26
+ * the numbers or call them a breakdown.
27
+ */
28
+ /** @deprecated Alias of `MenuLinkProps`; kept for symmetry with the other menu surfaces. */
29
+ type MenuIndexListLinkProps = MenuLinkProps;
30
+ type MenuIndexListProps = {
31
+ entries: MenuIndexEntry[];
32
+ /** e.g. "All 38 cuisines". Composed and pluralised by the caller. */
33
+ title: string;
34
+ /** One line under it, e.g. "Ordered by kitchens — every one has its own page". */
35
+ subtitle?: string;
36
+ /** Locale-aware integer formatter. Defaults to `String`. */
37
+ formatCount?: (value: number) => string;
38
+ linkComponent?: MenuLinkComponent;
39
+ className?: string;
40
+ };
41
+ declare function MenuIndexList({ entries, title, subtitle, formatCount, linkComponent: LinkComponent, className, }: MenuIndexListProps): react_jsx_runtime.JSX.Element;
42
+ /** Two columns on a phone, four from `lg` — the grid the list itself renders. */
43
+ declare function MenuIndexListSkeleton({ rows, className, }: {
44
+ rows?: number;
45
+ className?: string;
46
+ }): react_jsx_runtime.JSX.Element;
47
+
48
+ export { MenuIndexList, type MenuIndexListLinkProps, type MenuIndexListProps, MenuIndexListSkeleton };
@@ -0,0 +1,96 @@
1
+ import { Separator } from '../chunk-WSRWFA6K.js';
2
+ import { Skeleton } from '../chunk-QXHMPJ35.js';
3
+ import { cn } from '../chunk-FEK5XGZW.js';
4
+ import { __objRest, __spreadValues } from '../chunk-3GWWZKX7.js';
5
+ import { ChevronRight } from 'lucide-react';
6
+ import { jsxs, jsx } from 'react/jsx-runtime';
7
+
8
+ function DefaultLink(_a) {
9
+ var _b = _a, { href } = _b, props = __objRest(_b, ["href"]);
10
+ return /* @__PURE__ */ jsx("a", __spreadValues({ href }, props));
11
+ }
12
+ function MenuIndexList({
13
+ entries,
14
+ title,
15
+ subtitle,
16
+ formatCount = String,
17
+ linkComponent: LinkComponent = DefaultLink,
18
+ className
19
+ }) {
20
+ return /* @__PURE__ */ jsxs(
21
+ "section",
22
+ {
23
+ className: cn(
24
+ "rounded-[18px] border border-border bg-card px-3.5 py-4 lg:px-5 lg:py-5",
25
+ className
26
+ ),
27
+ children: [
28
+ /* @__PURE__ */ jsxs("div", { className: "pb-2", children: [
29
+ /* @__PURE__ */ jsx("h2", { className: "text-[15px] font-bold tracking-[-0.01em]", children: title }),
30
+ subtitle && /* @__PURE__ */ jsx("p", { className: "mt-0.5 text-[12.5px] text-muted-foreground", children: subtitle })
31
+ ] }),
32
+ /* @__PURE__ */ jsx("ul", { className: "grid grid-cols-2 gap-x-5 lg:grid-cols-4 lg:gap-x-8", children: entries.map((entry) => /* @__PURE__ */ jsxs("li", { children: [
33
+ /* @__PURE__ */ jsx(Separator, {}),
34
+ /* @__PURE__ */ jsxs(
35
+ LinkComponent,
36
+ {
37
+ href: entry.href,
38
+ className: "group flex items-center gap-2.5 py-2.5 transition-colors hover:text-accent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring motion-reduce:transition-none",
39
+ children: [
40
+ /* @__PURE__ */ jsx(
41
+ "span",
42
+ {
43
+ "aria-hidden": true,
44
+ className: cn(
45
+ "h-2.5 w-2.5 shrink-0 rounded-[3px]",
46
+ !entry.gradient && "bg-border"
47
+ ),
48
+ style: entry.gradient ? { backgroundImage: entry.gradient } : void 0
49
+ }
50
+ ),
51
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate text-[13.5px]", children: entry.name }),
52
+ /* @__PURE__ */ jsx("span", { className: "shrink-0 text-[13px] tabular-nums text-muted-foreground", children: formatCount(entry.count) }),
53
+ /* @__PURE__ */ jsx(
54
+ ChevronRight,
55
+ {
56
+ className: "h-3.5 w-3.5 shrink-0 text-muted-foreground transition-colors group-hover:text-accent motion-reduce:transition-none",
57
+ "aria-hidden": true
58
+ }
59
+ )
60
+ ]
61
+ }
62
+ )
63
+ ] }, entry.slug)) })
64
+ ]
65
+ }
66
+ );
67
+ }
68
+ function MenuIndexListSkeleton({
69
+ rows = 24,
70
+ className
71
+ }) {
72
+ return /* @__PURE__ */ jsxs(
73
+ "section",
74
+ {
75
+ className: cn(
76
+ "rounded-[18px] border border-border bg-card px-3.5 py-4 lg:px-5 lg:py-5",
77
+ className
78
+ ),
79
+ "aria-hidden": true,
80
+ children: [
81
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-[15px] w-[140px]" }),
82
+ /* @__PURE__ */ jsx(Skeleton, { className: "mt-1.5 h-3 w-[240px]" }),
83
+ /* @__PURE__ */ jsx("ul", { className: "mt-2 grid grid-cols-2 gap-x-5 lg:grid-cols-4 lg:gap-x-8", children: Array.from({ length: rows }, (_, index) => /* @__PURE__ */ jsxs("li", { children: [
84
+ /* @__PURE__ */ jsx(Separator, {}),
85
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2.5 py-2.5", children: [
86
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-2.5 w-2.5 rounded-[3px]" }),
87
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-3.5 flex-1" }),
88
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-3 w-6" })
89
+ ] })
90
+ ] }, index)) })
91
+ ]
92
+ }
93
+ );
94
+ }
95
+
96
+ export { MenuIndexList, MenuIndexListSkeleton };
@@ -0,0 +1,69 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { MenuIntentFigure, MenuLinkComponent } from './menu-types.js';
3
+ import 'react';
4
+ import './place-types.js';
5
+
6
+ /**
7
+ * MenuIntentHero — the head of an intent page (`/menu/cuisines/italian`).
8
+ *
9
+ * The intent page answers "which kitchens, for this, right now", and the hero's
10
+ * job is to make the size of the answer legible before the reader touches a
11
+ * filter: how many kitchens cook this, what they are known for, how many are
12
+ * serving. Every number in it is real and every one of them is a control.
13
+ *
14
+ * ## Distinct from `<MenuHubHero>`
15
+ *
16
+ * The hub's hero introduces a city's whole menu and carries a search field.
17
+ * This one introduces ONE intent and carries figures that filter the list
18
+ * below it. They share a look — a tinted panel with a watermark — and nothing
19
+ * else, so they are two components rather than one with a mode.
20
+ *
21
+ * ## The figures are the first filter
22
+ *
23
+ * "58 open now" is both a fact and the fastest way to act on it, so tapping a
24
+ * figure applies it as a filter rather than scrolling somewhere. A figure with
25
+ * no `onSelect` renders as a plain fact: the total is normally one, because
26
+ * filtering to "all of them" is not a state.
27
+ *
28
+ * They stay four across on the phone. The board proved the row legible at
29
+ * 390px, and a 2×2 costs ~40px of height in the tallest element on the
30
+ * smallest screen; the labels wrap instead.
31
+ *
32
+ * ## The paragraph survives the phone
33
+ *
34
+ * The board hides it under 640px. It is kept here and clamped instead: it is
35
+ * the page's only prose, it is what the description meta is built from, and
36
+ * `display: none` on the one indexed sentence is a cost the extra three lines
37
+ * do not justify.
38
+ */
39
+ type MenuIntentHeroProps = {
40
+ title: string;
41
+ /**
42
+ * Over the title — the active constraint ("TONIGHT · OPEN NOW OR LATER"), or
43
+ * the intent's kind ("CUISINE") when there is none.
44
+ */
45
+ kicker?: string;
46
+ /** Icon node for the kicker, resolved by the caller. */
47
+ kickerIcon?: React.ReactNode;
48
+ /** One paragraph of real numbers. Generated by the caller from the facets. */
49
+ description?: string;
50
+ figures?: MenuIntentFigure[];
51
+ /**
52
+ * Finished CSS gradient for the ground, e.g. `linear-gradient(140deg, …)`.
53
+ *
54
+ * A gradient rather than a token name, for the same reason `<MenuField>` and
55
+ * `<MenuIntentTiles>` take one: the hue tables are app-owned, and a component
56
+ * referencing `var(--h-pizza)` renders an unstyled panel in any consumer that
57
+ * does not define it. Missing, the panel falls back to the foreground colour
58
+ * — visibly unstyled rather than white text on white.
59
+ */
60
+ gradient?: string;
61
+ /** Watermark behind the text, resolved by the caller. */
62
+ icon?: React.ReactNode;
63
+ /** Used for any figure carrying an `href`. Defaults to a plain `<a>`. */
64
+ linkComponent?: MenuLinkComponent;
65
+ className?: string;
66
+ };
67
+ declare function MenuIntentHero({ title, kicker, kickerIcon, description, figures, gradient, icon, linkComponent, className, }: MenuIntentHeroProps): react_jsx_runtime.JSX.Element;
68
+
69
+ export { MenuIntentHero, type MenuIntentHeroProps };
@@ -0,0 +1,87 @@
1
+ import { cn } from '../chunk-FEK5XGZW.js';
2
+ import { __objRest, __spreadValues } from '../chunk-3GWWZKX7.js';
3
+ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
4
+
5
+ function DefaultLink(_a) {
6
+ var _b = _a, { href } = _b, props = __objRest(_b, ["href"]);
7
+ return /* @__PURE__ */ jsx("a", __spreadValues({ href }, props));
8
+ }
9
+ function MenuIntentHero({
10
+ title,
11
+ kicker,
12
+ kickerIcon,
13
+ description,
14
+ figures,
15
+ gradient,
16
+ icon,
17
+ linkComponent = DefaultLink,
18
+ className
19
+ }) {
20
+ return /* @__PURE__ */ jsxs(
21
+ "div",
22
+ {
23
+ className: cn(
24
+ "relative flex overflow-hidden rounded-[18px] text-white lg:min-h-[210px]",
25
+ !gradient && "bg-foreground",
26
+ className
27
+ ),
28
+ children: [
29
+ gradient && /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "absolute inset-0", style: { backgroundImage: gradient } }),
30
+ icon && /* @__PURE__ */ jsx(
31
+ "span",
32
+ {
33
+ "aria-hidden": true,
34
+ className: "pointer-events-none absolute -bottom-7 -right-4 opacity-20 [&>svg]:h-[120px] [&>svg]:w-[120px] lg:[&>svg]:h-[170px] lg:[&>svg]:w-[170px]",
35
+ children: icon
36
+ }
37
+ ),
38
+ /* @__PURE__ */ jsxs("div", { className: "relative z-[1] flex w-full flex-col gap-2.5 p-5 lg:gap-[11px] lg:px-[26px] lg:py-6", children: [
39
+ kicker && /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1.5 text-[10.5px] font-extrabold uppercase tracking-[0.13em] opacity-[0.82]", children: [
40
+ kickerIcon,
41
+ kicker
42
+ ] }),
43
+ /* @__PURE__ */ jsx("h1", { className: "font-serif text-[29px] font-semibold leading-[1.04] tracking-[-0.025em] lg:text-[40px]", children: title }),
44
+ description && /* @__PURE__ */ jsx("p", { className: "line-clamp-3 max-w-[520px] text-[13.5px] leading-[1.55] opacity-85 lg:line-clamp-none lg:text-sm", children: description }),
45
+ figures && figures.length > 0 && /* @__PURE__ */ jsx("div", { className: "mt-auto grid grid-cols-4 gap-x-4 gap-y-3 pt-3.5 lg:flex lg:gap-[26px] lg:pt-4", children: figures.map((figure) => /* @__PURE__ */ jsx(Figure, { figure, linkComponent }, figure.key)) })
46
+ ] })
47
+ ]
48
+ }
49
+ );
50
+ }
51
+ function Figure({
52
+ figure,
53
+ linkComponent: LinkComponent
54
+ }) {
55
+ const body = /* @__PURE__ */ jsxs(Fragment, { children: [
56
+ /* @__PURE__ */ jsx("span", { className: "block text-xl font-black leading-none tracking-[-0.03em] tabular-nums lg:text-2xl", children: figure.value }),
57
+ /* @__PURE__ */ jsx("span", { className: "mt-1 block text-[11px] leading-tight opacity-[0.72]", children: figure.label })
58
+ ] });
59
+ const interactive = cn(
60
+ "block min-w-0 rounded-lg text-left transition-opacity hover:opacity-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/70 motion-reduce:transition-none",
61
+ figure.active ? "opacity-100" : "opacity-90"
62
+ );
63
+ if (figure.href) {
64
+ return /* @__PURE__ */ jsx(
65
+ LinkComponent,
66
+ {
67
+ href: figure.href,
68
+ "aria-current": figure.active ? "page" : void 0,
69
+ className: interactive,
70
+ children: body
71
+ }
72
+ );
73
+ }
74
+ if (!figure.onSelect) return /* @__PURE__ */ jsx("span", { className: "block min-w-0", children: body });
75
+ return /* @__PURE__ */ jsx(
76
+ "button",
77
+ {
78
+ type: "button",
79
+ onClick: figure.onSelect,
80
+ "aria-pressed": figure.active,
81
+ className: interactive,
82
+ children: body
83
+ }
84
+ );
85
+ }
86
+
87
+ export { MenuIntentHero };
@@ -1,5 +1,5 @@
1
- export { MentionSignal, MenuItem } from '../chunk-TH6W2PWG.js';
2
- import '../chunk-Q6QH52LJ.js';
3
- import '../chunk-UKBNQO6Q.js';
1
+ export { MentionSignal, MenuItem } from '../chunk-UDTG3RSE.js';
2
+ import '../chunk-6TEEY5YA.js';
3
+ import '../chunk-2CXU5WZO.js';
4
4
  import '../chunk-FEK5XGZW.js';
5
5
  import '../chunk-3GWWZKX7.js';
@@ -0,0 +1,82 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { MenuKitchenPreview, MenuLinkComponent, MenuLinkProps } from './menu-types.js';
3
+ import 'react';
4
+ import './place-types.js';
5
+
6
+ /**
7
+ * MenuKitchenList — the intent page's spine.
8
+ *
9
+ * Venues ranked by how often reviews name their dishes, each row carrying the
10
+ * dish that earned it its place. Not a grid of dishes: a shortlist of rooms you
11
+ * could walk into.
12
+ *
13
+ * ## Two targets, and why this is not `<RankedVenueList>`
14
+ *
15
+ * The row goes to the venue. The dish name opens the item drawer — the same
16
+ * drawer the dish tiles open, listing every kitchen that makes it and for how
17
+ * much. Those are two different destinations inside one row, and the hub's
18
+ * `<RankedVenueList>` wraps its ENTIRE row in a single `LinkComponent`, so it
19
+ * structurally cannot express a second one. That is the reason this exists
20
+ * rather than a variant flag on that component.
21
+ *
22
+ * The mechanism is a stretched overlay, as in `<MenuItem>`: the venue link is
23
+ * an absolutely-positioned sibling at `z-[1]`, and the dish link sits above it
24
+ * at `z-[2]`. Nesting one anchor inside another is invalid HTML and breaks
25
+ * middle-click on both.
26
+ *
27
+ * The dish name is underlined so it reads as a target before it is pressed. The
28
+ * chevron belongs to the venue and stays on the right, away from it — an
29
+ * unmarked second target inside a row that already navigates is a trap.
30
+ *
31
+ * ## The filtered state changes the row's reason for being there
32
+ *
33
+ * With a dish or dietary filter on, each row swaps its most-named dish for the
34
+ * one that actually matched, and the kicker changes with it ("MOST NAMED HERE"
35
+ * → "WHY IT MATCHES"). A filtered list whose rows still advertise their
36
+ * most-named dish is claiming the filter matched something it did not.
37
+ *
38
+ * ## Header
39
+ *
40
+ * Deliberately none. The page composes `<SectionHeader>` above it, the same way
41
+ * the events hub does, because the heading here carries a count and a "see the
42
+ * full ranking" link into `/top` that the list knows nothing about.
43
+ */
44
+ /** @deprecated Alias of `MenuLinkProps`; kept for symmetry with the other menu surfaces. */
45
+ type MenuKitchenListLinkProps = MenuLinkProps;
46
+ type MenuKitchenListLabels = {
47
+ /** Accessible prefix for the row's venue link, e.g. "Open". */
48
+ openVenue: (name: string) => string;
49
+ /** Accessible name for the dish link, e.g. "See Truffle Tagliatelle". */
50
+ openDish: (name: string) => string;
51
+ /** Accessible name for the open-now dot. */
52
+ openNow: string;
53
+ /** Reveals the next page of rows, e.g. "Show 10 more". */
54
+ showMore?: string;
55
+ };
56
+ type MenuKitchenListProps = {
57
+ kitchens: MenuKitchenPreview[];
58
+ labels: MenuKitchenListLabels;
59
+ /**
60
+ * Reveals more rows IN PLACE. The intent page pages ten at a time and never
61
+ * navigates for the eleventh — the list is the page.
62
+ */
63
+ onShowMore?: () => void;
64
+ linkComponent?: MenuLinkComponent;
65
+ className?: string;
66
+ };
67
+ declare function MenuKitchenList({ kitchens, labels, onShowMore, linkComponent, className, }: MenuKitchenListProps): react_jsx_runtime.JSX.Element;
68
+ /**
69
+ * The list's own loading state, at the row's own footprint.
70
+ *
71
+ * It lives HERE, beside the component, and that is the whole point: a skeleton
72
+ * kept in the consuming app drifts the moment the row's padding or thumbnail
73
+ * size changes, and the drift is invisible until a page reflows on load. The
74
+ * measurements below are the same literals the row above uses — change one and
75
+ * the diff shows you the other.
76
+ */
77
+ declare function MenuKitchenListSkeleton({ rows, className, }: {
78
+ rows?: number;
79
+ className?: string;
80
+ }): react_jsx_runtime.JSX.Element;
81
+
82
+ export { MenuKitchenList, type MenuKitchenListLabels, type MenuKitchenListLinkProps, type MenuKitchenListProps, MenuKitchenListSkeleton };
@@ -0,0 +1,5 @@
1
+ export { MenuKitchenList, MenuKitchenListSkeleton } from '../chunk-24RFI4RQ.js';
2
+ import '../chunk-WSRWFA6K.js';
3
+ import '../chunk-QXHMPJ35.js';
4
+ import '../chunk-FEK5XGZW.js';
5
+ import '../chunk-3GWWZKX7.js';
@@ -1,4 +1,4 @@
1
- import { MenuConceptPreview, MenuFamilyPreview, MenuIntentTile, MenuItemPreview, MenuVenuePreview } from './menu-types.js';
1
+ import { MenuFamilyBlockPreview, MenuConceptPreview, MenuFamilyPreview, MenuFilterChip, MenuIndexEntry, MenuIntentFigure, MenuIntentTile, MenuItemPreview, MenuKitchenPreview, MenuLoosenOption, QaItem, MenuVenuePreview } from './menu-types.js';
2
2
  import { PlacePreview } from './place-types.js';
3
3
  import 'react';
4
4
 
@@ -51,5 +51,40 @@ declare const mockHubLabels: {
51
51
  openNow: string;
52
52
  until: string;
53
53
  };
54
+ /**
55
+ * The intent page's kitchen rows.
56
+ *
57
+ * The gaps are the gaps the API actually has, and they are the reason the row
58
+ * degrades the way it does: `must_try` entries arrive from a LIST endpoint, so
59
+ * a third of them carry no `slug` (nothing to open — the dish line goes inert)
60
+ * and none of them carry `mentions` (only `review_ids.length`, which is absent
61
+ * for census items — so the count line disappears rather than reading zero).
62
+ * A layout that survives these survives production.
63
+ */
64
+ declare const mockKitchens: MenuKitchenPreview[];
65
+ /** The same rows in a filtered state — each answering the filter, not volunteering. */
66
+ declare const mockMatchedKitchens: MenuKitchenPreview[];
67
+ declare const mockIntentFigures: MenuIntentFigure[];
68
+ /**
69
+ * Note there is no zero-count chip here, and that is not an oversight: the
70
+ * facets endpoint omits values that would match nothing, so a chip can only
71
+ * become `empty` client-side, after it was offered and the reader's next filter
72
+ * emptied it. The `Dead` story constructs that case deliberately.
73
+ */
74
+ declare const mockFilterChips: MenuFilterChip[];
75
+ declare const mockLoosenOptions: MenuLoosenOption[];
76
+ /**
77
+ * The long tail is the point of these fixtures.
78
+ *
79
+ * Cluj's cuisine facet runs from 457 down to 1, and twenty-two of the
80
+ * thirty-eight have fewer than twenty places. A layout tested only on the head
81
+ * of that list looks fine and then ships a page whose last two columns are
82
+ * single digits.
83
+ */
84
+ declare const mockIndexEntries: MenuIndexEntry[];
85
+ declare const mockFamilyBlock: MenuFamilyBlockPreview;
86
+ /** No photographs, no venue, no price — the degraded block still reads. */
87
+ declare const mockBareFamilyBlock: MenuFamilyBlockPreview;
88
+ declare const mockQa: QaItem[];
54
89
 
55
- export { mockConcept, mockDrinkFamily, mockFamily, mockFormatters, mockGradients, mockHubLabels, mockIntentTiles, mockItems, mockPlaces, mockVenues };
90
+ export { mockBareFamilyBlock, mockConcept, mockDrinkFamily, mockFamily, mockFamilyBlock, mockFilterChips, mockFormatters, mockGradients, mockHubLabels, mockIndexEntries, mockIntentFigures, mockIntentTiles, mockItems, mockKitchens, mockLoosenOptions, mockMatchedKitchens, mockPlaces, mockQa, mockVenues };