@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
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { Separator } from '../chunk-WSRWFA6K.js';
|
|
2
1
|
import { TagGroup } from '../chunk-6XMYEZVR.js';
|
|
3
2
|
import { Tag } from '../chunk-ZDAHKI3A.js';
|
|
3
|
+
import { Separator } from '../chunk-WSRWFA6K.js';
|
|
4
4
|
import { EventPosterFallback } from '../chunk-FDITZ2VM.js';
|
|
5
5
|
import { cn } from '../chunk-FEK5XGZW.js';
|
|
6
6
|
import { __objRest, __spreadValues } from '../chunk-3GWWZKX7.js';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { Separator } from '../chunk-WSRWFA6K.js';
|
|
2
|
-
import { Tag } from '../chunk-ZDAHKI3A.js';
|
|
3
1
|
import { resolveEventEntryFee } from '../chunk-DNSXMHYX.js';
|
|
2
|
+
import { Tag } from '../chunk-ZDAHKI3A.js';
|
|
3
|
+
import { Separator } from '../chunk-WSRWFA6K.js';
|
|
4
4
|
import { EventPosterFallback } from '../chunk-FDITZ2VM.js';
|
|
5
5
|
import { Skeleton } from '../chunk-QXHMPJ35.js';
|
|
6
6
|
import { buttonVariants } from '../chunk-H3BV4VAG.js';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { resolveEventEntryFee } from '../chunk-DNSXMHYX.js';
|
|
2
2
|
import { TagGroup } from '../chunk-6XMYEZVR.js';
|
|
3
3
|
import { Tag } from '../chunk-ZDAHKI3A.js';
|
|
4
|
-
import {
|
|
4
|
+
import { Separator } from '../chunk-WSRWFA6K.js';
|
|
5
5
|
import { EventPosterFallback } from '../chunk-FDITZ2VM.js';
|
|
6
6
|
import { Skeleton } from '../chunk-QXHMPJ35.js';
|
|
7
7
|
import { cn } from '../chunk-FEK5XGZW.js';
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* ExplainerSteps — how something works, as numbered rules a reader can scan.
|
|
6
|
+
*
|
|
7
|
+
* Each step is a numeral in a ring, one line of prose, and the live figure
|
|
8
|
+
* that proves the line, right-aligned with its unit. A thread runs from one
|
|
9
|
+
* numeral to the next so the three read as a sequence, and the last numeral
|
|
10
|
+
* is filled: the rules end where the reader is asked to do something.
|
|
11
|
+
*
|
|
12
|
+
* A step without a figure shows none — an invented number in the column is
|
|
13
|
+
* worse than an empty one. But a list where no step has a figure should be
|
|
14
|
+
* prose, not steps.
|
|
15
|
+
*
|
|
16
|
+
* Server-safe. Data in, not children: a server component composes these from
|
|
17
|
+
* translated strings and numbers, and an array is easier to number than a
|
|
18
|
+
* child list is to clone.
|
|
19
|
+
*/
|
|
20
|
+
type ExplainerStepItem = {
|
|
21
|
+
/** One line. Two at most on a phone; a third is a paragraph and belongs elsewhere. */
|
|
22
|
+
text: ReactNode;
|
|
23
|
+
/** The live figure, pre-formatted. */
|
|
24
|
+
figure?: string;
|
|
25
|
+
/** A unit or caption beside the figure, e.g. "names". */
|
|
26
|
+
unit?: string;
|
|
27
|
+
};
|
|
28
|
+
type ExplainerStepsProps = {
|
|
29
|
+
steps: ExplainerStepItem[];
|
|
30
|
+
className?: string;
|
|
31
|
+
};
|
|
32
|
+
declare function ExplainerSteps({ steps, className }: ExplainerStepsProps): react_jsx_runtime.JSX.Element;
|
|
33
|
+
|
|
34
|
+
export { type ExplainerStepItem, ExplainerSteps, type ExplainerStepsProps };
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { cn } from '../chunk-FEK5XGZW.js';
|
|
2
|
+
import '../chunk-3GWWZKX7.js';
|
|
3
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
4
|
+
|
|
5
|
+
function ExplainerSteps({ steps, className }) {
|
|
6
|
+
return /* @__PURE__ */ jsx("ol", { className: cn("flex flex-col", className), children: steps.map((step, index) => {
|
|
7
|
+
const isLast = index === steps.length - 1;
|
|
8
|
+
return /* @__PURE__ */ jsxs("li", { className: "relative flex gap-3.5 pb-5 last:pb-0", children: [
|
|
9
|
+
!isLast && /* @__PURE__ */ jsx(
|
|
10
|
+
"span",
|
|
11
|
+
{
|
|
12
|
+
"aria-hidden": true,
|
|
13
|
+
className: "absolute bottom-0 left-[11px] top-7 w-px bg-border"
|
|
14
|
+
}
|
|
15
|
+
),
|
|
16
|
+
/* @__PURE__ */ jsx(
|
|
17
|
+
"span",
|
|
18
|
+
{
|
|
19
|
+
"aria-hidden": true,
|
|
20
|
+
className: cn(
|
|
21
|
+
"relative flex h-6 w-6 shrink-0 items-center justify-center rounded-full text-[11px] font-bold tabular-nums",
|
|
22
|
+
isLast ? "bg-primary text-primary-foreground" : "border border-border bg-background text-muted-foreground"
|
|
23
|
+
),
|
|
24
|
+
children: index + 1
|
|
25
|
+
}
|
|
26
|
+
),
|
|
27
|
+
/* @__PURE__ */ jsx("p", { className: "min-w-0 flex-1 pt-0.5 text-[14px] leading-[1.45] text-foreground", children: step.text }),
|
|
28
|
+
step.figure && /* @__PURE__ */ jsxs("span", { className: "shrink-0 pt-0.5 text-right text-[15px] font-bold leading-[1.45] tabular-nums text-foreground", children: [
|
|
29
|
+
step.figure,
|
|
30
|
+
step.unit && /* @__PURE__ */ jsx("span", { className: "ml-1 text-[11px] font-normal text-muted-foreground", children: step.unit })
|
|
31
|
+
] })
|
|
32
|
+
] }, index);
|
|
33
|
+
}) });
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export { ExplainerSteps };
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* GlassGround — a frosted, inked caption rising out of one edge of a
|
|
5
|
+
* photograph.
|
|
6
|
+
*
|
|
7
|
+
* Layers over a `PhotoGround`: two masked `backdrop-blur`s (the frost) and
|
|
8
|
+
* an ink gradient (the contrast), running in one direction — absent where the
|
|
9
|
+
* photograph is the subject, solid where the type is. `MenuFamilyBlock` drew
|
|
10
|
+
* this first; this is the same idea with the stops as data, so a card can say
|
|
11
|
+
* "clear for 128px, then glass" without a sixth copy of the gradient strings.
|
|
12
|
+
*
|
|
13
|
+
* ## Why two blurs, and why the ramps are eased
|
|
14
|
+
*
|
|
15
|
+
* One blur under a linear mask has an edge: the eye reads the line where
|
|
16
|
+
* sharp becomes soft, however gently the mask fades it in. Two blurs — a
|
|
17
|
+
* light one that starts early and a heavier one that starts late — make the
|
|
18
|
+
* softening itself a gradient (the "progressive blur" of a phone's lock
|
|
19
|
+
* screen), and every ramp here is an ease-in through five stops rather than
|
|
20
|
+
* a straight line, because a straight ramp has a visible crease at each end
|
|
21
|
+
* where its slope changes.
|
|
22
|
+
*
|
|
23
|
+
* ## Why the stops are custom properties
|
|
24
|
+
*
|
|
25
|
+
* Tailwind emits only the classes it can read verbatim, so the gradients
|
|
26
|
+
* cannot be assembled from props into a class. The classes below are literal
|
|
27
|
+
* and read `--glass-*` variables; the component writes those inline, having
|
|
28
|
+
* interpolated the quarter stops in JS (`clear` and `full` must share a
|
|
29
|
+
* unit). Same mechanism as `HueChip`'s hue. The ink's colour is the
|
|
30
|
+
* package's one scrim literal, `oklch(0.16 0.012 55)`, never `var(--scrim)`.
|
|
31
|
+
*
|
|
32
|
+
* ## The ramp ends where the type begins
|
|
33
|
+
*
|
|
34
|
+
* `clear` is where the frost starts, `full` where it and the ink are solid.
|
|
35
|
+
* Set `full` to the exact seam the content starts at — a ramp that runs past
|
|
36
|
+
* the seam puts the first row of type on half-frost.
|
|
37
|
+
*/
|
|
38
|
+
type GlassStops = {
|
|
39
|
+
/** Which way the ink runs — toward the type. */
|
|
40
|
+
direction: "to bottom" | "to right";
|
|
41
|
+
/** Where the frost begins, e.g. `"0px"` or `"24%"`. */
|
|
42
|
+
clear: string;
|
|
43
|
+
/** Where frost and ink are solid — the seam the content starts at. Same unit as `clear`. */
|
|
44
|
+
full: string;
|
|
45
|
+
};
|
|
46
|
+
type GlassGroundProps = {
|
|
47
|
+
stops: GlassStops;
|
|
48
|
+
/** Stops from `lg`. Omit to keep the same ramp at every width. */
|
|
49
|
+
lgStops?: GlassStops;
|
|
50
|
+
/**
|
|
51
|
+
* How dark the ink gets at the far edge, 0–1. `0.96` is a caption the
|
|
52
|
+
* photograph is only a texture under; `0.4` is a photograph the caption is
|
|
53
|
+
* drawn on — the type is lifted, the room stays a room.
|
|
54
|
+
*/
|
|
55
|
+
peak?: number;
|
|
56
|
+
/**
|
|
57
|
+
* How much the frost softens the photograph where it is solid. `xl` turns
|
|
58
|
+
* a room into colour; `sm` keeps it a room and only takes the edge off the
|
|
59
|
+
* detail behind the type.
|
|
60
|
+
*/
|
|
61
|
+
blur?: "sm" | "md" | "lg" | "xl";
|
|
62
|
+
className?: string;
|
|
63
|
+
};
|
|
64
|
+
declare function GlassGround({ stops, lgStops, peak, blur, className, }: GlassGroundProps): react_jsx_runtime.JSX.Element;
|
|
65
|
+
|
|
66
|
+
export { GlassGround, type GlassGroundProps, type GlassStops };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* HubSurface — the surface a hub section sits on BELOW `lg`.
|
|
5
|
+
*
|
|
6
|
+
* On a phone a hub is an app screen, not a page: each section is a white card
|
|
7
|
+
* on the tinted ground, 18px radius, one card shadow and no border, with 14px
|
|
8
|
+
* of inset. Hairlines live inside a card, never across the page. From `lg` up
|
|
9
|
+
* the card dissolves — no fill, no radius, no inset — and the sections sit flat
|
|
10
|
+
* the way the desktop design draws them. One element, two looks; the section
|
|
11
|
+
* markup inside is identical at every width.
|
|
12
|
+
*
|
|
13
|
+
* `flush` drops the inset so a section whose content bleeds to the card edge —
|
|
14
|
+
* a snap rail, a full-width photograph — can pad its own header and let the
|
|
15
|
+
* rest run.
|
|
16
|
+
*
|
|
17
|
+
* Generalised out of the events hub's `EventsHubCard`, which had the same job
|
|
18
|
+
* and lived in the app. Nothing here is menu- or event-shaped.
|
|
19
|
+
*/
|
|
20
|
+
type HubSurfaceProps = {
|
|
21
|
+
flush?: boolean;
|
|
22
|
+
className?: string;
|
|
23
|
+
children: React.ReactNode;
|
|
24
|
+
};
|
|
25
|
+
declare function HubSurface({ flush, className, children }: HubSurfaceProps): react_jsx_runtime.JSX.Element;
|
|
26
|
+
|
|
27
|
+
export { HubSurface, type HubSurfaceProps };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { cn } from '../chunk-FEK5XGZW.js';
|
|
2
|
+
import '../chunk-3GWWZKX7.js';
|
|
3
|
+
import { jsx } from 'react/jsx-runtime';
|
|
4
|
+
|
|
5
|
+
function HubSurface({ flush, className, children }) {
|
|
6
|
+
return /* @__PURE__ */ jsx(
|
|
7
|
+
"section",
|
|
8
|
+
{
|
|
9
|
+
className: cn(
|
|
10
|
+
"rounded-[18px] border border-border bg-card shadow-none",
|
|
11
|
+
flush ? "overflow-hidden" : "px-3.5 py-4",
|
|
12
|
+
/*
|
|
13
|
+
* `lg:border-0` is load-bearing and was missing.
|
|
14
|
+
*
|
|
15
|
+
* Above `lg` this surface stops being a card — the background, the
|
|
16
|
+
* radius, the padding and the shadow are all dropped so the section
|
|
17
|
+
* sits on the page. The border was not, so every section on every
|
|
18
|
+
* desktop page kept a 1px rectangle around content that no longer had
|
|
19
|
+
* a card behind it. A caller that genuinely wants an edge at desktop
|
|
20
|
+
* adds `lg:border` in `className`, which lands after this in `cn`.
|
|
21
|
+
*/
|
|
22
|
+
"lg:overflow-visible lg:rounded-none lg:border-0 lg:bg-transparent lg:p-0 lg:shadow-none",
|
|
23
|
+
className
|
|
24
|
+
),
|
|
25
|
+
children
|
|
26
|
+
}
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export { HubSurface };
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* HueChip — a pill that wears a category's colour as a pale wash.
|
|
5
|
+
*
|
|
6
|
+
* Menu surfaces label things with a family's hue: a cuisine's dish groups, a
|
|
7
|
+
* going-out family's tags, a price band. Every one of them was an inline
|
|
8
|
+
* `style` with a hand-written `color-mix` next to a hand-written pill class
|
|
9
|
+
* list, which is how the two below drifted apart and how the mix space bug
|
|
10
|
+
* survived as long as it did — it had to be found and fixed in each copy.
|
|
11
|
+
*
|
|
12
|
+
* ## The mix space is the whole reason this is a component
|
|
13
|
+
*
|
|
14
|
+
* **`in oklab`, and never `in oklch`.** They describe the same colours, but
|
|
15
|
+
* oklch is POLAR, so `color-mix(in oklch, …)` interpolates the hue ANGLE — and
|
|
16
|
+
* the paper being mixed into is achromatic (`--card` is `oklch(1 0 0)`, hue 0).
|
|
17
|
+
* Mixing 20% of a family colour into that lands at a fifth of the family's hue:
|
|
18
|
+
* honey 82° came out at 16°, which is pink, so a Breakfast & Brunch card wore
|
|
19
|
+
* pink chips under an amber mosaic. Cool families were worse — a bar's 205°
|
|
20
|
+
* blue mixes to 41°, orange; a club's 300° violet to 60°, mustard.
|
|
21
|
+
*
|
|
22
|
+
* oklab is rectangular: mixing toward an achromatic base scales chroma along
|
|
23
|
+
* the SAME hue instead of rotating it, which is what "a pale version of this
|
|
24
|
+
* colour" has to mean.
|
|
25
|
+
*
|
|
26
|
+
* ## Why an inline style and not a Tailwind class
|
|
27
|
+
*
|
|
28
|
+
* The hue is DATA — one of ~40 cuisine values resolved by the app at request
|
|
29
|
+
* time — not one of a fixed set of variants. Tailwind can only emit classes it
|
|
30
|
+
* can see at build time, so a class-based version would mean either a
|
|
31
|
+
* safelisted colour per cuisine (a palette that must be edited to add a
|
|
32
|
+
* cuisine) or an arbitrary-value class built from a runtime string, which
|
|
33
|
+
* Tailwind cannot compile. A CSS custom property set inline is the honest
|
|
34
|
+
* mechanism; this component is the one place that writes it.
|
|
35
|
+
*/
|
|
36
|
+
/** Mix strengths, so callers say what a chip IS rather than picking a number. */
|
|
37
|
+
declare const STRENGTH: {
|
|
38
|
+
/** A labelled facet: readable at 11.5px, still clearly the family's colour. */
|
|
39
|
+
readonly chip: 20;
|
|
40
|
+
/** A quieter wash, for a chip that sits beside a stronger one. */
|
|
41
|
+
readonly subtle: 12;
|
|
42
|
+
};
|
|
43
|
+
type HueChipTone = keyof typeof STRENGTH;
|
|
44
|
+
/**
|
|
45
|
+
* The tint itself, exported for the surfaces that need the colour rather than
|
|
46
|
+
* the pill — a section ground, a hover state. Same rule: `in oklab`.
|
|
47
|
+
*/
|
|
48
|
+
declare function hueTint(hue: string, tone?: HueChipTone, base?: string): string;
|
|
49
|
+
type HueChipProps = {
|
|
50
|
+
/**
|
|
51
|
+
* A finished CSS colour — the DS owns no category table, so the app resolves
|
|
52
|
+
* it. Without one the chip falls back to `--muted`, which is the right
|
|
53
|
+
* neutral for a facet that has no family behind it.
|
|
54
|
+
*/
|
|
55
|
+
hue?: string;
|
|
56
|
+
tone?: HueChipTone;
|
|
57
|
+
/** The paper the wash is mixed into. Must be the surface the chip sits on. */
|
|
58
|
+
base?: string;
|
|
59
|
+
/** Drawn before the label, e.g. a wallet on a price band. */
|
|
60
|
+
icon?: React.ReactNode;
|
|
61
|
+
/** `li` inside a list, which is where most of these live. */
|
|
62
|
+
as?: "span" | "li" | "div";
|
|
63
|
+
children: React.ReactNode;
|
|
64
|
+
className?: string;
|
|
65
|
+
/** For a chip whose visible text is a bare figure, e.g. "22–86 lei". */
|
|
66
|
+
"aria-label"?: string;
|
|
67
|
+
};
|
|
68
|
+
declare function HueChip({ hue, tone, base, icon, as: Tag, children, className, "aria-label": ariaLabel, }: HueChipProps): react_jsx_runtime.JSX.Element;
|
|
69
|
+
|
|
70
|
+
export { HueChip, type HueChipProps, type HueChipTone, hueTint };
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
3
|
+
import { MenuVenueSignature, MenuLinkComponent, MenuLinkProps } from './menu-types.js';
|
|
4
|
+
import { PlacePreview, CitedText, PlaceGradeDimensions } from './place-types.js';
|
|
5
|
+
import { VenueGradeDimensionKey } from './venue-grade-dimensions.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* KitchenDossier — one kitchen, told as a story on its own photograph.
|
|
9
|
+
*
|
|
10
|
+
* The hub used to say this three times: a spotlight with everything the
|
|
11
|
+
* digest knows, a ranked row with a grade, a card with the signature dish —
|
|
12
|
+
* and the same kitchen led all three. This is the one card: the venue's
|
|
13
|
+
* photograph clear at the top, a frosted caption rising from the foot with
|
|
14
|
+
* the identity (rating, name, type · area · reviews), and under it a PANEL
|
|
15
|
+
* that says the three or four things the reviews established one at a time —
|
|
16
|
+
* the dish it is known for, what people say, the grade — with segments at the
|
|
17
|
+
* top edge showing which is up. The story runs on its own while the card is
|
|
18
|
+
* in view; a tap advances.
|
|
19
|
+
*
|
|
20
|
+
* ## A photograph with a caption, not a dark card
|
|
21
|
+
*
|
|
22
|
+
* `ConceptCompare` is a dark card with a photograph showing through two glass
|
|
23
|
+
* bands. This is the reverse — the room is the subject and the facts are its
|
|
24
|
+
* caption — which is `GlassGround` at its lightest and smallest: anchored to
|
|
25
|
+
* the CAPTION, not the card, so it covers the identity, the panel and the
|
|
26
|
+
* action plus a short lead above them and nothing else; a `sm` blur that
|
|
27
|
+
* only takes the edge off the detail; ink that never passes 0.45; a text
|
|
28
|
+
* shadow under the identity doing the rest. Everything above the caption is
|
|
29
|
+
* the photograph, untouched. The panel is its own glass, so its rule reads
|
|
30
|
+
* as a panel and not a hole. The same layout at every width; the desktop
|
|
31
|
+
* card is wider, not turned on its side.
|
|
32
|
+
*
|
|
33
|
+
* ## Two targets, and everything else is "next"
|
|
34
|
+
*
|
|
35
|
+
* The name and the action open the venue; the dish name in the first part
|
|
36
|
+
* opens the item. A tap anywhere else on the card advances the story.
|
|
37
|
+
*/
|
|
38
|
+
/** @deprecated Alias of `MenuLinkProps`; kept for symmetry with the other menu surfaces. */
|
|
39
|
+
type KitchenDossierLinkProps = MenuLinkProps;
|
|
40
|
+
type KitchenDossierLabels = {
|
|
41
|
+
/** Kickers over the three parts. */
|
|
42
|
+
knownFor: string;
|
|
43
|
+
whatPeopleSay: string;
|
|
44
|
+
theGrade: string;
|
|
45
|
+
/** Currency beside the price, e.g. "lei". */
|
|
46
|
+
currency: string;
|
|
47
|
+
/** Beside the mention count, e.g. "4 name it". */
|
|
48
|
+
mentions: (count: number) => string;
|
|
49
|
+
/** "427 reviews", composed by the caller. */
|
|
50
|
+
reviewCount?: string;
|
|
51
|
+
/** "Open until 22:00", composed by the caller. Omit when closed or unknown. */
|
|
52
|
+
openUntil?: string;
|
|
53
|
+
/** Accessible name per segment, e.g. "Part 2 of 3". */
|
|
54
|
+
part: (index: number, count: number) => string;
|
|
55
|
+
/** Accessible name of the tap-to-advance. */
|
|
56
|
+
next: string;
|
|
57
|
+
/** Dimension key → translated label. */
|
|
58
|
+
dimensions: Partial<Record<VenueGradeDimensionKey, string>>;
|
|
59
|
+
};
|
|
60
|
+
type KitchenDossierProps = {
|
|
61
|
+
place: PlacePreview;
|
|
62
|
+
/** The venue's destination. */
|
|
63
|
+
href: string;
|
|
64
|
+
/** "Restaurant · Centru", composed by the caller. */
|
|
65
|
+
meta?: string;
|
|
66
|
+
/** Part one. */
|
|
67
|
+
signature?: MenuVenueSignature;
|
|
68
|
+
/** Part two — the digest's highlights; two are shown. */
|
|
69
|
+
quotes?: CitedText[];
|
|
70
|
+
/** Part three — the grade, with the overall already formatted. */
|
|
71
|
+
grade?: {
|
|
72
|
+
overall?: string;
|
|
73
|
+
dimensions?: PlaceGradeDimensions;
|
|
74
|
+
};
|
|
75
|
+
labels: KitchenDossierLabels;
|
|
76
|
+
formatPrice: (amount: number) => string;
|
|
77
|
+
/** 0–100 → display numeral. */
|
|
78
|
+
formatScore: (value: number) => string;
|
|
79
|
+
/** How long each part shows. */
|
|
80
|
+
partDurationMs?: number;
|
|
81
|
+
/** The foot's action — pass a rendered link or button. */
|
|
82
|
+
action?: ReactNode;
|
|
83
|
+
linkComponent?: MenuLinkComponent;
|
|
84
|
+
className?: string;
|
|
85
|
+
};
|
|
86
|
+
declare function KitchenDossier({ place, href, meta, signature, quotes, grade, labels, formatPrice, formatScore, partDurationMs, action, linkComponent: LinkComponent, className, }: KitchenDossierProps): react_jsx_runtime.JSX.Element;
|
|
87
|
+
|
|
88
|
+
export { KitchenDossier, type KitchenDossierLabels, type KitchenDossierLinkProps, type KitchenDossierProps };
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import { DIMENSION_ORDER, VenueGradeDimensions } from '../chunk-AKQ57NXY.js';
|
|
2
|
+
import { VenueQuotes } from '../chunk-62ES6N32.js';
|
|
3
|
+
import { StoryProvider, StorySegments, StoryPanel } from '../chunk-F6FMZJDG.js';
|
|
4
|
+
import { GlassGround } from '../chunk-SY6RV476.js';
|
|
5
|
+
import { Separator } from '../chunk-WSRWFA6K.js';
|
|
6
|
+
import { PhotoGround, PHOTO_GROUND } from '../chunk-5RWTOT5Y.js';
|
|
7
|
+
import { cn } from '../chunk-FEK5XGZW.js';
|
|
8
|
+
import { __objRest, __spreadValues } from '../chunk-3GWWZKX7.js';
|
|
9
|
+
import { MessageSquareQuote, Star } from 'lucide-react';
|
|
10
|
+
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
11
|
+
|
|
12
|
+
function DefaultLink(_a) {
|
|
13
|
+
var _b = _a, { href } = _b, props = __objRest(_b, ["href"]);
|
|
14
|
+
return /* @__PURE__ */ jsx("a", __spreadValues({ href }, props));
|
|
15
|
+
}
|
|
16
|
+
var GLASS_LEAD = "128px";
|
|
17
|
+
var DIMENSION_LIMIT = 3;
|
|
18
|
+
var QUOTE_LIMIT = 2;
|
|
19
|
+
var initialOf = (name) => {
|
|
20
|
+
var _a, _b;
|
|
21
|
+
return ((_b = (_a = name.match(new RegExp("\\p{L}", "u"))) == null ? void 0 : _a[0]) != null ? _b : "").toLocaleUpperCase();
|
|
22
|
+
};
|
|
23
|
+
function KitchenDossier({
|
|
24
|
+
place,
|
|
25
|
+
href,
|
|
26
|
+
meta,
|
|
27
|
+
signature,
|
|
28
|
+
quotes,
|
|
29
|
+
grade,
|
|
30
|
+
labels,
|
|
31
|
+
formatPrice,
|
|
32
|
+
formatScore,
|
|
33
|
+
partDurationMs,
|
|
34
|
+
action,
|
|
35
|
+
linkComponent: LinkComponent = DefaultLink,
|
|
36
|
+
className
|
|
37
|
+
}) {
|
|
38
|
+
var _a, _b, _c, _d;
|
|
39
|
+
const rating = (_a = place.rating) == null ? void 0 : _a.rating;
|
|
40
|
+
const parts = [];
|
|
41
|
+
if (signature) {
|
|
42
|
+
const item = signature.item;
|
|
43
|
+
const firstEntry = (_b = item.sizes_and_prices) == null ? void 0 : _b[0];
|
|
44
|
+
const mentions = (_d = (_c = item.mentions) != null ? _c : item.review_count) != null ? _d : 0;
|
|
45
|
+
parts.push(
|
|
46
|
+
/* @__PURE__ */ jsxs(Part, { kicker: labels.knownFor, children: [
|
|
47
|
+
signature.href ? /* @__PURE__ */ jsx(
|
|
48
|
+
LinkComponent,
|
|
49
|
+
{
|
|
50
|
+
href: signature.href,
|
|
51
|
+
className: "w-max max-w-full text-[16px] font-bold leading-tight tracking-[-0.01em] text-white hover:underline focus-visible:outline-none focus-visible:underline",
|
|
52
|
+
children: item.name
|
|
53
|
+
}
|
|
54
|
+
) : /* @__PURE__ */ jsx("span", { className: "text-[16px] font-bold leading-tight tracking-[-0.01em] text-white", children: item.name }),
|
|
55
|
+
signature.why && /* @__PURE__ */ jsx("p", { className: "mt-1 line-clamp-2 font-serif text-[13px] italic leading-snug text-white/75", children: signature.why }),
|
|
56
|
+
(firstEntry || mentions > 0) && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
57
|
+
/* @__PURE__ */ jsx(Separator, { className: "my-2.5 via-white/20" }),
|
|
58
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-end justify-between gap-3", children: [
|
|
59
|
+
/* @__PURE__ */ jsx("span", { className: "flex items-baseline gap-1.5", children: firstEntry && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
60
|
+
/* @__PURE__ */ jsxs("span", { className: "text-[18px] font-extrabold leading-none tabular-nums text-white", children: [
|
|
61
|
+
formatPrice(firstEntry.price),
|
|
62
|
+
/* @__PURE__ */ jsx("span", { className: "ml-1 text-[12px] font-bold", children: labels.currency })
|
|
63
|
+
] }),
|
|
64
|
+
firstEntry.size > 0 && /* @__PURE__ */ jsxs("span", { className: "text-[11.5px] tabular-nums text-white/60", children: [
|
|
65
|
+
firstEntry.size,
|
|
66
|
+
item.kind === "dish" ? "g" : "ml"
|
|
67
|
+
] })
|
|
68
|
+
] }) }),
|
|
69
|
+
mentions > 0 && /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1 text-[12px] font-semibold tabular-nums text-white/85", children: [
|
|
70
|
+
/* @__PURE__ */ jsx(MessageSquareQuote, { className: "h-3.5 w-3.5", "aria-hidden": true }),
|
|
71
|
+
labels.mentions(mentions)
|
|
72
|
+
] })
|
|
73
|
+
] })
|
|
74
|
+
] })
|
|
75
|
+
] }, "signature")
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
if (quotes && quotes.length > 0) {
|
|
79
|
+
parts.push(
|
|
80
|
+
/* @__PURE__ */ jsx(Part, { kicker: labels.whatPeopleSay, children: /* @__PURE__ */ jsx(VenueQuotes, { quotes, limit: QUOTE_LIMIT, className: "lg:grid-cols-1" }) }, "quotes")
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
const dimensions = grade == null ? void 0 : grade.dimensions;
|
|
84
|
+
const strongest = dimensions ? DIMENSION_ORDER.filter((key) => typeof dimensions[key] === "number").sort((a, b) => {
|
|
85
|
+
var _a2, _b2;
|
|
86
|
+
return ((_a2 = dimensions[b]) != null ? _a2 : 0) - ((_b2 = dimensions[a]) != null ? _b2 : 0);
|
|
87
|
+
}).slice(0, DIMENSION_LIMIT) : [];
|
|
88
|
+
if (grade && (grade.overall || strongest.length > 0)) {
|
|
89
|
+
parts.push(
|
|
90
|
+
/* @__PURE__ */ jsx(Part, { kicker: labels.theGrade, children: /* @__PURE__ */ jsxs("div", { className: "flex items-end gap-4", children: [
|
|
91
|
+
grade.overall && /* @__PURE__ */ jsx("span", { className: "text-[34px] font-extrabold leading-none tracking-[-0.03em] tabular-nums text-[oklch(0.82_0.15_85)]", children: grade.overall }),
|
|
92
|
+
dimensions && strongest.length > 0 && /* @__PURE__ */ jsx(
|
|
93
|
+
VenueGradeDimensions,
|
|
94
|
+
{
|
|
95
|
+
className: "min-w-0 flex-1",
|
|
96
|
+
dimensions,
|
|
97
|
+
labels: labels.dimensions,
|
|
98
|
+
order: strongest,
|
|
99
|
+
formatScore
|
|
100
|
+
}
|
|
101
|
+
)
|
|
102
|
+
] }) }, "grade")
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
return /* @__PURE__ */ jsxs(
|
|
106
|
+
"article",
|
|
107
|
+
{
|
|
108
|
+
className: cn(
|
|
109
|
+
/* `flex flex-col` so the story fills the card's height: in a rail
|
|
110
|
+
the cards are stretched to the tallest, and a story that stopped
|
|
111
|
+
at its own min-height left a band of bare photograph under the
|
|
112
|
+
action on every shorter card. */
|
|
113
|
+
"dark relative isolate flex flex-col overflow-hidden rounded-[18px] border border-border text-foreground shadow-[var(--shadow-card)]",
|
|
114
|
+
PHOTO_GROUND,
|
|
115
|
+
className
|
|
116
|
+
),
|
|
117
|
+
children: [
|
|
118
|
+
/* @__PURE__ */ jsx(PhotoGround, { className: "-z-20", photos: [place.thumbnail_url], initials: initialOf(place.name), loading: "eager" }),
|
|
119
|
+
/* @__PURE__ */ jsxs(
|
|
120
|
+
StoryProvider,
|
|
121
|
+
{
|
|
122
|
+
count: parts.length,
|
|
123
|
+
durationMs: partDurationMs,
|
|
124
|
+
nextLabel: labels.next,
|
|
125
|
+
className: "relative flex min-h-[560px] flex-1 flex-col p-4 lg:min-h-[440px] lg:p-5",
|
|
126
|
+
children: [
|
|
127
|
+
/* @__PURE__ */ jsx(
|
|
128
|
+
StorySegments,
|
|
129
|
+
{
|
|
130
|
+
labels: parts.map((_, index) => labels.part(index, parts.length)),
|
|
131
|
+
className: "-mt-1"
|
|
132
|
+
}
|
|
133
|
+
),
|
|
134
|
+
/* @__PURE__ */ jsx("div", { className: "relative min-h-[184px] flex-1", children: labels.openUntil && /* @__PURE__ */ jsxs("span", { className: "absolute left-0 top-3 inline-flex h-[30px] items-center gap-[7px] rounded-full bg-[oklch(0.16_0.012_55_/_0.72)] px-3 text-[11.5px] font-bold text-white backdrop-blur-[2px]", children: [
|
|
135
|
+
/* @__PURE__ */ jsx("i", { className: "h-[7px] w-[7px] rounded-full bg-success", "aria-hidden": true }),
|
|
136
|
+
labels.openUntil
|
|
137
|
+
] }) }),
|
|
138
|
+
/* @__PURE__ */ jsxs("div", { className: "relative flex flex-col gap-3.5", children: [
|
|
139
|
+
/* @__PURE__ */ jsx(
|
|
140
|
+
"div",
|
|
141
|
+
{
|
|
142
|
+
"aria-hidden": true,
|
|
143
|
+
className: "pointer-events-none absolute -inset-x-4 -bottom-4 -top-32 -z-10 lg:-inset-x-5 lg:-bottom-5",
|
|
144
|
+
children: /* @__PURE__ */ jsx(
|
|
145
|
+
GlassGround,
|
|
146
|
+
{
|
|
147
|
+
stops: { direction: "to bottom", clear: "0px", full: GLASS_LEAD },
|
|
148
|
+
peak: 0.4,
|
|
149
|
+
blur: "sm"
|
|
150
|
+
}
|
|
151
|
+
)
|
|
152
|
+
}
|
|
153
|
+
),
|
|
154
|
+
/* @__PURE__ */ jsxs("div", { className: "[text-shadow:0_1px_12px_oklch(0_0_0/0.55)]", children: [
|
|
155
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-baseline justify-between gap-4", children: [
|
|
156
|
+
/* @__PURE__ */ jsx("h3", { className: "min-w-0 font-serif text-[26px] font-semibold leading-tight tracking-[-0.02em] text-white lg:text-[32px]", children: /* @__PURE__ */ jsx(LinkComponent, { href, className: "focus-visible:outline-none focus-visible:underline", children: place.name }) }),
|
|
157
|
+
rating !== void 0 && /* @__PURE__ */ jsxs("span", { className: "inline-flex shrink-0 items-center gap-1 text-[18px] font-extrabold leading-none tabular-nums text-white lg:text-[22px]", children: [
|
|
158
|
+
rating.toFixed(1),
|
|
159
|
+
/* @__PURE__ */ jsx(Star, { className: "h-3.5 w-3.5 lg:h-4 lg:w-4", "aria-hidden": true })
|
|
160
|
+
] })
|
|
161
|
+
] }),
|
|
162
|
+
(meta || labels.reviewCount) && /* @__PURE__ */ jsx("p", { className: "mt-1 truncate text-[12.5px] text-white/70", children: [meta, labels.reviewCount].filter(Boolean).join(" \xB7 ") })
|
|
163
|
+
] }),
|
|
164
|
+
parts.length > 0 && /* @__PURE__ */ jsx(
|
|
165
|
+
StoryPanel,
|
|
166
|
+
{
|
|
167
|
+
parts,
|
|
168
|
+
className: "rounded-[14px] border border-white/10 bg-[oklch(0.16_0.012_55_/_0.35)] p-4 backdrop-blur-md"
|
|
169
|
+
}
|
|
170
|
+
),
|
|
171
|
+
action && /* @__PURE__ */ jsx("div", { className: "flex [&>*]:w-full lg:[&>*]:w-auto", children: action })
|
|
172
|
+
] })
|
|
173
|
+
]
|
|
174
|
+
}
|
|
175
|
+
)
|
|
176
|
+
]
|
|
177
|
+
}
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
function Part({ kicker, children }) {
|
|
181
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
|
|
182
|
+
/* @__PURE__ */ jsx("span", { className: "mb-1.5 block text-[9.5px] font-bold uppercase tracking-[0.12em] text-white/60", children: kicker }),
|
|
183
|
+
children
|
|
184
|
+
] });
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export { KitchenDossier };
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { MenuLinkComponent, MenuLoosenOption } from './menu-types.js';
|
|
3
|
+
import 'react';
|
|
4
|
+
import './place-types.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* MenuDeadEnd — what dropped out, and the one press that brings it back.
|
|
8
|
+
*
|
|
9
|
+
* A filter combination that returns nothing is the intent page's worst state,
|
|
10
|
+
* and an empty-state illustration is the worst answer to it: the reader knows
|
|
11
|
+
* the list is empty, what they do not know is WHICH of their four filters did
|
|
12
|
+
* it. So this says what dropped out, why, and what widening one specific filter
|
|
13
|
+
* would restore — with the count, because "+5 kitchens, including the
|
|
14
|
+
* highest-rated Italian room in the city" is an answer and "try removing a
|
|
15
|
+
* filter" is a shrug.
|
|
16
|
+
*
|
|
17
|
+
* It is also drawn on a NON-empty list, whenever a filter has cost the reader
|
|
18
|
+
* something notable. The board's case is 34 kitchens still standing and Casa
|
|
19
|
+
* Boema — the one they came for — missing.
|
|
20
|
+
*
|
|
21
|
+
* Interactive: the consumer marks its own module `"use client"`.
|
|
22
|
+
*/
|
|
23
|
+
type MenuDeadEndProps = {
|
|
24
|
+
/**
|
|
25
|
+
* The explanation, composed by the caller.
|
|
26
|
+
*
|
|
27
|
+
* A node rather than a string because it emphasises real names ("**Casa
|
|
28
|
+
* Boema** and six others dropped out"), and assembling that from fragments
|
|
29
|
+
* here would produce translated word-salad in one of the two locales.
|
|
30
|
+
*/
|
|
31
|
+
children: React.ReactNode;
|
|
32
|
+
/** The widening this offers, e.g. "Raise to 60 lei · 16 kitchens". */
|
|
33
|
+
actionLabel?: string;
|
|
34
|
+
/** Where the widening leads. Preferred over `onAction` — see `MenuLoosenOption.href`. */
|
|
35
|
+
actionHref?: string;
|
|
36
|
+
onAction?: () => void;
|
|
37
|
+
linkComponent?: MenuLinkComponent;
|
|
38
|
+
className?: string;
|
|
39
|
+
};
|
|
40
|
+
declare function MenuDeadEnd({ children, actionLabel, actionHref, onAction, linkComponent: LinkComponent, className, }: MenuDeadEndProps): react_jsx_runtime.JSX.Element;
|
|
41
|
+
/**
|
|
42
|
+
* MenuLoosenList — one row per active filter, each priced in kitchens.
|
|
43
|
+
*
|
|
44
|
+
* The companion to the panel above: the panel names the single best widening,
|
|
45
|
+
* this lists every one of them so the reader can choose which constraint they
|
|
46
|
+
* care least about. Every row states what it would return, because a filter the
|
|
47
|
+
* reader cannot price is a filter they will not drop.
|
|
48
|
+
*/
|
|
49
|
+
type MenuLoosenListProps = {
|
|
50
|
+
options: MenuLoosenOption[];
|
|
51
|
+
linkComponent?: MenuLinkComponent;
|
|
52
|
+
className?: string;
|
|
53
|
+
};
|
|
54
|
+
declare function MenuLoosenList({ options, linkComponent: LinkComponent, className, }: MenuLoosenListProps): react_jsx_runtime.JSX.Element;
|
|
55
|
+
|
|
56
|
+
export { MenuDeadEnd, type MenuDeadEndProps, MenuLoosenList, type MenuLoosenListProps };
|