@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,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 };
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { MenuIntentTile, MenuLinkComponent, MenuLinkProps } from './menu-types.js';
|
|
3
|
+
import 'react';
|
|
4
|
+
import './place-types.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* MenuIntentTiles — the sentence people arrive with, as six coloured tiles.
|
|
8
|
+
*
|
|
9
|
+
* An intent is a family plus a constraint: Italian *tonight*, burgers, coffee.
|
|
10
|
+
* Each tile is a destination, so `href` is required on the view model — a
|
|
11
|
+
* category page that does not exist is filtered out by the caller rather than
|
|
12
|
+
* rendered as a tile that links nowhere. Roughly thirty-two of thirty-eight
|
|
13
|
+
* cuisines have a page, so that case is normal, not exceptional.
|
|
14
|
+
*
|
|
15
|
+
* The icon is a watermark bleeding off the bottom-right corner at low opacity,
|
|
16
|
+
* not a badge: it gives the tile a silhouette to be recognised by in a rail
|
|
17
|
+
* without competing with the label. It is `aria-hidden` — the label already
|
|
18
|
+
* says what the tile is.
|
|
19
|
+
*
|
|
20
|
+
* Server-safe.
|
|
21
|
+
*/
|
|
22
|
+
/** @deprecated Alias of `MenuLinkProps`; kept so existing imports keep working. */
|
|
23
|
+
type MenuIntentTilesLinkProps = MenuLinkProps;
|
|
24
|
+
type MenuIntentTilesProps = {
|
|
25
|
+
tiles: MenuIntentTile[];
|
|
26
|
+
linkComponent?: MenuLinkComponent;
|
|
27
|
+
className?: string;
|
|
28
|
+
};
|
|
29
|
+
declare function MenuIntentTiles({ tiles, linkComponent: LinkComponent, className, }: MenuIntentTilesProps): react_jsx_runtime.JSX.Element;
|
|
30
|
+
|
|
31
|
+
export { MenuIntentTiles, type MenuIntentTilesLinkProps, type MenuIntentTilesProps };
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { cn } from '../chunk-FEK5XGZW.js';
|
|
2
|
+
import { __objRest, __spreadValues } from '../chunk-3GWWZKX7.js';
|
|
3
|
+
import { ChevronRight } from 'lucide-react';
|
|
4
|
+
import { jsx, jsxs } 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 MenuIntentTiles({
|
|
11
|
+
tiles,
|
|
12
|
+
linkComponent: LinkComponent = DefaultLink,
|
|
13
|
+
className
|
|
14
|
+
}) {
|
|
15
|
+
return /* @__PURE__ */ jsx(
|
|
16
|
+
"div",
|
|
17
|
+
{
|
|
18
|
+
className: cn(
|
|
19
|
+
"grid grid-cols-2 gap-3 lg:grid-cols-6 lg:gap-3.5",
|
|
20
|
+
className
|
|
21
|
+
),
|
|
22
|
+
children: tiles.map((tile) => /* @__PURE__ */ jsxs(
|
|
23
|
+
LinkComponent,
|
|
24
|
+
{
|
|
25
|
+
href: tile.href,
|
|
26
|
+
className: "relative flex min-h-[110px] flex-col overflow-hidden rounded-2xl bg-muted p-3.5 text-white focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 lg:min-h-[124px] lg:px-3.5 lg:pb-3.5 lg:pt-[15px]",
|
|
27
|
+
style: tile.gradient ? { backgroundImage: tile.gradient } : void 0,
|
|
28
|
+
children: [
|
|
29
|
+
tile.icon && /* @__PURE__ */ jsx(
|
|
30
|
+
"span",
|
|
31
|
+
{
|
|
32
|
+
"aria-hidden": true,
|
|
33
|
+
className: "pointer-events-none absolute -bottom-2.5 -right-1.5 z-0 opacity-[0.22] [&>svg]:h-[62px] [&>svg]:w-[62px]",
|
|
34
|
+
children: tile.icon
|
|
35
|
+
}
|
|
36
|
+
),
|
|
37
|
+
/* @__PURE__ */ jsxs("span", { className: "relative z-[1] flex h-full flex-col", children: [
|
|
38
|
+
/* @__PURE__ */ jsx("span", { className: "text-[14.5px] font-extrabold leading-[1.2] tracking-[-0.015em]", children: tile.label }),
|
|
39
|
+
tile.caption && /* @__PURE__ */ jsx("span", { className: "mt-[3px] text-[11.5px] leading-[1.35]", children: tile.caption }),
|
|
40
|
+
/* @__PURE__ */ jsx(ChevronRight, { className: "mt-auto h-4 w-4 opacity-85", "aria-hidden": true })
|
|
41
|
+
] })
|
|
42
|
+
]
|
|
43
|
+
},
|
|
44
|
+
tile.slug
|
|
45
|
+
))
|
|
46
|
+
}
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export { MenuIntentTiles };
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
3
|
+
import { MenuItemPreview, MenuLinkComponent, MenuLinkProps } from './menu-types.js';
|
|
4
|
+
import './place-types.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* MenuItem — THE menu row.
|
|
8
|
+
*
|
|
9
|
+
* A venue's page (its menu list, its signature picks, the "pairs well with"
|
|
10
|
+
* list inside the entity drawer) and the cross-venue surfaces (the city menu,
|
|
11
|
+
* the hub's "known for" list, the daily editorial buckets) all render this one
|
|
12
|
+
* component, so an item is visibly the same object wherever it is met.
|
|
13
|
+
*
|
|
14
|
+
* Hoisted out of the app, where it had grown a second and a third lookalike
|
|
15
|
+
* beside it. It carries the app version's design decisions verbatim; only the
|
|
16
|
+
* couplings changed — the locale, the translation keys and the category-colour
|
|
17
|
+
* table are injected, because the DS owns none of those.
|
|
18
|
+
*
|
|
19
|
+
* ## The left mark
|
|
20
|
+
*
|
|
21
|
+
* An 8px rounded tab in the section's CATEGORY colour, flat against the row's
|
|
22
|
+
* left edge and bulging right. It replaced an earlier monogram chip, and the
|
|
23
|
+
* trade was deliberate:
|
|
24
|
+
*
|
|
25
|
+
* + The text column widens. At 390px the monogram costs 50px (38 + gap) and
|
|
26
|
+
* the tab costs 16px, so the text column goes 308px → 342px: ~11% more room
|
|
27
|
+
* for names and descriptions, which means fewer wrapped names and more
|
|
28
|
+
* items per screen.
|
|
29
|
+
* + The hue answers "which section am I in" rather than "what is this made
|
|
30
|
+
* of". Every row in a category draws the same mark, so it survives the
|
|
31
|
+
* category header scrolling out of view — which a per-item ingredient hue
|
|
32
|
+
* cannot do, since it changes for reasons the reader cannot see.
|
|
33
|
+
* − COLOUR-BLINDNESS. The monogram gave a reader who cannot separate the hues
|
|
34
|
+
* a LETTER; a rule collapses to the same grey bar for everyone. Both marks
|
|
35
|
+
* are `aria-hidden` and neither carries meaning the text lacks, so this is
|
|
36
|
+
* not a WCAG 1.4.1 failure — but it is a real loss of scannability for ~8%
|
|
37
|
+
* of men, and it has not been bought back yet.
|
|
38
|
+
*
|
|
39
|
+
* `markGradient` is passed IN, never derived: inside a menu the colour depends
|
|
40
|
+
* on the neighbouring sections, which a single row cannot see.
|
|
41
|
+
*
|
|
42
|
+
* ## No chevron
|
|
43
|
+
*
|
|
44
|
+
* The row is already `role="button"`, so an arrow would only restate that, and
|
|
45
|
+
* the right edge belongs to the mention count, which the arrow would collide
|
|
46
|
+
* with.
|
|
47
|
+
*/
|
|
48
|
+
/** @deprecated Alias of `MenuLinkProps`; kept so existing imports keep working. */
|
|
49
|
+
type MenuItemLinkProps = MenuLinkProps;
|
|
50
|
+
type MenuItemLabels = {
|
|
51
|
+
/** Currency unit beside the price, e.g. "RON". */
|
|
52
|
+
currency: string;
|
|
53
|
+
/** Fully-worded mention count — the accessible name for the glyph-and-numeral pair. */
|
|
54
|
+
mentions: (count: number) => string;
|
|
55
|
+
/** Chip text for a `signature`-strength item. */
|
|
56
|
+
signature: string;
|
|
57
|
+
/** Chip text for a `weak` (merely mentioned) item. */
|
|
58
|
+
mentioned: string;
|
|
59
|
+
};
|
|
60
|
+
type MenuItemProps = {
|
|
61
|
+
item: MenuItemPreview;
|
|
62
|
+
labels: MenuItemLabels;
|
|
63
|
+
/** Locale-aware money formatter. The DS owns no `Intl` locale. */
|
|
64
|
+
formatPrice: (amount: number) => string;
|
|
65
|
+
/** Locale-aware integer formatter for the mention count. Defaults to `String`. */
|
|
66
|
+
formatCount?: (value: number) => string;
|
|
67
|
+
/**
|
|
68
|
+
* Opens the item's own sheet. Ignored when `href` is set.
|
|
69
|
+
*
|
|
70
|
+
* Neither this nor `href` → an INERT row: same geometry, mark, type and meta
|
|
71
|
+
* line, but no `role="button"`, no tab stop and no hover or press states.
|
|
72
|
+
* That is the must-try mention with no menu row behind it — it has nothing to
|
|
73
|
+
* open, so it renders as content rather than as a control that goes nowhere.
|
|
74
|
+
*/
|
|
75
|
+
onOpen?: () => void;
|
|
76
|
+
showSignatureBadge?: boolean;
|
|
77
|
+
signatureTier?: "strong" | "weak";
|
|
78
|
+
onSignatureEvidence?: () => void;
|
|
79
|
+
/** Opens the reviews behind the mention count. */
|
|
80
|
+
onMentions?: () => void;
|
|
81
|
+
/** The mark's colour, as a CSS `background-image` value. */
|
|
82
|
+
markGradient?: string;
|
|
83
|
+
/**
|
|
84
|
+
* The venue this item belongs to — an attribution line under the name.
|
|
85
|
+
*
|
|
86
|
+
* Set it ONLY on cross-venue surfaces, where ten near-identical burgers come
|
|
87
|
+
* from ten kitchens and the venue IS the tap decision. A venue's own menu
|
|
88
|
+
* page must never pass it: the page already said whose menu this is. Passed
|
|
89
|
+
* in rather than read off `item.place` so the redundant case cannot be the
|
|
90
|
+
* default.
|
|
91
|
+
*/
|
|
92
|
+
venueName?: string;
|
|
93
|
+
/**
|
|
94
|
+
* Explicit subtitle, in italic, in place of the description. Used where the
|
|
95
|
+
* reviewer's reason IS the row's content rather than the payoff for tapping.
|
|
96
|
+
*/
|
|
97
|
+
subtitle?: string;
|
|
98
|
+
/**
|
|
99
|
+
* Render the row as a LINK instead of a `role="button"` div — for surfaces
|
|
100
|
+
* where it navigates rather than opening a sheet, so it stays
|
|
101
|
+
* middle-clickable and shareable. Implemented as a stretched overlay rather
|
|
102
|
+
* than by wrapping the row: the mention count is a control in its own right
|
|
103
|
+
* and a button inside an anchor is invalid.
|
|
104
|
+
*/
|
|
105
|
+
href?: string;
|
|
106
|
+
/** Accessible name for the link overlay. Defaults to the item's name. */
|
|
107
|
+
linkLabel?: string;
|
|
108
|
+
linkComponent?: MenuLinkComponent;
|
|
109
|
+
disabled?: boolean;
|
|
110
|
+
className?: string;
|
|
111
|
+
};
|
|
112
|
+
/**
|
|
113
|
+
* The mention count: a filled quote glyph and the numeral, pinned right.
|
|
114
|
+
*
|
|
115
|
+
* Deliberately NOT a badge — a chip would add a filled shape to a row that has
|
|
116
|
+
* none, and would read as a status label the venue was given rather than a
|
|
117
|
+
* count drawn from the same reviews.
|
|
118
|
+
*/
|
|
119
|
+
declare function MentionSignal({ label, count, onMentions, formatCount, }: {
|
|
120
|
+
label: string;
|
|
121
|
+
count: number;
|
|
122
|
+
onMentions?: () => void;
|
|
123
|
+
formatCount?: (value: number) => string;
|
|
124
|
+
}): react_jsx_runtime.JSX.Element;
|
|
125
|
+
declare const MenuItem: React.NamedExoticComponent<MenuItemProps>;
|
|
126
|
+
|
|
127
|
+
export { MentionSignal, MenuItem, type MenuItemLabels, type MenuItemLinkProps, type MenuItemProps };
|
|
@@ -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,90 @@
|
|
|
1
|
+
import { MenuFamilyBlockPreview, MenuConceptPreview, MenuFamilyPreview, MenuFilterChip, MenuIndexEntry, MenuIntentFigure, MenuIntentTile, MenuItemPreview, MenuKitchenPreview, MenuLoosenOption, QaItem, MenuVenuePreview } from './menu-types.js';
|
|
2
|
+
import { PlacePreview } from './place-types.js';
|
|
3
|
+
import 'react';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Fixtures for the menu-hub surfaces — used by their stories, and shipped so
|
|
7
|
+
* that consumers rendering these components outside an app (post-generator's
|
|
8
|
+
* Remotion frames, admin previews) do not each rebuild a plausible view model.
|
|
9
|
+
*
|
|
10
|
+
* ## Every venue here is invented
|
|
11
|
+
*
|
|
12
|
+
* The names, ratings and quotes are fabricated. Real venues must never appear
|
|
13
|
+
* in marketing or promotional renders — a real kitchen's rating in a promo
|
|
14
|
+
* frame is a claim about that business, made without its involvement, and it
|
|
15
|
+
* goes stale the moment the data moves. Anything filmed from these fixtures is
|
|
16
|
+
* safe to publish; anything filmed from live data is not.
|
|
17
|
+
*
|
|
18
|
+
* The SHAPES are real, though: the gaps are the gaps the API actually has, so
|
|
19
|
+
* a layout that survives these fixtures survives production. `mentions` is
|
|
20
|
+
* populated everywhere because it is populated everywhere upstream; one venue
|
|
21
|
+
* has no `must_try`, one has no photograph, and one grade is missing the
|
|
22
|
+
* dimensions a coffee bar never gets.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
declare const mockGradients: {
|
|
26
|
+
readonly pasta: "linear-gradient(150deg, oklch(0.555 0.105 80), oklch(0.455 0.10 70))";
|
|
27
|
+
readonly meat: "linear-gradient(150deg, oklch(0.49 0.13 28), oklch(0.41 0.12 22))";
|
|
28
|
+
readonly greens: "linear-gradient(150deg, oklch(0.505 0.115 148), oklch(0.415 0.11 142))";
|
|
29
|
+
readonly sweet: "linear-gradient(150deg, oklch(0.505 0.14 50), oklch(0.405 0.13 42))";
|
|
30
|
+
readonly cocktail: "linear-gradient(150deg, oklch(0.485 0.095 205), oklch(0.405 0.09 212))";
|
|
31
|
+
};
|
|
32
|
+
declare const mockPlaces: PlacePreview[];
|
|
33
|
+
declare const mockVenues: MenuVenuePreview[];
|
|
34
|
+
declare const mockItems: MenuItemPreview[];
|
|
35
|
+
declare const mockConcept: MenuConceptPreview;
|
|
36
|
+
declare const mockFamily: MenuFamilyPreview;
|
|
37
|
+
/** The drinks counterpart, and the reason `chip.count` is optional. */
|
|
38
|
+
declare const mockDrinkFamily: MenuFamilyPreview;
|
|
39
|
+
declare const mockIntentTiles: MenuIntentTile[];
|
|
40
|
+
/** Formatters the stories pass. Real callers inject locale-aware ones. */
|
|
41
|
+
declare const mockFormatters: {
|
|
42
|
+
price: (value: number) => string;
|
|
43
|
+
number: (value: number) => string;
|
|
44
|
+
score: (value: number) => string;
|
|
45
|
+
};
|
|
46
|
+
declare const mockHubLabels: {
|
|
47
|
+
mentions: (count: number) => string;
|
|
48
|
+
venues: (count: number) => string;
|
|
49
|
+
seeAll: string;
|
|
50
|
+
signature: string;
|
|
51
|
+
openNow: string;
|
|
52
|
+
until: string;
|
|
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[];
|
|
89
|
+
|
|
90
|
+
export { mockBareFamilyBlock, mockConcept, mockDrinkFamily, mockFamily, mockFamilyBlock, mockFilterChips, mockFormatters, mockGradients, mockHubLabels, mockIndexEntries, mockIntentFigures, mockIntentTiles, mockItems, mockKitchens, mockLoosenOptions, mockMatchedKitchens, mockPlaces, mockQa, mockVenues };
|