@bearmenu/ui 0.8.9 → 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-AXV5O2IZ.js → chunk-AKQ57NXY.js} +3 -3
- 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-SY6RV476.js +48 -0
- package/dist/chunk-UDN7UHJW.js +60 -0
- package/dist/{chunk-TT3WFS6S.js → chunk-YOKHBAIH.js} +11 -11
- package/dist/components/badge.d.ts +1 -1
- package/dist/components/button.d.ts +1 -1
- package/dist/components/command.d.ts +7 -7
- 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 +75 -38
- package/dist/components/concept-compare.js +209 -133
- 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.js +1 -1
- package/dist/components/event-detail-panel.js +1 -1
- package/dist/components/event-index-row.js +1 -1
- package/dist/components/event-list-row-wide.js +1 -1
- 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.js +11 -1
- 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 +33 -27
- package/dist/components/menu-family-card.js +43 -85
- package/dist/components/menu-field.js +92 -2
- 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-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-kitchen-list.d.ts +82 -0
- package/dist/components/menu-kitchen-list.js +5 -0
- package/dist/components/menu-mocks.d.ts +37 -2
- package/dist/components/menu-mocks.js +152 -27
- package/dist/components/menu-types.d.ts +182 -6
- package/dist/components/motion-icon-controls.js +2 -2
- package/dist/components/motion-icon-set.js +6 -6
- package/dist/components/motion-icon.d.ts +22 -1
- package/dist/components/motion-icon.js +1 -1
- 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 +2 -2
- 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/snap-rail.d.ts +24 -1
- package/dist/components/snap-rail.js +3 -109
- package/dist/components/spinner.d.ts +2 -2
- package/dist/components/story.d.ts +34 -0
- package/dist/components/story.js +3 -0
- package/dist/components/text.d.ts +3 -3
- 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 +3 -1
- package/dist/components/venue-grade-dimensions.js +1 -1
- package/dist/components/weekend-panel.js +2 -2
- package/package.json +1 -1
- package/src/components/concept-compare-rail.tsx +131 -0
- package/src/components/concept-compare.stories.tsx +122 -54
- package/src/components/concept-compare.tsx +363 -196
- 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/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.tsx +11 -1
- 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 +43 -35
- package/src/components/menu-family-card.tsx +122 -142
- 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-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-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 +198 -23
- package/src/components/menu-types.ts +189 -6
- package/src/components/motion-icon-controls.tsx +11 -11
- package/src/components/motion-icon-set.tsx +6 -5
- package/src/components/motion-icon.test.tsx +23 -1
- package/src/components/motion-icon.tsx +20 -0
- package/src/components/photo-ground.tsx +147 -0
- package/src/components/place-types.ts +2 -2
- package/src/components/qa-block.stories.tsx +43 -0
- package/src/components/qa-block.tsx +60 -0
- package/src/components/snap-rail.tsx +58 -15
- 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.tsx +2 -2
- package/dist/chunk-THOVPZNN.js +0 -37
- package/dist/chunk-YS6BHKZO.js +0 -92
- package/dist/components/ranked-venue-list.d.ts +0 -49
- package/dist/components/ranked-venue-list.js +0 -87
- package/dist/components/signature-line-list.d.ts +0 -45
- package/dist/components/signature-line-list.js +0 -99
- package/dist/components/venue-busyness-strip.d.ts +0 -35
- package/dist/components/venue-busyness-strip.js +0 -37
- package/dist/components/venue-signature-block.d.ts +0 -39
- package/dist/components/venue-signature-block.js +0 -6
- package/dist/components/venue-spotlight.d.ts +0 -74
- package/dist/components/venue-spotlight.js +0 -138
- package/src/components/ranked-venue-list.stories.tsx +0 -80
- package/src/components/ranked-venue-list.tsx +0 -177
- package/src/components/signature-line-list.stories.tsx +0 -85
- package/src/components/signature-line-list.tsx +0 -136
- package/src/components/venue-busyness-strip.stories.tsx +0 -66
- package/src/components/venue-busyness-strip.tsx +0 -64
- package/src/components/venue-signature-block.stories.tsx +0 -68
- package/src/components/venue-signature-block.tsx +0 -71
- package/src/components/venue-spotlight.stories.tsx +0 -141
- package/src/components/venue-spotlight.tsx +0 -236
- package/dist/{chunk-XWMRABZW.js → chunk-HSIYYA2E.js} +1 -1
|
@@ -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 };
|
|
@@ -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 };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import '../chunk-3GWWZKX7.js';
|
|
1
|
+
import { __spreadProps, __spreadValues } from '../chunk-3GWWZKX7.js';
|
|
2
2
|
|
|
3
3
|
// src/components/menu-mocks.ts
|
|
4
4
|
var mockGradients = {
|
|
@@ -312,19 +312,7 @@ var mockFamily = {
|
|
|
312
312
|
price_low: 32,
|
|
313
313
|
price_high: 84,
|
|
314
314
|
price_median: 49,
|
|
315
|
-
venue_photo_urls: []
|
|
316
|
-
signature: {
|
|
317
|
-
item: {
|
|
318
|
-
id: "f1",
|
|
319
|
-
slug: "truffle-tagliatelle",
|
|
320
|
-
name: "Truffle Tagliatelle",
|
|
321
|
-
kind: "dish",
|
|
322
|
-
mentions: 17,
|
|
323
|
-
sizes_and_prices: [{ size: 320, price: 62 }]
|
|
324
|
-
},
|
|
325
|
-
why: "Salt & Stone",
|
|
326
|
-
href: "/menu/dishes/truffle-tagliatelle"
|
|
327
|
-
}
|
|
315
|
+
venue_photo_urls: []
|
|
328
316
|
};
|
|
329
317
|
var mockDrinkFamily = {
|
|
330
318
|
slug: "cocktail",
|
|
@@ -340,18 +328,7 @@ var mockDrinkFamily = {
|
|
|
340
328
|
price_low: 18,
|
|
341
329
|
price_high: 54,
|
|
342
330
|
price_median: 28,
|
|
343
|
-
venue_photo_urls: []
|
|
344
|
-
signature: {
|
|
345
|
-
item: {
|
|
346
|
-
id: "f2",
|
|
347
|
-
slug: "house-negroni",
|
|
348
|
-
name: "House negroni",
|
|
349
|
-
kind: "drink",
|
|
350
|
-
mentions: 16,
|
|
351
|
-
sizes_and_prices: [{ size: 150, price: 32 }]
|
|
352
|
-
},
|
|
353
|
-
why: "The Copper Yard"
|
|
354
|
-
}
|
|
331
|
+
venue_photo_urls: []
|
|
355
332
|
};
|
|
356
333
|
var intentGradient = (hue) => `linear-gradient(150deg, oklch(0.52 0.14 ${hue}), oklch(0.43 0.13 ${hue - 4}))`;
|
|
357
334
|
var mockIntentTiles = [
|
|
@@ -375,5 +352,153 @@ var mockHubLabels = {
|
|
|
375
352
|
openNow: "Open now",
|
|
376
353
|
until: "until"
|
|
377
354
|
};
|
|
355
|
+
var mockKitchens = [
|
|
356
|
+
{
|
|
357
|
+
place: placeBySlug("salt-and-stone"),
|
|
358
|
+
href: "/places/salt-and-stone",
|
|
359
|
+
metaLabel: "courtyard \xB7 Centru",
|
|
360
|
+
hoursLabel: "kitchen until 23:00",
|
|
361
|
+
priceLabel: "62 lei",
|
|
362
|
+
dish: {
|
|
363
|
+
name: "Truffle Tagliatelle",
|
|
364
|
+
href: "/menu/dishes/truffle-tagliatelle",
|
|
365
|
+
kicker: "MOST NAMED HERE",
|
|
366
|
+
meta: "17 reviews name it"
|
|
367
|
+
}
|
|
368
|
+
},
|
|
369
|
+
{
|
|
370
|
+
place: placeBySlug("the-copper-yard"),
|
|
371
|
+
href: "/places/the-copper-yard",
|
|
372
|
+
metaLabel: "pizzeria \xB7 Centru",
|
|
373
|
+
hoursLabel: "kitchen until 23:30",
|
|
374
|
+
priceLabel: "54 lei",
|
|
375
|
+
dish: {
|
|
376
|
+
name: "Tagliatelle cu Hribi",
|
|
377
|
+
href: "/menu/dishes/tagliatelle-cu-hribi",
|
|
378
|
+
kicker: "MOST NAMED HERE",
|
|
379
|
+
meta: "14 reviews name it"
|
|
380
|
+
}
|
|
381
|
+
},
|
|
382
|
+
{
|
|
383
|
+
// Closed, and its must-try has no live menu row — the dish line is inert.
|
|
384
|
+
place: __spreadProps(__spreadValues({}, placeBySlug("the-quiet-hours")), { open_now: false }),
|
|
385
|
+
href: "/places/the-quiet-hours",
|
|
386
|
+
metaLabel: "bistro \xB7 Gheorgheni",
|
|
387
|
+
hoursLabel: "closed \xB7 opens 12:00",
|
|
388
|
+
priceLabel: "58 lei",
|
|
389
|
+
dish: { name: "Risotto ai Funghi", kicker: "MOST NAMED HERE" }
|
|
390
|
+
},
|
|
391
|
+
{
|
|
392
|
+
// No dish at all: the row must still read as a venue.
|
|
393
|
+
place: placeBySlug("green-room-canteen"),
|
|
394
|
+
href: "/places/green-room-canteen",
|
|
395
|
+
metaLabel: "bistro \xB7 Centru",
|
|
396
|
+
priceLabel: "38 lei"
|
|
397
|
+
}
|
|
398
|
+
];
|
|
399
|
+
var mockMatchedKitchens = mockKitchens.slice(0, 3).map((kitchen) => __spreadProps(__spreadValues({}, kitchen), {
|
|
400
|
+
dish: kitchen.dish && __spreadProps(__spreadValues({}, kitchen.dish), {
|
|
401
|
+
kicker: "WHY IT MATCHES",
|
|
402
|
+
meta: "mid-range \xB7 300g",
|
|
403
|
+
matched: true,
|
|
404
|
+
matchLabel: "vegetarian"
|
|
405
|
+
})
|
|
406
|
+
}));
|
|
407
|
+
var mockIntentFigures = [
|
|
408
|
+
{ key: "total", value: "247", label: "kitchens" },
|
|
409
|
+
{ key: "pizza", value: "120", label: "make a pizza", onSelect: () => {
|
|
410
|
+
} },
|
|
411
|
+
{ key: "pasta", value: "96", label: "make pasta", onSelect: () => {
|
|
412
|
+
} },
|
|
413
|
+
{ key: "open", value: "58", label: "open now", onSelect: () => {
|
|
414
|
+
} }
|
|
415
|
+
];
|
|
416
|
+
var mockFilterChips = [
|
|
417
|
+
{ key: "open", label: "Open now", count: 58, active: true },
|
|
418
|
+
{ key: "price", label: "Mid-range", count: 96 },
|
|
419
|
+
{ key: "terrace", label: "Terrace", count: 31 },
|
|
420
|
+
{ key: "veg", label: "Has a vegetarian dish", count: 84 },
|
|
421
|
+
{ key: "late", label: "Serving past midnight", count: 4 },
|
|
422
|
+
{ key: "centru", label: "Centru", count: 90 }
|
|
423
|
+
];
|
|
424
|
+
var mockLoosenOptions = [
|
|
425
|
+
{ key: "open", label: "Drop \u201COpen now\u201D", gainLabel: "+8 kitchens" },
|
|
426
|
+
{ key: "price", label: "Drop \u201CMid-range\u201D", gainLabel: "+7 kitchens" },
|
|
427
|
+
{ key: "veg", label: "Drop \u201CVegetarian\u201D", gainLabel: "+2 kitchens" }
|
|
428
|
+
];
|
|
429
|
+
var mockIndexEntries = [
|
|
430
|
+
["Breakfast & brunch", 314],
|
|
431
|
+
["Romanian", 299],
|
|
432
|
+
["Bakery", 250],
|
|
433
|
+
["Italian", 247],
|
|
434
|
+
["Pizza", 245],
|
|
435
|
+
["Fast food", 229],
|
|
436
|
+
["Vegetarian", 172],
|
|
437
|
+
["American", 153],
|
|
438
|
+
["Vegan", 108],
|
|
439
|
+
["Mediterranean", 78],
|
|
440
|
+
["Seafood", 77],
|
|
441
|
+
["Eastern European", 67],
|
|
442
|
+
["Fusion", 61],
|
|
443
|
+
["Middle Eastern", 58],
|
|
444
|
+
["BBQ", 57],
|
|
445
|
+
["Deli", 51],
|
|
446
|
+
["Steakhouse", 49],
|
|
447
|
+
["Japanese", 34],
|
|
448
|
+
["French", 26],
|
|
449
|
+
["Sushi", 23],
|
|
450
|
+
["Turkish", 22],
|
|
451
|
+
["Chinese", 14],
|
|
452
|
+
["Greek", 12],
|
|
453
|
+
["Indian", 11],
|
|
454
|
+
["Thai", 7],
|
|
455
|
+
["German", 5],
|
|
456
|
+
["Lebanese", 3],
|
|
457
|
+
["Portuguese", 1]
|
|
458
|
+
].map(([name, count], index) => ({
|
|
459
|
+
slug: String(name).toLowerCase().replace(/[^a-z]+/g, "-"),
|
|
460
|
+
name: String(name),
|
|
461
|
+
count: Number(count),
|
|
462
|
+
href: `/menu/cuisines/${String(name).toLowerCase().replace(/[^a-z]+/g, "-")}`,
|
|
463
|
+
gradient: Object.values(mockGradients)[index % 5]
|
|
464
|
+
}));
|
|
465
|
+
var mockFamilyBlock = {
|
|
466
|
+
slug: "italian",
|
|
467
|
+
name: "Italian",
|
|
468
|
+
venueCount: 247,
|
|
469
|
+
chips: [
|
|
470
|
+
{ slug: "pizza", label: "Pizza", count: 120 },
|
|
471
|
+
{ slug: "pasta", label: "Pasta", count: 96 },
|
|
472
|
+
{ slug: "salad", label: "Salad", count: 88 }
|
|
473
|
+
],
|
|
474
|
+
priceBandLabel: "22\u201386 lei",
|
|
475
|
+
photoUrls: [],
|
|
476
|
+
// Three, as the cuisines index sends, and stripped the same way: no dish and
|
|
477
|
+
// no price. The dish column is ~110px inside this card, which truncates the
|
|
478
|
+
// name and wraps its kicker to three lines; the price is already the band
|
|
479
|
+
// above.
|
|
480
|
+
venues: mockKitchens.slice(0, 3).map((kitchen) => __spreadProps(__spreadValues({}, kitchen), { dish: void 0, priceLabel: void 0 })),
|
|
481
|
+
href: "/menu/cuisines/italian"
|
|
482
|
+
};
|
|
483
|
+
var mockBareFamilyBlock = {
|
|
484
|
+
slug: "portuguese",
|
|
485
|
+
name: "Portuguese",
|
|
486
|
+
venueCount: 1,
|
|
487
|
+
href: "/menu/cuisines/portuguese"
|
|
488
|
+
};
|
|
489
|
+
var mockQa = [
|
|
490
|
+
{
|
|
491
|
+
question: "Which Italian kitchen is best in Cluj?",
|
|
492
|
+
answer: "Salt & Stone is named most often \u2014 seventeen reviews single out the truffle tagliatelle. The Copper Yard is second on volume and about eight lei cheaper across the menu."
|
|
493
|
+
},
|
|
494
|
+
{
|
|
495
|
+
question: "What does an Italian main cost?",
|
|
496
|
+
answer: "Pasta runs 38 to 84 lei and pizza 30 to 68 across these kitchens. Ninety-six of them sit in the mid-range price band."
|
|
497
|
+
},
|
|
498
|
+
{
|
|
499
|
+
question: "Anywhere open late?",
|
|
500
|
+
answer: "Four keep the kitchen going past midnight \u2014 The Copper Yard until 00:00 is the latest of the Italian rooms."
|
|
501
|
+
}
|
|
502
|
+
];
|
|
378
503
|
|
|
379
|
-
export { mockConcept, mockDrinkFamily, mockFamily, mockFormatters, mockGradients, mockHubLabels, mockIntentTiles, mockItems, mockPlaces, mockVenues };
|
|
504
|
+
export { mockBareFamilyBlock, mockConcept, mockDrinkFamily, mockFamily, mockFamilyBlock, mockFilterChips, mockFormatters, mockGradients, mockHubLabels, mockIndexEntries, mockIntentFigures, mockIntentTiles, mockItems, mockKitchens, mockLoosenOptions, mockMatchedKitchens, mockPlaces, mockQa, mockVenues };
|
|
@@ -148,10 +148,8 @@ type MenuFamilyPreview = {
|
|
|
148
148
|
price_low?: number;
|
|
149
149
|
price_high?: number;
|
|
150
150
|
price_median?: number;
|
|
151
|
-
/** Pre-resolved venue photos for the
|
|
151
|
+
/** Pre-resolved venue photos for the mosaic. Venue photography only. */
|
|
152
152
|
venue_photo_urls?: string[];
|
|
153
|
-
/** The item this family is most named for. */
|
|
154
|
-
signature?: MenuFamilySignature;
|
|
155
153
|
};
|
|
156
154
|
type MenuFamilyChip = {
|
|
157
155
|
slug: string;
|
|
@@ -159,8 +157,6 @@ type MenuFamilyChip = {
|
|
|
159
157
|
/** Venue count. Absent where no facet backs the chip. */
|
|
160
158
|
count?: number;
|
|
161
159
|
};
|
|
162
|
-
/** A family card's featured item. Same shape as a venue's, for the same reason. */
|
|
163
|
-
type MenuFamilySignature = MenuVenueSignature;
|
|
164
160
|
/**
|
|
165
161
|
* A venue as the hub's venue-scoped surfaces consume it: the place itself plus
|
|
166
162
|
* the pre-resolved bits a hub row needs and `PlacePreview` does not carry —
|
|
@@ -220,6 +216,186 @@ type MenuIntentTile = {
|
|
|
220
216
|
/** Icon node, resolved by the caller — the DS does not pick icons for it. */
|
|
221
217
|
icon?: ReactNode;
|
|
222
218
|
};
|
|
219
|
+
/**
|
|
220
|
+
* One figure in the intent hero — a number the reader can tap to filter by.
|
|
221
|
+
*
|
|
222
|
+
* The value arrives pre-formatted because the figures are not all the same
|
|
223
|
+
* quantity: "247" is a count and "58" is a count of a subset, but a caller may
|
|
224
|
+
* also want "4 past midnight", and a component that formatted numbers itself
|
|
225
|
+
* would need the locale it is forbidden to own.
|
|
226
|
+
*/
|
|
227
|
+
type MenuIntentFigure = {
|
|
228
|
+
key: string;
|
|
229
|
+
/** Pre-formatted, e.g. "247". */
|
|
230
|
+
value: string;
|
|
231
|
+
/** The unit, e.g. "kitchens" or "make a pizza". */
|
|
232
|
+
label: string;
|
|
233
|
+
/**
|
|
234
|
+
* The state this figure applies, as a URL.
|
|
235
|
+
*
|
|
236
|
+
* An `href` rather than only a callback because the filter state lives in the
|
|
237
|
+
* query string: a link is server-rendered, crawlable, middle-clickable and
|
|
238
|
+
* survives with no JavaScript, and `?open=now` is an indexable state we
|
|
239
|
+
* actively want followed. `onSelect` remains for consumers holding the state
|
|
240
|
+
* in memory; `href` wins when both are set.
|
|
241
|
+
*/
|
|
242
|
+
href?: string;
|
|
243
|
+
/**
|
|
244
|
+
* Applies this figure as a filter without navigating. A figure with neither
|
|
245
|
+
* `href` nor `onSelect` renders as a static fact rather than as a control
|
|
246
|
+
* that does nothing — the total is usually one, since filtering to "all of
|
|
247
|
+
* them" is not a state.
|
|
248
|
+
*/
|
|
249
|
+
onSelect?: () => void;
|
|
250
|
+
/** The filter this figure applies is already on. */
|
|
251
|
+
active?: boolean;
|
|
252
|
+
};
|
|
253
|
+
/**
|
|
254
|
+
* A filter chip on the intent page.
|
|
255
|
+
*
|
|
256
|
+
* Every chip counts KITCHENS, whatever facet backs it — a dietary chip reads
|
|
257
|
+
* "has a vegetarian dish" and counts venues, not dishes. Mixing venue counts
|
|
258
|
+
* and item counts in one row is what made the earlier version unreadable, so
|
|
259
|
+
* the type carries no unit: there is only ever one.
|
|
260
|
+
*/
|
|
261
|
+
type MenuFilterChip = {
|
|
262
|
+
key: string;
|
|
263
|
+
label: string;
|
|
264
|
+
/** Kitchens this chip would leave. Absent where no facet backs it. */
|
|
265
|
+
count?: number;
|
|
266
|
+
active?: boolean;
|
|
267
|
+
/**
|
|
268
|
+
* Zero kitchens under the current filters — dimmed, but STILL TAPPABLE.
|
|
269
|
+
*
|
|
270
|
+
* Tapping swaps it in and drops whichever active filter conflicts, with a
|
|
271
|
+
* line saying which. A disabled chip would leave the reader guessing why the
|
|
272
|
+
* thing they want is unavailable; this one answers it by being pressed.
|
|
273
|
+
*/
|
|
274
|
+
empty?: boolean;
|
|
275
|
+
/** Icon node, resolved by the caller. */
|
|
276
|
+
icon?: ReactNode;
|
|
277
|
+
};
|
|
278
|
+
/**
|
|
279
|
+
* A venue row on the intent page — the page's spine.
|
|
280
|
+
*
|
|
281
|
+
* Distinct from `MenuVenuePreview` (the hub's ranked row) in one structural
|
|
282
|
+
* way that is the whole point of the surface: the dish carries its own `href`,
|
|
283
|
+
* so the row has TWO targets. The row goes to the venue; the dish name opens
|
|
284
|
+
* the item drawer. `RankedVenueList` wraps its entire row in one link and
|
|
285
|
+
* therefore cannot express this, which is why this is a separate shape rather
|
|
286
|
+
* than a flag on that one.
|
|
287
|
+
*/
|
|
288
|
+
type MenuKitchenPreview = {
|
|
289
|
+
place: PlacePreview;
|
|
290
|
+
/** The venue page. The row's primary target. */
|
|
291
|
+
href: string;
|
|
292
|
+
/** Venue type and district, composed and localised by the caller, e.g. "pizzeria · Centru". */
|
|
293
|
+
metaLabel?: string;
|
|
294
|
+
/**
|
|
295
|
+
* "kitchen until 23:00", or "closed · opens 12:00". Composed by the caller
|
|
296
|
+
* because the wording differs by scope — a bar is not a kitchen.
|
|
297
|
+
*/
|
|
298
|
+
hoursLabel?: string;
|
|
299
|
+
dish?: MenuKitchenDish;
|
|
300
|
+
/** Pre-formatted, e.g. "62 lei". */
|
|
301
|
+
priceLabel?: string;
|
|
302
|
+
};
|
|
303
|
+
/** The dish a kitchen row leads with. The row's second target. */
|
|
304
|
+
type MenuKitchenDish = {
|
|
305
|
+
name: string;
|
|
306
|
+
/**
|
|
307
|
+
* Opens the item drawer. Omit for an inert dish line — a must-try the
|
|
308
|
+
* reviews named that has no live menu row behind it.
|
|
309
|
+
*/
|
|
310
|
+
href?: string;
|
|
311
|
+
/** Kicker over the name: "MOST NAMED HERE", or "WHY IT MATCHES" when filtered. */
|
|
312
|
+
kicker: string;
|
|
313
|
+
/** Under the name: "17 reviews name it", or "mid-range · 300g" when matched. */
|
|
314
|
+
meta?: string;
|
|
315
|
+
/**
|
|
316
|
+
* This row is answering a filter rather than volunteering its most-named
|
|
317
|
+
* dish. Draws the match mark and tints the block, so a reader scanning a
|
|
318
|
+
* filtered list can see that every row changed its reason for being there.
|
|
319
|
+
*/
|
|
320
|
+
matched?: boolean;
|
|
321
|
+
/** The filter it matched, e.g. "vegetarian". */
|
|
322
|
+
matchLabel?: string;
|
|
323
|
+
};
|
|
324
|
+
/**
|
|
325
|
+
* One row in a browse index — the complete set, at a glance.
|
|
326
|
+
*
|
|
327
|
+
* The index is the page's whole SEO job: it carries a crawlable link to every
|
|
328
|
+
* destination the city has, which is what frees the richer blocks below it to
|
|
329
|
+
* be progressive. So it holds nothing but what a reader needs to choose — a
|
|
330
|
+
* name, a count and a colour.
|
|
331
|
+
*/
|
|
332
|
+
type MenuIndexEntry = {
|
|
333
|
+
slug: string;
|
|
334
|
+
name: string;
|
|
335
|
+
count: number;
|
|
336
|
+
href: string;
|
|
337
|
+
/** Finished CSS gradient for the mark. Falls back to the border colour. */
|
|
338
|
+
gradient?: string;
|
|
339
|
+
};
|
|
340
|
+
/**
|
|
341
|
+
* A family on the cuisines index — the hub's card, opened out sideways.
|
|
342
|
+
*
|
|
343
|
+
* Deliberately thin. The reader here is choosing a cuisine, not a venue, and
|
|
344
|
+
* every field the block does NOT carry is a decision the next page owns.
|
|
345
|
+
*/
|
|
346
|
+
type MenuFamilyBlockPreview = {
|
|
347
|
+
slug: string;
|
|
348
|
+
name: string;
|
|
349
|
+
venueCount: number;
|
|
350
|
+
/** Facet chips. Labels only — the block never prints their counts. */
|
|
351
|
+
chips?: MenuFamilyChip[];
|
|
352
|
+
/** Composed price band, e.g. "22–86 lei". */
|
|
353
|
+
priceBandLabel?: string;
|
|
354
|
+
/** Venue photography only — item photography is AI-generated and never shown. */
|
|
355
|
+
photoUrls?: string[];
|
|
356
|
+
/**
|
|
357
|
+
* Venues, as kitchen rows. The block draws every one it is given — the caller
|
|
358
|
+
* decides how many, because how much of a claim the block makes is the page's
|
|
359
|
+
* decision, not the component's.
|
|
360
|
+
*
|
|
361
|
+
* The same `MenuKitchenPreview` the intent page's list takes, rendered by the
|
|
362
|
+
* same component — a family block that grew its own venue row would be the
|
|
363
|
+
* fourth near-identical row in the package, and they drift the moment one of
|
|
364
|
+
* them gets a fix.
|
|
365
|
+
*/
|
|
366
|
+
venues?: MenuKitchenPreview[];
|
|
367
|
+
/** The family's own page. */
|
|
368
|
+
href: string;
|
|
369
|
+
};
|
|
370
|
+
/**
|
|
371
|
+
* One "loosen this" row on a dead end — a filter, and what dropping it returns.
|
|
372
|
+
*
|
|
373
|
+
* The count is the point: "drop Open now" is a guess, "+8 kitchens" is an
|
|
374
|
+
* answer, and a dead end that cannot say which filter costs the most is just
|
|
375
|
+
* an empty state with extra steps.
|
|
376
|
+
*/
|
|
377
|
+
type MenuLoosenOption = {
|
|
378
|
+
key: string;
|
|
379
|
+
/** e.g. 'Drop "Open now"'. */
|
|
380
|
+
label: string;
|
|
381
|
+
/** e.g. "+8 kitchens". Pre-formatted. */
|
|
382
|
+
gainLabel: string;
|
|
383
|
+
/**
|
|
384
|
+
* The state without this filter, as a URL.
|
|
385
|
+
*
|
|
386
|
+
* An `href` because dropping a filter IS a navigation wherever the filter set
|
|
387
|
+
* lives in the query string — which makes it server-renderable, shareable and
|
|
388
|
+
* undoable with the back button. `onSelect` remains for consumers holding the
|
|
389
|
+
* state in memory; `href` wins when both are set.
|
|
390
|
+
*/
|
|
391
|
+
href?: string;
|
|
392
|
+
onSelect?: () => void;
|
|
393
|
+
};
|
|
394
|
+
/** One question and its answer. Static prose — never an accordion; see `<QaBlock>`. */
|
|
395
|
+
type QaItem = {
|
|
396
|
+
question: string;
|
|
397
|
+
answer: string;
|
|
398
|
+
};
|
|
223
399
|
/** Formatters injected into every menu surface. The DS owns no `Intl` locale. */
|
|
224
400
|
type MenuFormatters = {
|
|
225
401
|
/** 24 → "24 lei". */
|
|
@@ -242,4 +418,4 @@ type MenuHubLabels = {
|
|
|
242
418
|
until: string;
|
|
243
419
|
};
|
|
244
420
|
|
|
245
|
-
export { type MenuConceptPreview, type MenuConceptRow, type MenuFamilyChip, type MenuFamilyPreview, type
|
|
421
|
+
export { type MenuConceptPreview, type MenuConceptRow, type MenuFamilyBlockPreview, type MenuFamilyChip, type MenuFamilyPreview, type MenuFilterChip, type MenuFormatters, type MenuHubLabels, type MenuIndexEntry, type MenuIntentFigure, type MenuIntentTile, type MenuItemPlaceRef, type MenuItemPreview, type MenuKitchenDish, type MenuKitchenPreview, type MenuLinkComponent, type MenuLinkProps, type MenuLoosenOption, MenuSizeAndPrice, type MenuVenuePreview, type MenuVenueSignature, type QaItem };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { MotionBookmark, MotionCheck, MotionChevron, MotionCopy, MotionEye, MotionHeart, MotionPlayPause, MotionRefresh, MotionSearch, MotionShare, MotionSliders, MotionSort, MotionStar, MotionTheme } from '../chunk-
|
|
2
|
-
import '../chunk-
|
|
1
|
+
export { MotionBookmark, MotionCheck, MotionChevron, MotionCopy, MotionEye, MotionHeart, MotionPlayPause, MotionRefresh, MotionSearch, MotionShare, MotionSliders, MotionSort, MotionStar, MotionTheme } from '../chunk-YOKHBAIH.js';
|
|
2
|
+
import '../chunk-G7IFL4Z4.js';
|
|
3
3
|
import '../chunk-FEK5XGZW.js';
|
|
4
4
|
import '../chunk-3GWWZKX7.js';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { beats, MotionIcon, returnTransition } from '../chunk-
|
|
1
|
+
import { beats, pivot, MotionIcon, returnTransition } from '../chunk-G7IFL4Z4.js';
|
|
2
2
|
import '../chunk-FEK5XGZW.js';
|
|
3
3
|
import { __spreadProps, __spreadValues } from '../chunk-3GWWZKX7.js';
|
|
4
4
|
import * as React from 'react';
|
|
@@ -28,11 +28,11 @@ function MotionHouse(props) {
|
|
|
28
28
|
motion.path,
|
|
29
29
|
{
|
|
30
30
|
variants: houseRoof,
|
|
31
|
-
style:
|
|
31
|
+
style: pivot(12, 10),
|
|
32
32
|
d: "M3 10a2 2 0 0 1 .709-1.528l7-6a2 2 0 0 1 2.582 0l7 6A2 2 0 0 1 21 10"
|
|
33
33
|
}
|
|
34
34
|
),
|
|
35
|
-
/* @__PURE__ */ jsxs(motion.g, { variants: houseWalls, style:
|
|
35
|
+
/* @__PURE__ */ jsxs(motion.g, { variants: houseWalls, style: pivot(12, 21), children: [
|
|
36
36
|
/* @__PURE__ */ jsx("path", { d: "M21 10v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V10" }),
|
|
37
37
|
/* @__PURE__ */ jsx("path", { d: "M15 21v-8a1 1 0 0 0-1-1h-4a1 1 0 0 0-1 1v8" })
|
|
38
38
|
] })
|
|
@@ -60,7 +60,7 @@ var knife = {
|
|
|
60
60
|
transition: beats(UTENSILS_TIMES, 0.6)
|
|
61
61
|
}
|
|
62
62
|
};
|
|
63
|
-
var CROSSING =
|
|
63
|
+
var CROSSING = pivot(12, 12);
|
|
64
64
|
var HANDLE_CROSSED = "M2.1 21.8 8.5 15.4";
|
|
65
65
|
var HANDLE_CLEAR = "M2.1 21.8 12 12";
|
|
66
66
|
var forkHandle = {
|
|
@@ -108,13 +108,13 @@ var pinDot = {
|
|
|
108
108
|
}
|
|
109
109
|
};
|
|
110
110
|
function MotionMapPin(props) {
|
|
111
|
-
return /* @__PURE__ */ jsx(MotionIcon, __spreadProps(__spreadValues({}, props), { children: /* @__PURE__ */ jsxs(motion.g, { variants: pinBody, style:
|
|
111
|
+
return /* @__PURE__ */ jsx(MotionIcon, __spreadProps(__spreadValues({}, props), { children: /* @__PURE__ */ jsxs(motion.g, { variants: pinBody, style: pivot(12, 22), children: [
|
|
112
112
|
/* @__PURE__ */ jsx("path", { d: "M20 10c0 4.993-5.539 10.193-7.399 11.799a1 1 0 0 1-1.202 0C9.539 20.193 4 14.993 4 10a8 8 0 0 1 16 0" }),
|
|
113
113
|
/* @__PURE__ */ jsx(
|
|
114
114
|
motion.circle,
|
|
115
115
|
{
|
|
116
116
|
variants: pinDot,
|
|
117
|
-
style:
|
|
117
|
+
style: pivot(12, 10),
|
|
118
118
|
fill: "currentColor",
|
|
119
119
|
fillOpacity: 0,
|
|
120
120
|
cx: "12",
|
|
@@ -23,6 +23,27 @@ type MotionIconVariant = "rest" | "active" | "play";
|
|
|
23
23
|
declare function beats(times: number[], duration?: number): Transition;
|
|
24
24
|
/** Leaving is not a performance — it returns the glyph, firmly and fast. */
|
|
25
25
|
declare const returnTransition: Transition;
|
|
26
|
+
/**
|
|
27
|
+
* A moving part's pivot, in the icon's 24-unit VIEWBOX coordinates.
|
|
28
|
+
*
|
|
29
|
+
* Every part in this family wrote `style={{ originX: "12px", originY: "12px" }}`
|
|
30
|
+
* meaning "the centre of the icon" — and every one of them was pivoting
|
|
31
|
+
* somewhere else. Framer Motion sets `transform-box: fill-box` on any SVG
|
|
32
|
+
* element it transforms, which makes `transform-origin` relative to that
|
|
33
|
+
* element's OWN bounding box, not the viewBox. The chevron `m6 9 6 6 6-6` has
|
|
34
|
+
* its box at (6, 9), so "12px 12px" resolved to user-space (18, 21): a 180°
|
|
35
|
+
* flip around a point outside the arrow, which is why it visibly slid as it
|
|
36
|
+
* turned. A house's walls pivoting at its floor, a pin at its tip, a lens at
|
|
37
|
+
* its centre — all of them missed by the same offset for the same reason.
|
|
38
|
+
*
|
|
39
|
+
* `transform-box: view-box` is the single switch that makes px origins mean
|
|
40
|
+
* what they say. Declared once here so the next icon cannot get it wrong.
|
|
41
|
+
*/
|
|
42
|
+
declare function pivot(x: number, y: number): {
|
|
43
|
+
readonly originX: `${number}px`;
|
|
44
|
+
readonly originY: `${number}px`;
|
|
45
|
+
readonly transformBox: "view-box";
|
|
46
|
+
};
|
|
26
47
|
type PoseValues = Record<string, number>;
|
|
27
48
|
/**
|
|
28
49
|
* Builds the three variants for a moving part.
|
|
@@ -41,4 +62,4 @@ declare function MotionIcon({ active, replayToken, className, children, }: Motio
|
|
|
41
62
|
children: React.ReactNode;
|
|
42
63
|
}): react_jsx_runtime.JSX.Element;
|
|
43
64
|
|
|
44
|
-
export { MotionIcon, type MotionIconProps, type MotionIconVariant, beats, poses, returnTransition, useIconGesture };
|
|
65
|
+
export { MotionIcon, type MotionIconProps, type MotionIconVariant, beats, pivot, poses, returnTransition, useIconGesture };
|