@bearmenu/ui 0.8.7 → 0.8.9
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-637L7XQ3.js → chunk-3TNPSN4D.js} +28 -9
- package/dist/chunk-62ES6N32.js +27 -0
- package/dist/chunk-AHL45DD2.js +145 -0
- package/dist/chunk-AXV5O2IZ.js +58 -0
- package/dist/chunk-IYDH5IU6.js +66 -0
- package/dist/chunk-OPVKL4VS.js +18 -0
- package/dist/chunk-Q6QH52LJ.js +38 -0
- package/dist/chunk-TH6W2PWG.js +213 -0
- package/dist/chunk-THOVPZNN.js +37 -0
- package/dist/chunk-YS6BHKZO.js +92 -0
- package/dist/components/badge.d.ts +1 -1
- package/dist/components/button.d.ts +1 -1
- package/dist/components/category-deck.js +10 -1
- package/dist/components/command.d.ts +7 -7
- package/dist/components/concept-compare.d.ts +73 -0
- package/dist/components/concept-compare.js +162 -0
- package/dist/components/coverflow.d.ts +11 -3
- package/dist/components/coverflow.js +34 -51
- package/dist/components/date-input.d.ts +1 -1
- package/dist/components/drift-rail.d.ts +24 -10
- package/dist/components/drift-rail.js +1 -1
- package/dist/components/event-card.d.ts +8 -0
- package/dist/components/event-card.js +64 -34
- package/dist/components/event-compact-row.d.ts +33 -6
- package/dist/components/event-compact-row.js +120 -39
- package/dist/components/event-day-header.js +3 -1
- package/dist/components/event-day-separator.d.ts +7 -1
- package/dist/components/event-day-separator.js +3 -2
- package/dist/components/event-detail-panel.d.ts +171 -25
- package/dist/components/event-detail-panel.js +471 -174
- package/dist/components/event-hub-hero.d.ts +25 -4
- package/dist/components/event-hub-hero.js +89 -19
- package/dist/components/event-list-row-wide.js +1 -1
- package/dist/components/event-list-row.d.ts +19 -6
- package/dist/components/event-list-row.js +79 -35
- package/dist/components/hour-band.d.ts +38 -40
- package/dist/components/hour-band.js +55 -27
- package/dist/components/hour-group.js +1 -1
- package/dist/components/hub-surface.d.ts +27 -0
- package/dist/components/hub-surface.js +20 -0
- package/dist/components/menu-family-card.d.ts +59 -0
- package/dist/components/menu-family-card.js +121 -0
- package/dist/components/menu-field.d.ts +57 -0
- package/dist/components/menu-field.js +3 -0
- package/dist/components/menu-hub-hero.d.ts +65 -0
- package/dist/components/menu-hub-hero.js +103 -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-mocks.d.ts +55 -0
- package/dist/components/menu-mocks.js +379 -0
- package/dist/components/menu-types.d.ts +245 -0
- package/dist/components/menu-types.js +1 -0
- package/dist/components/motion-icon-set.js +15 -3
- package/dist/components/open-now-list.d.ts +40 -0
- package/dist/components/open-now-list.js +69 -0
- package/dist/components/place-types.d.ts +112 -1
- package/dist/components/poster-tape.js +1 -1
- package/dist/components/poster-wall.d.ts +3 -3
- package/dist/components/poster-wall.js +73 -17
- package/dist/components/ranked-venue-list.d.ts +49 -0
- package/dist/components/ranked-venue-list.js +87 -0
- package/dist/components/section-header.d.ts +16 -1
- package/dist/components/section-header.js +26 -7
- package/dist/components/signature-chip.d.ts +37 -0
- package/dist/components/signature-chip.js +4 -0
- package/dist/components/signature-line-list.d.ts +45 -0
- package/dist/components/signature-line-list.js +99 -0
- package/dist/components/snap-rail.d.ts +54 -0
- package/dist/components/snap-rail.js +109 -0
- package/dist/components/spinner.d.ts +2 -2
- package/dist/components/stat-banner.d.ts +47 -0
- package/dist/components/stat-banner.js +61 -0
- package/dist/components/text.d.ts +3 -3
- package/dist/components/timeline-band.d.ts +20 -2
- package/dist/components/timeline-band.js +1 -1
- package/dist/components/timeline-playhead.d.ts +26 -0
- package/dist/components/timeline-playhead.js +25 -0
- package/dist/components/venue-busyness-strip.d.ts +35 -0
- package/dist/components/venue-busyness-strip.js +37 -0
- package/dist/components/venue-grade-dimensions.d.ts +36 -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-signature-block.d.ts +39 -0
- package/dist/components/venue-signature-block.js +6 -0
- package/dist/components/venue-spotlight.d.ts +74 -0
- package/dist/components/venue-spotlight.js +138 -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.d.ts +5 -4
- package/dist/components/weekend-panel.js +85 -71
- package/dist/hooks/use-drag-swipe.d.ts +44 -0
- package/dist/hooks/use-drag-swipe.js +2 -0
- package/dist/hooks/use-media-query.d.ts +17 -0
- package/dist/hooks/use-media-query.js +2 -0
- package/package.json +1 -1
- package/src/components/category-deck.tsx +19 -8
- package/src/components/colour-dictionary.stories.tsx +227 -0
- package/src/components/concept-compare.stories.tsx +104 -0
- package/src/components/concept-compare.tsx +276 -0
- package/src/components/coverflow.test.tsx +28 -1
- package/src/components/coverflow.tsx +37 -43
- package/src/components/drift-rail.tsx +167 -42
- package/src/components/event-card.tsx +55 -10
- package/src/components/event-compact-row.stories.tsx +30 -0
- package/src/components/event-compact-row.test.tsx +49 -0
- package/src/components/event-compact-row.tsx +162 -38
- package/src/components/event-day-header.tsx +3 -1
- package/src/components/event-day-separator.tsx +8 -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/event-hub-hero.stories.tsx +30 -0
- package/src/components/event-hub-hero.tsx +102 -30
- package/src/components/event-list-row.stories.tsx +34 -0
- package/src/components/event-list-row.test.tsx +53 -0
- package/src/components/event-list-row.tsx +135 -87
- package/src/components/hour-band.stories.tsx +28 -5
- package/src/components/hour-band.tsx +90 -70
- package/src/components/hub-surface.stories.tsx +68 -0
- package/src/components/hub-surface.tsx +40 -0
- package/src/components/menu-family-card.stories.tsx +112 -0
- package/src/components/menu-family-card.tsx +211 -0
- package/src/components/menu-field.stories.tsx +74 -0
- package/src/components/menu-field.tsx +199 -0
- package/src/components/menu-hub-hero.stories.tsx +92 -0
- package/src/components/menu-hub-hero.tsx +187 -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-mocks.ts +417 -0
- package/src/components/menu-types.ts +259 -0
- package/src/components/motion-icon-set.tsx +30 -3
- package/src/components/open-now-list.stories.tsx +76 -0
- package/src/components/open-now-list.tsx +127 -0
- package/src/components/place-types.ts +112 -0
- package/src/components/poster-wall.test.tsx +28 -0
- package/src/components/poster-wall.tsx +96 -23
- package/src/components/ranked-venue-list.stories.tsx +80 -0
- package/src/components/ranked-venue-list.tsx +177 -0
- package/src/components/section-header.stories.tsx +24 -0
- package/src/components/section-header.test.tsx +28 -0
- package/src/components/section-header.tsx +45 -7
- package/src/components/signature-chip.tsx +77 -0
- package/src/components/signature-line-list.stories.tsx +85 -0
- package/src/components/signature-line-list.tsx +136 -0
- package/src/components/snap-rail.stories.tsx +79 -0
- package/src/components/snap-rail.test.tsx +18 -0
- package/src/components/snap-rail.tsx +148 -0
- package/src/components/stat-banner.stories.tsx +100 -0
- package/src/components/stat-banner.tsx +117 -0
- package/src/components/timeline-band.tsx +52 -12
- package/src/components/timeline-playhead.stories.tsx +30 -0
- package/src/components/timeline-playhead.test.tsx +11 -0
- package/src/components/timeline-playhead.tsx +46 -0
- package/src/components/venue-busyness-strip.stories.tsx +66 -0
- package/src/components/venue-busyness-strip.tsx +64 -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-signature-block.stories.tsx +68 -0
- package/src/components/venue-signature-block.tsx +71 -0
- package/src/components/venue-spotlight.stories.tsx +141 -0
- package/src/components/venue-spotlight.tsx +236 -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/src/components/weekend-panel.test.tsx +43 -0
- package/src/components/weekend-panel.tsx +142 -108
- package/src/hooks/use-drag-swipe.test.tsx +66 -0
- package/src/hooks/use-drag-swipe.ts +105 -0
- package/src/hooks/use-media-query.ts +31 -0
- package/dist/chunk-GPH6L4DX.js +0 -55
|
@@ -25,24 +25,60 @@ function useInViewport() {
|
|
|
25
25
|
React.useEffect(() => {
|
|
26
26
|
const node = ref.current;
|
|
27
27
|
if (!node || typeof IntersectionObserver === "undefined") return;
|
|
28
|
-
const observer = new IntersectionObserver(
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
const observer = new IntersectionObserver(
|
|
29
|
+
([entry]) => setInView(entry.isIntersecting),
|
|
30
|
+
{
|
|
31
|
+
threshold: 0
|
|
32
|
+
}
|
|
33
|
+
);
|
|
31
34
|
observer.observe(node);
|
|
32
35
|
return () => observer.disconnect();
|
|
33
36
|
}, []);
|
|
34
37
|
return [ref, inView];
|
|
35
38
|
}
|
|
36
|
-
function PosterWall({
|
|
39
|
+
function PosterWall({
|
|
40
|
+
tiles,
|
|
41
|
+
variant = "default",
|
|
42
|
+
children,
|
|
43
|
+
className
|
|
44
|
+
}) {
|
|
37
45
|
const isCompact = variant === "compact";
|
|
38
|
-
const
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
46
|
+
const maxCols = isCompact ? 6 : 12;
|
|
47
|
+
const maxSlots = isCompact ? POSTER_WALL_COMPACT_TILE_COUNT : POSTER_WALL_DESKTOP_TILE_COUNT;
|
|
48
|
+
const heightPx = isCompact ? 330 : 420;
|
|
49
|
+
const pictures = React.useMemo(
|
|
50
|
+
() => (tiles != null ? tiles : []).filter((tile) => !!tile.src),
|
|
51
|
+
[tiles]
|
|
52
|
+
);
|
|
43
53
|
const prefersReducedMotion = usePrefersReducedMotion();
|
|
44
54
|
const [wallRef, inView] = useInViewport();
|
|
45
55
|
const isSweepPaused = prefersReducedMotion || !inView;
|
|
56
|
+
const [width, setWidth] = React.useState(isCompact ? 390 : 1200);
|
|
57
|
+
React.useEffect(() => {
|
|
58
|
+
const node = wallRef.current;
|
|
59
|
+
if (!node || typeof ResizeObserver === "undefined") return;
|
|
60
|
+
const observer = new ResizeObserver(
|
|
61
|
+
([entry]) => setWidth(entry.contentRect.width)
|
|
62
|
+
);
|
|
63
|
+
observer.observe(node);
|
|
64
|
+
return () => observer.disconnect();
|
|
65
|
+
}, [wallRef]);
|
|
66
|
+
let cols = maxCols;
|
|
67
|
+
let slots;
|
|
68
|
+
if (pictures.length === 0) {
|
|
69
|
+
slots = Array.from({ length: maxSlots }, () => ({}));
|
|
70
|
+
} else {
|
|
71
|
+
const shown = Math.min(pictures.length, maxSlots);
|
|
72
|
+
cols = Math.min(
|
|
73
|
+
maxCols,
|
|
74
|
+
Math.max(2, Math.round(Math.sqrt(shown * width / heightPx)))
|
|
75
|
+
);
|
|
76
|
+
const rows = Math.max(1, Math.ceil(shown / cols));
|
|
77
|
+
slots = Array.from(
|
|
78
|
+
{ length: cols * rows },
|
|
79
|
+
(_, index) => pictures[index % pictures.length]
|
|
80
|
+
);
|
|
81
|
+
}
|
|
46
82
|
return /* @__PURE__ */ jsxs(
|
|
47
83
|
"div",
|
|
48
84
|
{
|
|
@@ -62,10 +98,8 @@ function PosterWall({ tiles, variant = "default", children, className }) {
|
|
|
62
98
|
/* @__PURE__ */ jsx(
|
|
63
99
|
"div",
|
|
64
100
|
{
|
|
65
|
-
className:
|
|
66
|
-
|
|
67
|
-
isCompact ? "grid-cols-6" : "grid-cols-12"
|
|
68
|
-
),
|
|
101
|
+
className: "absolute inset-0 grid auto-rows-fr gap-[2px] opacity-50",
|
|
102
|
+
style: { gridTemplateColumns: `repeat(${cols}, minmax(0, 1fr))` },
|
|
69
103
|
"aria-hidden": true,
|
|
70
104
|
children: slots.map(
|
|
71
105
|
(tile, index) => tile.src ? (
|
|
@@ -130,7 +164,10 @@ function PosterWall({ tiles, variant = "default", children, className }) {
|
|
|
130
164
|
}
|
|
131
165
|
);
|
|
132
166
|
}
|
|
133
|
-
function PosterWallSkeleton({
|
|
167
|
+
function PosterWallSkeleton({
|
|
168
|
+
variant = "default",
|
|
169
|
+
className
|
|
170
|
+
}) {
|
|
134
171
|
const isCompact = variant === "compact";
|
|
135
172
|
const tileCount = isCompact ? POSTER_WALL_COMPACT_TILE_COUNT : POSTER_WALL_DESKTOP_TILE_COUNT;
|
|
136
173
|
return /* @__PURE__ */ jsxs(
|
|
@@ -150,7 +187,13 @@ function PosterWallSkeleton({ variant = "default", className }) {
|
|
|
150
187
|
"absolute inset-0 grid auto-rows-fr gap-[2px] opacity-50",
|
|
151
188
|
isCompact ? "grid-cols-6" : "grid-cols-12"
|
|
152
189
|
),
|
|
153
|
-
children: Array.from({ length: tileCount }, (_, index) => /* @__PURE__ */ jsx(
|
|
190
|
+
children: Array.from({ length: tileCount }, (_, index) => /* @__PURE__ */ jsx(
|
|
191
|
+
Skeleton,
|
|
192
|
+
{
|
|
193
|
+
className: "h-full w-full rounded-none bg-white/10"
|
|
194
|
+
},
|
|
195
|
+
index
|
|
196
|
+
))
|
|
154
197
|
}
|
|
155
198
|
),
|
|
156
199
|
/* @__PURE__ */ jsx(
|
|
@@ -170,8 +213,21 @@ function PosterWallSkeleton({ variant = "default", className }) {
|
|
|
170
213
|
isCompact ? "justify-end gap-[11px] p-5" : "justify-center gap-[15px] px-12"
|
|
171
214
|
),
|
|
172
215
|
children: [
|
|
173
|
-
/* @__PURE__ */ jsx(
|
|
174
|
-
|
|
216
|
+
/* @__PURE__ */ jsx(
|
|
217
|
+
Skeleton,
|
|
218
|
+
{
|
|
219
|
+
className: cn(
|
|
220
|
+
"bg-white/15",
|
|
221
|
+
isCompact ? "h-[11px] w-40" : "h-3 w-52"
|
|
222
|
+
)
|
|
223
|
+
}
|
|
224
|
+
),
|
|
225
|
+
/* @__PURE__ */ jsx(
|
|
226
|
+
Skeleton,
|
|
227
|
+
{
|
|
228
|
+
className: cn("bg-white/20", isCompact ? "h-7 w-64" : "h-10 w-96")
|
|
229
|
+
}
|
|
230
|
+
)
|
|
175
231
|
]
|
|
176
232
|
}
|
|
177
233
|
)
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { MenuVenuePreview, MenuLinkComponent, MenuLinkProps } from './menu-types.js';
|
|
3
|
+
import 'react';
|
|
4
|
+
import './place-types.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* RankedVenueList — venues ordered by how often reviews name their dishes.
|
|
8
|
+
*
|
|
9
|
+
* The answer to "best Italian in Cluj", and the reason it is defensible is the
|
|
10
|
+
* mention count sitting on every row: the order is evidence, not opinion. A
|
|
11
|
+
* caller that cannot supply mentions should use a different surface rather than
|
|
12
|
+
* this one with the column blank — an unexplained ranking is worse than none.
|
|
13
|
+
*
|
|
14
|
+
* The tab strip re-ranks in place and never navigates, so `activeTab` and
|
|
15
|
+
* `onTabChange` are controlled by the caller. Omit `tabs` entirely and the
|
|
16
|
+
* header is just a heading.
|
|
17
|
+
*
|
|
18
|
+
* No directive: without `tabs` the list renders on the server, which is the
|
|
19
|
+
* common case on a hub.
|
|
20
|
+
*/
|
|
21
|
+
/** @deprecated Alias of `MenuLinkProps`; kept so existing imports keep working. */
|
|
22
|
+
type RankedVenueListLinkProps = MenuLinkProps;
|
|
23
|
+
type RankedVenueListTab = {
|
|
24
|
+
slug: string;
|
|
25
|
+
label: string;
|
|
26
|
+
};
|
|
27
|
+
type RankedVenueListProps = {
|
|
28
|
+
venues: MenuVenuePreview[];
|
|
29
|
+
/** Section heading, e.g. "The kitchens Cluj rates for Italian". */
|
|
30
|
+
heading?: string;
|
|
31
|
+
/** The basis of the order, e.g. "by how often reviews name their dishes". */
|
|
32
|
+
caption?: string;
|
|
33
|
+
tabs?: RankedVenueListTab[];
|
|
34
|
+
activeTab?: string;
|
|
35
|
+
onTabChange?: (slug: string) => void;
|
|
36
|
+
/** Composed price band per venue, keyed by place slug, e.g. "38—84". */
|
|
37
|
+
priceBandLabels?: Record<string, string>;
|
|
38
|
+
/**
|
|
39
|
+
* The app's own grade per venue slug, already formatted ("8.7"). Shown in
|
|
40
|
+
* place of the star rating when present — the star is the third-party
|
|
41
|
+
* fallback for a venue the reviews have not graded yet.
|
|
42
|
+
*/
|
|
43
|
+
gradeLabels?: Record<string, string>;
|
|
44
|
+
linkComponent?: MenuLinkComponent;
|
|
45
|
+
className?: string;
|
|
46
|
+
};
|
|
47
|
+
declare function RankedVenueList({ venues, heading, caption, tabs, activeTab, onTabChange, priceBandLabels, gradeLabels, linkComponent: LinkComponent, className, }: RankedVenueListProps): react_jsx_runtime.JSX.Element;
|
|
48
|
+
|
|
49
|
+
export { RankedVenueList, type RankedVenueListLinkProps, type RankedVenueListProps, type RankedVenueListTab };
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { Separator } from '../chunk-WSRWFA6K.js';
|
|
2
|
+
import { cn } from '../chunk-FEK5XGZW.js';
|
|
3
|
+
import { __objRest, __spreadValues } from '../chunk-3GWWZKX7.js';
|
|
4
|
+
import { MessageSquare, Star, ChevronRight } from 'lucide-react';
|
|
5
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
6
|
+
|
|
7
|
+
function DefaultLink(_a) {
|
|
8
|
+
var _b = _a, { href } = _b, props = __objRest(_b, ["href"]);
|
|
9
|
+
return /* @__PURE__ */ jsx("a", __spreadValues({ href }, props));
|
|
10
|
+
}
|
|
11
|
+
function RankedVenueList({
|
|
12
|
+
venues,
|
|
13
|
+
heading,
|
|
14
|
+
caption,
|
|
15
|
+
tabs,
|
|
16
|
+
activeTab,
|
|
17
|
+
onTabChange,
|
|
18
|
+
priceBandLabels,
|
|
19
|
+
gradeLabels,
|
|
20
|
+
linkComponent: LinkComponent = DefaultLink,
|
|
21
|
+
className
|
|
22
|
+
}) {
|
|
23
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("overflow-hidden rounded-[14px] border border-border bg-card", className), children: [
|
|
24
|
+
(heading || tabs) && /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-x-4 gap-y-2 border-b border-border p-3.5", children: [
|
|
25
|
+
heading && /* @__PURE__ */ jsx("h3", { className: "text-[15px] font-bold leading-tight tracking-[-0.01em]", children: heading }),
|
|
26
|
+
caption && /* @__PURE__ */ jsx("span", { className: "text-[12.5px] text-muted-foreground", children: caption }),
|
|
27
|
+
tabs && tabs.length > 0 && /* @__PURE__ */ jsx("div", { className: "ml-auto flex flex-wrap gap-1.5", children: tabs.map((tab) => /* @__PURE__ */ jsx(
|
|
28
|
+
"button",
|
|
29
|
+
{
|
|
30
|
+
type: "button",
|
|
31
|
+
"aria-pressed": tab.slug === activeTab,
|
|
32
|
+
onClick: () => onTabChange == null ? void 0 : onTabChange(tab.slug),
|
|
33
|
+
className: cn(
|
|
34
|
+
"inline-flex h-7 items-center rounded-full border px-2.5 text-xs font-semibold transition-colors",
|
|
35
|
+
tab.slug === activeTab ? "border-accent bg-accent-subtle text-accent" : "border-border text-muted-foreground hover:bg-muted/50"
|
|
36
|
+
),
|
|
37
|
+
children: tab.label
|
|
38
|
+
},
|
|
39
|
+
tab.slug
|
|
40
|
+
)) })
|
|
41
|
+
] }),
|
|
42
|
+
/* @__PURE__ */ jsx("ol", { children: venues.map((venue, index) => {
|
|
43
|
+
var _a;
|
|
44
|
+
const { place, signature } = venue;
|
|
45
|
+
const mentions = (_a = signature == null ? void 0 : signature.item.mentions) != null ? _a : 0;
|
|
46
|
+
return /* @__PURE__ */ jsxs("li", { children: [
|
|
47
|
+
index > 0 && /* @__PURE__ */ jsx(Separator, {}),
|
|
48
|
+
/* @__PURE__ */ jsxs(
|
|
49
|
+
LinkComponent,
|
|
50
|
+
{
|
|
51
|
+
href: venue.href,
|
|
52
|
+
className: "flex items-center gap-3 p-3 transition-colors hover:bg-muted/40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring",
|
|
53
|
+
children: [
|
|
54
|
+
/* @__PURE__ */ jsx("span", { className: "w-4 shrink-0 text-center text-[13px] font-extrabold tabular-nums text-accent", children: index + 1 }),
|
|
55
|
+
/* @__PURE__ */ jsx("span", { className: "h-12 w-12 shrink-0 overflow-hidden rounded-xl bg-[repeating-linear-gradient(135deg,oklch(0.78_0.018_55)_0_8px,oklch(0.84_0.012_58)_8px_16px)]", children: place.thumbnail_url && // eslint-disable-next-line @next/next/no-img-element
|
|
56
|
+
/* @__PURE__ */ jsx(
|
|
57
|
+
"img",
|
|
58
|
+
{
|
|
59
|
+
src: place.thumbnail_url,
|
|
60
|
+
alt: "",
|
|
61
|
+
className: "h-full w-full object-cover",
|
|
62
|
+
loading: "lazy"
|
|
63
|
+
}
|
|
64
|
+
) }),
|
|
65
|
+
/* @__PURE__ */ jsxs("span", { className: "min-w-0 flex-1", children: [
|
|
66
|
+
/* @__PURE__ */ jsx("span", { className: "block text-sm font-bold leading-snug tracking-[-0.01em]", children: place.name }),
|
|
67
|
+
(signature == null ? void 0 : signature.item.name) && /* @__PURE__ */ jsx("span", { className: "mt-px block truncate font-serif text-xs italic text-muted-foreground", children: `\u201C${signature.item.name}\u201D` })
|
|
68
|
+
] }),
|
|
69
|
+
mentions > 0 && /* @__PURE__ */ jsxs("span", { className: "hidden shrink-0 items-center gap-1 text-[11.5px] tabular-nums text-muted-foreground sm:inline-flex", children: [
|
|
70
|
+
/* @__PURE__ */ jsx(MessageSquare, { className: "h-[11px] w-[11px]", "aria-hidden": true }),
|
|
71
|
+
mentions
|
|
72
|
+
] }),
|
|
73
|
+
(gradeLabels == null ? void 0 : gradeLabels[place.slug]) ? /* @__PURE__ */ jsx("span", { className: "inline-flex h-6 shrink-0 items-center rounded-md bg-foreground px-1.5 text-[12px] font-extrabold tabular-nums text-background", children: gradeLabels[place.slug] }) : place.rating && /* @__PURE__ */ jsxs("span", { className: "inline-flex shrink-0 items-center gap-1 text-[12.5px] font-semibold tabular-nums", children: [
|
|
74
|
+
/* @__PURE__ */ jsx(Star, { className: "h-3 w-3", "aria-hidden": true }),
|
|
75
|
+
place.rating.rating
|
|
76
|
+
] }),
|
|
77
|
+
(priceBandLabels == null ? void 0 : priceBandLabels[place.slug]) && /* @__PURE__ */ jsx("span", { className: "hidden shrink-0 text-[12.5px] tabular-nums text-muted-foreground sm:block", children: priceBandLabels[place.slug] }),
|
|
78
|
+
/* @__PURE__ */ jsx(ChevronRight, { className: "h-4 w-4 shrink-0 text-muted-foreground", "aria-hidden": true })
|
|
79
|
+
]
|
|
80
|
+
}
|
|
81
|
+
)
|
|
82
|
+
] }, place.slug);
|
|
83
|
+
}) })
|
|
84
|
+
] });
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export { RankedVenueList };
|
|
@@ -25,6 +25,21 @@ type SectionHeaderProps = {
|
|
|
25
25
|
/** Trailing action label, e.g. "See all". */
|
|
26
26
|
action?: string;
|
|
27
27
|
actionHref?: string;
|
|
28
|
+
/**
|
|
29
|
+
* `muted` (default): 14px in `--muted-foreground`, at every width. `app`:
|
|
30
|
+
* BELOW `lg`, 12.5px semibold in `--accent` — the way an app's "All ›" reads
|
|
31
|
+
* at the end of a card's title row, quieter in size and louder in colour so
|
|
32
|
+
* it sits in the count's tier rather than competing with the title. From
|
|
33
|
+
* `lg` it falls back to `muted`: the app treatment is a phone treatment, and
|
|
34
|
+
* the desktop page it shares a header with is unchanged by it.
|
|
35
|
+
*/
|
|
36
|
+
actionTone?: "muted" | "app";
|
|
37
|
+
/**
|
|
38
|
+
* `always` (default) shows the subtitle at every width. `lg` hides it below
|
|
39
|
+
* the `lg` breakpoint for surfaces that narrate on desktop and not on a
|
|
40
|
+
* phone, where a card title with a count is the whole header.
|
|
41
|
+
*/
|
|
42
|
+
subtitleFrom?: "always" | "lg";
|
|
28
43
|
/** Defaults to a plain `<a>`. Pass `next/link` in a Next app. */
|
|
29
44
|
linkComponent?: React.ComponentType<SectionHeaderLinkProps>;
|
|
30
45
|
onAction?: (mouseEvent: React.MouseEvent) => void;
|
|
@@ -37,7 +52,7 @@ type SectionHeaderProps = {
|
|
|
37
52
|
as?: "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "p" | "div";
|
|
38
53
|
className?: string;
|
|
39
54
|
};
|
|
40
|
-
declare function SectionHeader({ title, count, subtitle, action, actionHref, linkComponent: LinkComponent, onAction, as: Heading, className, }: SectionHeaderProps): react_jsx_runtime.JSX.Element;
|
|
55
|
+
declare function SectionHeader({ title, count, subtitle, action, actionHref, actionTone, subtitleFrom, linkComponent: LinkComponent, onAction, as: Heading, className, }: SectionHeaderProps): react_jsx_runtime.JSX.Element;
|
|
41
56
|
type SectionHeaderSkeletonProps = {
|
|
42
57
|
/** Shows the inline count placeholder, matching `SectionHeaderProps["count"]`. */
|
|
43
58
|
showCount?: boolean;
|
|
@@ -8,38 +8,57 @@ function DefaultLink(_a) {
|
|
|
8
8
|
var _b = _a, { href } = _b, props = __objRest(_b, ["href"]);
|
|
9
9
|
return /* @__PURE__ */ jsx("a", __spreadValues({ href }, props));
|
|
10
10
|
}
|
|
11
|
-
var
|
|
12
|
-
"inline-flex shrink-0 items-center gap-0.5 rounded-sm
|
|
13
|
-
"transition-colors duration-150
|
|
11
|
+
var ACTION_BASE_CLASSES = cn(
|
|
12
|
+
"inline-flex min-h-11 shrink-0 items-center gap-0.5 rounded-sm -my-2 py-2 -mr-1 pr-1",
|
|
13
|
+
"transition-colors duration-150 motion-reduce:transition-none",
|
|
14
14
|
"cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2"
|
|
15
15
|
);
|
|
16
|
+
var ACTION_TONE_CLASSES = {
|
|
17
|
+
muted: "text-[14px] text-muted-foreground hover:text-foreground",
|
|
18
|
+
app: cn(
|
|
19
|
+
"text-[12.5px] font-semibold text-accent hover:text-accent/80",
|
|
20
|
+
"lg:text-[14px] lg:font-normal lg:text-muted-foreground lg:hover:text-foreground"
|
|
21
|
+
)
|
|
22
|
+
};
|
|
16
23
|
function SectionHeader({
|
|
17
24
|
title,
|
|
18
25
|
count,
|
|
19
26
|
subtitle,
|
|
20
27
|
action,
|
|
21
28
|
actionHref,
|
|
29
|
+
actionTone = "muted",
|
|
30
|
+
subtitleFrom = "always",
|
|
22
31
|
linkComponent: LinkComponent = DefaultLink,
|
|
23
32
|
onAction,
|
|
24
33
|
as: Heading = "h2",
|
|
25
34
|
className
|
|
26
35
|
}) {
|
|
27
36
|
const actionIcon = /* @__PURE__ */ jsx(ChevronRight, { className: "h-3.5 w-3.5", "aria-hidden": true });
|
|
37
|
+
const actionClasses = cn(ACTION_BASE_CLASSES, ACTION_TONE_CLASSES[actionTone]);
|
|
28
38
|
return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col mb-3.5", className), children: [
|
|
29
39
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2", children: [
|
|
30
40
|
/* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-baseline", children: [
|
|
31
41
|
/* @__PURE__ */ jsx(Heading, { className: "truncate text-[17px] font-bold tracking-[-0.01em] text-foreground md:text-[18px]", children: title }),
|
|
32
|
-
count !== void 0 && /* @__PURE__ */ jsx("span", { className: "ml-
|
|
42
|
+
count !== void 0 && /* @__PURE__ */ jsx("span", { className: "ml-1.5 shrink-0 text-[12px] tabular-nums text-muted-foreground lg:ml-0.5", children: count })
|
|
33
43
|
] }),
|
|
34
|
-
action && (actionHref ? /* @__PURE__ */ jsxs(LinkComponent, { href: actionHref, className:
|
|
44
|
+
action && (actionHref ? /* @__PURE__ */ jsxs(LinkComponent, { href: actionHref, className: actionClasses, onClick: onAction, children: [
|
|
35
45
|
action,
|
|
36
46
|
actionIcon
|
|
37
|
-
] }) : onAction ? /* @__PURE__ */ jsxs("button", { type: "button", className:
|
|
47
|
+
] }) : onAction ? /* @__PURE__ */ jsxs("button", { type: "button", className: actionClasses, onClick: onAction, children: [
|
|
38
48
|
action,
|
|
39
49
|
actionIcon
|
|
40
50
|
] }) : null)
|
|
41
51
|
] }),
|
|
42
|
-
subtitle && /* @__PURE__ */ jsx(
|
|
52
|
+
subtitle && /* @__PURE__ */ jsx(
|
|
53
|
+
"p",
|
|
54
|
+
{
|
|
55
|
+
className: cn(
|
|
56
|
+
"mt-0.5 text-[13px] text-muted-foreground md:text-[14px]",
|
|
57
|
+
subtitleFrom === "lg" && "hidden lg:block"
|
|
58
|
+
),
|
|
59
|
+
children: subtitle
|
|
60
|
+
}
|
|
61
|
+
)
|
|
43
62
|
] });
|
|
44
63
|
}
|
|
45
64
|
function SectionHeaderSkeleton({
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* "Signature" / "Mentioned" — the marker for a menu item the review digest
|
|
5
|
+
* backs, shared by `<MenuItem>` and the dish/drink cards so the same claim
|
|
6
|
+
* never renders two different ways.
|
|
7
|
+
*
|
|
8
|
+
* When `onEvidence` is supplied the chip becomes a button that opens the
|
|
9
|
+
* reviews behind it, and takes a dashed outline to read as interactive. That is
|
|
10
|
+
* the whole differentiator: every competitor badges items ("Nou", "NEW"), none
|
|
11
|
+
* can show you why. Without evidence it degrades to a plain label rather than
|
|
12
|
+
* offering a tap that would reveal nothing.
|
|
13
|
+
*
|
|
14
|
+
* `stopPropagation` matters on every host: the row is a `role="button"` and the
|
|
15
|
+
* cards carry a stretched crawlable `<a>` overlay, so an un-stopped click would
|
|
16
|
+
* also open the item drawer. On the cards it must additionally out-rank that
|
|
17
|
+
* overlay's `z-[1]`, hence `relative z-[2]` from the caller.
|
|
18
|
+
*
|
|
19
|
+
* `surface="image"` is for card covers, where the chip sits over the photo
|
|
20
|
+
* rather than in the caption block. The accent-on-canvas palette has no
|
|
21
|
+
* contrast guarantee over an arbitrary photo, so it swaps to the same frosted
|
|
22
|
+
* fill the place badge uses there and keeps the dashed edge — which is what
|
|
23
|
+
* says "there is evidence behind this" — in the inverted colour.
|
|
24
|
+
*
|
|
25
|
+
* The label is passed in: the DS owns no translation keys.
|
|
26
|
+
*/
|
|
27
|
+
type SignatureChipProps = {
|
|
28
|
+
/** Already translated. `strong` → "Signature", `weak` → "Mentioned". */
|
|
29
|
+
label: string;
|
|
30
|
+
onEvidence?: () => void;
|
|
31
|
+
/** `image` = over a card cover; `canvas` = in a caption or row. */
|
|
32
|
+
surface?: "canvas" | "image";
|
|
33
|
+
className?: string;
|
|
34
|
+
};
|
|
35
|
+
declare function SignatureChip({ label, onEvidence, surface, className, }: SignatureChipProps): react_jsx_runtime.JSX.Element;
|
|
36
|
+
|
|
37
|
+
export { SignatureChip, type SignatureChipProps };
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { MenuItemLabels } from './menu-item.js';
|
|
3
|
+
import { MenuVenuePreview, MenuLinkComponent, MenuLinkProps } from './menu-types.js';
|
|
4
|
+
import 'react';
|
|
5
|
+
import './place-types.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* SignatureLineList — family, venue, dish, price and reputation on one line.
|
|
9
|
+
*
|
|
10
|
+
* The densest surface on the hub, and the one that most nearly justifies the
|
|
11
|
+
* whole catalogue: every row ties a kitchen to the single thing its reviewers
|
|
12
|
+
* keep naming, with the price and the count that back it.
|
|
13
|
+
*
|
|
14
|
+
* ## Two targets per row, deliberately
|
|
15
|
+
*
|
|
16
|
+
* The venue line goes to the venue; the item row goes to the item across every
|
|
17
|
+
* kitchen that makes it. Those are genuinely different destinations, and
|
|
18
|
+
* collapsing them into one tap loses the comparison that makes the item worth
|
|
19
|
+
* showing. They are separate anchors rather than one anchor with a nested
|
|
20
|
+
* button, which would be invalid markup.
|
|
21
|
+
*
|
|
22
|
+
* Below `lg` the row stacks: venue line first, then the full `<MenuItem>`
|
|
23
|
+
* underneath. The desktop layout puts them side by side. Same markup, one
|
|
24
|
+
* breakpoint — no second component.
|
|
25
|
+
*
|
|
26
|
+
* Server-safe.
|
|
27
|
+
*/
|
|
28
|
+
/** @deprecated Alias of `MenuLinkProps`; kept so existing imports keep working. */
|
|
29
|
+
type SignatureLineListLinkProps = MenuLinkProps;
|
|
30
|
+
type SignatureLineListProps = {
|
|
31
|
+
venues: MenuVenuePreview[];
|
|
32
|
+
/** Passed through to each `<MenuItem>`. */
|
|
33
|
+
labels: MenuItemLabels;
|
|
34
|
+
formatPrice: (amount: number) => string;
|
|
35
|
+
formatCount?: (value: number) => string;
|
|
36
|
+
/** Composed meta line per venue, keyed by place slug, e.g. "romanian · Centru". */
|
|
37
|
+
metaLabels?: Record<string, string>;
|
|
38
|
+
/** The row's category mark per venue, keyed by place slug. */
|
|
39
|
+
hues?: Record<string, string>;
|
|
40
|
+
linkComponent?: MenuLinkComponent;
|
|
41
|
+
className?: string;
|
|
42
|
+
};
|
|
43
|
+
declare function SignatureLineList({ venues, labels, formatPrice, formatCount, metaLabels, hues, linkComponent: LinkComponent, className, }: SignatureLineListProps): react_jsx_runtime.JSX.Element;
|
|
44
|
+
|
|
45
|
+
export { SignatureLineList, type SignatureLineListLinkProps, type SignatureLineListProps };
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { Separator } from '../chunk-WSRWFA6K.js';
|
|
2
|
+
import { MenuItem } from '../chunk-TH6W2PWG.js';
|
|
3
|
+
import '../chunk-Q6QH52LJ.js';
|
|
4
|
+
import '../chunk-UKBNQO6Q.js';
|
|
5
|
+
import { cn } from '../chunk-FEK5XGZW.js';
|
|
6
|
+
import { __objRest, __spreadValues } from '../chunk-3GWWZKX7.js';
|
|
7
|
+
import { Star, ChevronRight } from 'lucide-react';
|
|
8
|
+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
9
|
+
|
|
10
|
+
function DefaultLink(_a) {
|
|
11
|
+
var _b = _a, { href } = _b, props = __objRest(_b, ["href"]);
|
|
12
|
+
return /* @__PURE__ */ jsx("a", __spreadValues({ href }, props));
|
|
13
|
+
}
|
|
14
|
+
function SignatureLineList({
|
|
15
|
+
venues,
|
|
16
|
+
labels,
|
|
17
|
+
formatPrice,
|
|
18
|
+
formatCount,
|
|
19
|
+
metaLabels,
|
|
20
|
+
hues,
|
|
21
|
+
linkComponent: LinkComponent = DefaultLink,
|
|
22
|
+
className
|
|
23
|
+
}) {
|
|
24
|
+
return /* @__PURE__ */ jsx("ul", { className: cn("flex flex-col", className), children: venues.map((venue, index) => {
|
|
25
|
+
const { place, signature } = venue;
|
|
26
|
+
return /* @__PURE__ */ jsxs(
|
|
27
|
+
"li",
|
|
28
|
+
{
|
|
29
|
+
className: "grid gap-x-4 py-3.5 lg:grid-cols-[minmax(0,260px)_minmax(0,1fr)] lg:items-center",
|
|
30
|
+
children: [
|
|
31
|
+
index > 0 && /* @__PURE__ */ jsx(Separator, { className: "mb-3.5 lg:col-span-2" }),
|
|
32
|
+
/* @__PURE__ */ jsxs(
|
|
33
|
+
LinkComponent,
|
|
34
|
+
{
|
|
35
|
+
href: venue.href,
|
|
36
|
+
className: "flex items-center gap-3 rounded-md focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
37
|
+
children: [
|
|
38
|
+
/* @__PURE__ */ jsxs("span", { className: "relative h-12 w-12 shrink-0 overflow-hidden rounded-xl bg-[repeating-linear-gradient(135deg,oklch(0.78_0.018_55)_0_8px,oklch(0.84_0.012_58)_8px_16px)] lg:h-16 lg:w-16", children: [
|
|
39
|
+
place.thumbnail_url && // eslint-disable-next-line @next/next/no-img-element
|
|
40
|
+
/* @__PURE__ */ jsx(
|
|
41
|
+
"img",
|
|
42
|
+
{
|
|
43
|
+
src: place.thumbnail_url,
|
|
44
|
+
alt: "",
|
|
45
|
+
className: "h-full w-full object-cover",
|
|
46
|
+
loading: "lazy"
|
|
47
|
+
}
|
|
48
|
+
),
|
|
49
|
+
place.open_now && /* @__PURE__ */ jsx(
|
|
50
|
+
"i",
|
|
51
|
+
{
|
|
52
|
+
"aria-hidden": true,
|
|
53
|
+
className: "absolute right-1 top-1 h-[7px] w-[7px] rounded-full bg-[var(--free,currentColor)] ring-2 ring-white/80"
|
|
54
|
+
}
|
|
55
|
+
)
|
|
56
|
+
] }),
|
|
57
|
+
/* @__PURE__ */ jsxs("span", { className: "min-w-0 flex-1", children: [
|
|
58
|
+
/* @__PURE__ */ jsx("span", { className: "block truncate text-[15px] font-bold tracking-[-0.01em]", children: place.name }),
|
|
59
|
+
/* @__PURE__ */ jsxs("span", { className: "mt-0.5 flex items-center gap-1.5 text-xs text-muted-foreground", children: [
|
|
60
|
+
place.rating && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
61
|
+
/* @__PURE__ */ jsx(Star, { className: "h-[11px] w-[11px]", "aria-hidden": true }),
|
|
62
|
+
/* @__PURE__ */ jsx("span", { className: "tabular-nums", children: place.rating.rating })
|
|
63
|
+
] }),
|
|
64
|
+
(metaLabels == null ? void 0 : metaLabels[place.slug]) && /* @__PURE__ */ jsx("span", { className: "truncate", children: metaLabels[place.slug] })
|
|
65
|
+
] })
|
|
66
|
+
] }),
|
|
67
|
+
/* @__PURE__ */ jsx(
|
|
68
|
+
ChevronRight,
|
|
69
|
+
{
|
|
70
|
+
className: "h-4 w-4 shrink-0 text-muted-foreground lg:hidden",
|
|
71
|
+
"aria-hidden": true
|
|
72
|
+
}
|
|
73
|
+
)
|
|
74
|
+
]
|
|
75
|
+
}
|
|
76
|
+
),
|
|
77
|
+
signature && /* @__PURE__ */ jsx(
|
|
78
|
+
MenuItem,
|
|
79
|
+
{
|
|
80
|
+
item: signature.item,
|
|
81
|
+
labels,
|
|
82
|
+
formatPrice,
|
|
83
|
+
formatCount,
|
|
84
|
+
markGradient: hues == null ? void 0 : hues[place.slug],
|
|
85
|
+
subtitle: signature.why,
|
|
86
|
+
showSignatureBadge: signature.isSignature,
|
|
87
|
+
href: signature.href,
|
|
88
|
+
linkComponent: LinkComponent,
|
|
89
|
+
className: "min-w-0"
|
|
90
|
+
}
|
|
91
|
+
)
|
|
92
|
+
]
|
|
93
|
+
},
|
|
94
|
+
place.slug
|
|
95
|
+
);
|
|
96
|
+
}) });
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export { SignatureLineList };
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import * as class_variance_authority_types from 'class-variance-authority/types';
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import { VariantProps } from 'class-variance-authority';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* SnapRail — a horizontal, finger-driven rail with mandatory snap points, the
|
|
7
|
+
* way a native app lays out a row of cards. Not `DriftRail`: that one moves on
|
|
8
|
+
* its own and is for ambience; this one waits to be swiped and is for content
|
|
9
|
+
* the reader is expected to reach.
|
|
10
|
+
*
|
|
11
|
+
* Every direct child becomes a snap target and takes the rail's item width, so
|
|
12
|
+
* callers pass bare cards — no wrapper, no per-item width. Widths are
|
|
13
|
+
* percentages of the rail, so the peek of the next card is what tells the
|
|
14
|
+
* reader there is more; there is no scrollbar and no edge fade.
|
|
15
|
+
*
|
|
16
|
+
* `bleed` cancels the host's horizontal padding and restores it as scroll
|
|
17
|
+
* padding, so the first card lines up with the host's content edge and the
|
|
18
|
+
* rail still snaps to that same edge after a swipe. Without it, the rail
|
|
19
|
+
* snaps flush to the host's rounded corner and loads looking pre-scrolled.
|
|
20
|
+
* `card` is the 14px inset of a hub card; `page` is the 16px page gutter.
|
|
21
|
+
*/
|
|
22
|
+
declare const snapRailVariants: (props?: ({
|
|
23
|
+
itemWidth?: "sm" | "lg" | "full" | "md" | null | undefined;
|
|
24
|
+
gap?: "default" | "sm" | null | undefined;
|
|
25
|
+
bleed?: "none" | "page" | "card" | null | undefined;
|
|
26
|
+
} & class_variance_authority_types.ClassProp) | undefined) => string;
|
|
27
|
+
type SnapRailProps = React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof snapRailVariants> & {
|
|
28
|
+
/**
|
|
29
|
+
* Page dots under the rail, one per child, the current one filled. For
|
|
30
|
+
* `itemWidth="full"`, where no next card peeks in to say there is more.
|
|
31
|
+
* Decorative: the rail itself is the control, and every card is reachable
|
|
32
|
+
* by scrolling, so the dots are `aria-hidden`.
|
|
33
|
+
*/
|
|
34
|
+
dots?: boolean;
|
|
35
|
+
/** Classes for the dots row, e.g. `lg:hidden` where the rail becomes a grid. */
|
|
36
|
+
dotsClassName?: string;
|
|
37
|
+
};
|
|
38
|
+
declare const SnapRail: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLDivElement> & VariantProps<(props?: ({
|
|
39
|
+
itemWidth?: "sm" | "lg" | "full" | "md" | null | undefined;
|
|
40
|
+
gap?: "default" | "sm" | null | undefined;
|
|
41
|
+
bleed?: "none" | "page" | "card" | null | undefined;
|
|
42
|
+
} & class_variance_authority_types.ClassProp) | undefined) => string> & {
|
|
43
|
+
/**
|
|
44
|
+
* Page dots under the rail, one per child, the current one filled. For
|
|
45
|
+
* `itemWidth="full"`, where no next card peeks in to say there is more.
|
|
46
|
+
* Decorative: the rail itself is the control, and every card is reachable
|
|
47
|
+
* by scrolling, so the dots are `aria-hidden`.
|
|
48
|
+
*/
|
|
49
|
+
dots?: boolean;
|
|
50
|
+
/** Classes for the dots row, e.g. `lg:hidden` where the rail becomes a grid. */
|
|
51
|
+
dotsClassName?: string;
|
|
52
|
+
} & React.RefAttributes<HTMLDivElement>>;
|
|
53
|
+
|
|
54
|
+
export { SnapRail, type SnapRailProps };
|