@bearmenu/ui 0.8.16 → 0.8.17
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-ZLPWEHZO.js → chunk-54FQQZZJ.js} +1 -1
- package/dist/{chunk-36KEHCET.js → chunk-5LTILG3L.js} +15 -4
- package/dist/{chunk-P43B3ONY.js → chunk-5XREWOBB.js} +3 -1
- package/dist/{chunk-CLLY6XB3.js → chunk-6VOWT4YR.js} +6 -3
- package/dist/{chunk-S33K4RC5.js → chunk-BKKQ7GCZ.js} +1 -1
- package/dist/chunk-QY66ME5B.js +207 -0
- package/dist/{chunk-T6S2ZBV6.js → chunk-SDWN6L4O.js} +1 -1
- package/dist/{chunk-CNJQUHCU.js → chunk-SWD6CJ35.js} +2 -1
- package/dist/{chunk-HDR7V6IZ.js → chunk-TIQNM4XD.js} +8 -1
- package/dist/components/alert-dialog.js +1 -1
- package/dist/components/badge.d.ts +1 -1
- package/dist/components/badge.js +1 -1
- package/dist/components/button.d.ts +1 -1
- package/dist/components/button.js +1 -1
- package/dist/components/calendar.js +1 -1
- package/dist/components/carousel.js +1 -1
- package/dist/components/coach-marks.d.ts +63 -0
- package/dist/components/coach-marks.js +162 -0
- package/dist/components/combobox.js +1 -1
- package/dist/components/concept-compare-rail.js +2 -2
- package/dist/components/concept-compare.js +2 -2
- package/dist/components/concept-convergence.js +2 -2
- package/dist/components/doorway-tiles.d.ts +11 -0
- package/dist/components/doorway-tiles.js +5 -3
- package/dist/components/empty-state.js +1 -1
- package/dist/components/event-detail-panel.js +2 -2
- 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 +1 -1
- package/dist/components/facet-group.d.ts +8 -1
- package/dist/components/facet-group.js +3 -0
- package/dist/components/filter-category-row.js +1 -1
- package/dist/components/hue-chip.js +1 -1
- package/dist/components/item.js +4 -1
- package/dist/components/kitchen-dossier.js +1 -1
- package/dist/components/link-button.js +1 -1
- package/dist/components/menu-family-block.d.ts +8 -1
- package/dist/components/menu-family-block.js +101 -68
- package/dist/components/menu-family-card.js +2 -2
- package/dist/components/menu-filter-chips.d.ts +12 -3
- package/dist/components/menu-filter-chips.js +22 -4
- package/dist/components/menu-index-list.js +1 -1
- package/dist/components/menu-item.js +3 -3
- package/dist/components/menu-kitchen-list.d.ts +14 -2
- package/dist/components/menu-kitchen-list.js +1 -1
- package/dist/components/multi-select-combobox.js +2 -2
- package/dist/components/pagination.js +1 -1
- package/dist/components/photo-ground.d.ts +7 -1
- package/dist/components/photo-ground.js +1 -1
- package/dist/components/place-card.js +1 -1
- package/dist/components/place-details-mobile.js +1 -1
- package/dist/components/searchable-select.js +1 -1
- package/dist/components/section-header.d.ts +7 -1
- package/dist/components/section-header.js +11 -7
- package/dist/components/signature-chip.js +2 -2
- package/dist/components/sliding-panels.js +1 -1
- package/dist/components/toggle-group.js +9 -4
- package/dist/components/toggle.d.ts +1 -1
- package/dist/components/toggle.js +1 -1
- package/dist/components/top-ranking-entry.js +3 -3
- package/package.json +1 -1
- package/src/components/badge.stories.tsx +31 -1
- package/src/components/badge.tsx +2 -0
- package/src/components/button.stories.tsx +5 -4
- package/src/components/button.test.tsx +11 -1
- package/src/components/button.tsx +16 -5
- package/src/components/coach-marks.stories.tsx +79 -0
- package/src/components/coach-marks.test.tsx +140 -0
- package/src/components/coach-marks.tsx +252 -0
- package/src/components/doorway-tiles.stories.tsx +34 -0
- package/src/components/doorway-tiles.test.tsx +27 -0
- package/src/components/doorway-tiles.tsx +30 -2
- package/src/components/facet-group.tsx +9 -0
- package/src/components/hue-chip.test.tsx +6 -3
- package/src/components/hue-chip.tsx +5 -2
- package/src/components/item.tsx +4 -1
- package/src/components/menu-family-block.stories.tsx +12 -0
- package/src/components/menu-family-block.tsx +53 -20
- package/src/components/menu-filter-chips.tsx +32 -4
- package/src/components/menu-kitchen-list.tsx +45 -6
- package/src/components/photo-ground.tsx +7 -0
- package/src/components/section-header.test.tsx +15 -0
- package/src/components/section-header.tsx +26 -12
- package/src/components/toggle-group.stories.tsx +37 -1
- package/src/components/toggle-group.tsx +11 -6
- package/src/components/toggle.tsx +8 -0
- package/dist/chunk-SXQZ7DLH.js +0 -167
|
@@ -59,11 +59,19 @@ export type MenuFilterChipsProps = {
|
|
|
59
59
|
onMorePointerDown?: () => void;
|
|
60
60
|
/** Locale-aware integer formatter for the counts. Defaults to `String`. */
|
|
61
61
|
formatCount?: (value: number) => string;
|
|
62
|
+
/**
|
|
63
|
+
* `sm` (36px) is the row under a page header. `lg` (44px) is for a row
|
|
64
|
+
* inside a sticky bar, where the chips are the only thing the thumb can
|
|
65
|
+
* reach and 36px sits under the target floor.
|
|
66
|
+
*/
|
|
67
|
+
chipSize?: MenuFilterChipSize;
|
|
62
68
|
/** Accessible name for the row. */
|
|
63
69
|
ariaLabel?: string;
|
|
64
70
|
className?: string;
|
|
65
71
|
};
|
|
66
72
|
|
|
73
|
+
export type MenuFilterChipSize = "sm" | "lg";
|
|
74
|
+
|
|
67
75
|
export function MenuFilterChips({
|
|
68
76
|
chips,
|
|
69
77
|
onToggle,
|
|
@@ -73,6 +81,7 @@ export function MenuFilterChips({
|
|
|
73
81
|
moreIcon,
|
|
74
82
|
onMorePointerDown,
|
|
75
83
|
formatCount = String,
|
|
84
|
+
chipSize = "sm",
|
|
76
85
|
ariaLabel,
|
|
77
86
|
className,
|
|
78
87
|
}: MenuFilterChipsProps) {
|
|
@@ -99,6 +108,7 @@ export function MenuFilterChips({
|
|
|
99
108
|
<Chip
|
|
100
109
|
key={chip.key}
|
|
101
110
|
chip={chip}
|
|
111
|
+
size={chipSize}
|
|
102
112
|
onToggle={() => onToggle(chip.key)}
|
|
103
113
|
formatCount={formatCount}
|
|
104
114
|
/>
|
|
@@ -120,7 +130,11 @@ export function MenuFilterChips({
|
|
|
120
130
|
onPointerDown={onMorePointerDown}
|
|
121
131
|
aria-label={moreLabel}
|
|
122
132
|
aria-haspopup="dialog"
|
|
123
|
-
className=
|
|
133
|
+
className={cn(
|
|
134
|
+
"inline-flex shrink-0 items-center justify-center rounded-full border border-border text-accent transition-colors hover:bg-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring motion-reduce:transition-none lg:hidden",
|
|
135
|
+
// The one control that never scrolls keeps the chips' height.
|
|
136
|
+
chipSize === "lg" ? "size-11" : "size-10"
|
|
137
|
+
)}
|
|
124
138
|
>
|
|
125
139
|
{moreIcon}
|
|
126
140
|
</button>
|
|
@@ -143,10 +157,12 @@ export function MenuFilterChips({
|
|
|
143
157
|
|
|
144
158
|
function Chip({
|
|
145
159
|
chip,
|
|
160
|
+
size,
|
|
146
161
|
onToggle,
|
|
147
162
|
formatCount,
|
|
148
163
|
}: {
|
|
149
164
|
chip: MenuFilterChip;
|
|
165
|
+
size: MenuFilterChipSize;
|
|
150
166
|
onToggle: () => void;
|
|
151
167
|
formatCount: (value: number) => string;
|
|
152
168
|
}) {
|
|
@@ -156,7 +172,8 @@ function Chip({
|
|
|
156
172
|
onClick={onToggle}
|
|
157
173
|
aria-pressed={chip.active}
|
|
158
174
|
className={cn(
|
|
159
|
-
"inline-flex
|
|
175
|
+
"inline-flex shrink-0 items-center gap-1.5 whitespace-nowrap rounded-full border px-4 text-sm transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring motion-reduce:transition-none",
|
|
176
|
+
size === "lg" ? "h-11" : "h-9",
|
|
160
177
|
chip.active
|
|
161
178
|
? "border-accent font-semibold text-accent shadow-[0_1px_2px_oklch(0.51_0.095_45_/_0.08)]"
|
|
162
179
|
: "border-border bg-card text-foreground hover:border-accent/40",
|
|
@@ -226,12 +243,23 @@ export function MenuFilterSummary({
|
|
|
226
243
|
*/
|
|
227
244
|
const CHIP_WIDTHS = [128, 104, 96, 148, 112, 88];
|
|
228
245
|
|
|
229
|
-
export function MenuFilterChipsSkeleton({
|
|
246
|
+
export function MenuFilterChipsSkeleton({
|
|
247
|
+
className,
|
|
248
|
+
chipSize = "sm",
|
|
249
|
+
}: {
|
|
250
|
+
className?: string;
|
|
251
|
+
/** Match the row it stands in for, so the chips do not jump 8px on load. */
|
|
252
|
+
chipSize?: MenuFilterChipSize;
|
|
253
|
+
}) {
|
|
230
254
|
return (
|
|
231
255
|
<div className={cn("flex items-center gap-2 overflow-hidden", className)} aria-hidden>
|
|
232
256
|
<Skeleton className="h-3 w-[92px] shrink-0" />
|
|
233
257
|
{CHIP_WIDTHS.map((width, index) => (
|
|
234
|
-
<Skeleton
|
|
258
|
+
<Skeleton
|
|
259
|
+
key={index}
|
|
260
|
+
className={cn("shrink-0 rounded-full", chipSize === "lg" ? "h-11" : "h-9")}
|
|
261
|
+
style={{ width }}
|
|
262
|
+
/>
|
|
235
263
|
))}
|
|
236
264
|
</div>
|
|
237
265
|
);
|
|
@@ -61,6 +61,18 @@ export type MenuKitchenListLabels = {
|
|
|
61
61
|
showMore?: string;
|
|
62
62
|
};
|
|
63
63
|
|
|
64
|
+
/**
|
|
65
|
+
* `wide` (default): the intent page's full-width list — from `lg` the venue
|
|
66
|
+
* takes a fixed 226px column and the dish stands beside it in a column of its
|
|
67
|
+
* own. `stacked`: the list sits in a narrow column (a concept card's row
|
|
68
|
+
* half), so the venue keeps the row and the dish takes a SECOND LINE under it
|
|
69
|
+
* from `lg`; in a half-width card the dish column had ~100px and its kicker,
|
|
70
|
+
* name and meta wrapped into a stack of single words. `phone`: the phone row
|
|
71
|
+
* at every width — no dish line, the venue and its price only — for a card
|
|
72
|
+
* that keeps its phone form on a desktop rail.
|
|
73
|
+
*/
|
|
74
|
+
export type MenuKitchenListLayout = "wide" | "stacked" | "phone";
|
|
75
|
+
|
|
64
76
|
export type MenuKitchenListProps = {
|
|
65
77
|
kitchens: MenuKitchenPreview[];
|
|
66
78
|
labels: MenuKitchenListLabels;
|
|
@@ -69,6 +81,7 @@ export type MenuKitchenListProps = {
|
|
|
69
81
|
* navigates for the eleventh — the list is the page.
|
|
70
82
|
*/
|
|
71
83
|
onShowMore?: () => void;
|
|
84
|
+
layout?: MenuKitchenListLayout;
|
|
72
85
|
linkComponent?: MenuLinkComponent;
|
|
73
86
|
className?: string;
|
|
74
87
|
};
|
|
@@ -77,6 +90,7 @@ export function MenuKitchenList({
|
|
|
77
90
|
kitchens,
|
|
78
91
|
labels,
|
|
79
92
|
onShowMore,
|
|
93
|
+
layout = "wide",
|
|
80
94
|
linkComponent = DefaultLink,
|
|
81
95
|
className,
|
|
82
96
|
}: MenuKitchenListProps) {
|
|
@@ -90,7 +104,12 @@ export function MenuKitchenList({
|
|
|
90
104
|
because `<ol>` admits only list items, so a sibling element
|
|
91
105
|
between them would be invalid markup. */}
|
|
92
106
|
{index > 0 && <Separator />}
|
|
93
|
-
<KitchenRow
|
|
107
|
+
<KitchenRow
|
|
108
|
+
kitchen={kitchen}
|
|
109
|
+
labels={labels}
|
|
110
|
+
layout={layout}
|
|
111
|
+
linkComponent={linkComponent}
|
|
112
|
+
/>
|
|
94
113
|
</li>
|
|
95
114
|
))}
|
|
96
115
|
</ol>
|
|
@@ -111,17 +130,25 @@ export function MenuKitchenList({
|
|
|
111
130
|
function KitchenRow({
|
|
112
131
|
kitchen,
|
|
113
132
|
labels,
|
|
133
|
+
layout,
|
|
114
134
|
linkComponent: LinkComponent,
|
|
115
135
|
}: {
|
|
116
136
|
kitchen: MenuKitchenPreview;
|
|
117
137
|
labels: MenuKitchenListLabels;
|
|
138
|
+
layout: MenuKitchenListLayout;
|
|
118
139
|
linkComponent: MenuLinkComponent;
|
|
119
140
|
}) {
|
|
120
141
|
const { place, dish } = kitchen;
|
|
121
142
|
const openNow = place.open_now === true;
|
|
143
|
+
const wide = layout === "wide";
|
|
122
144
|
|
|
123
145
|
return (
|
|
124
|
-
<div
|
|
146
|
+
<div
|
|
147
|
+
className={cn(
|
|
148
|
+
"relative flex flex-wrap items-center gap-[11px] py-3 transition-colors hover:bg-muted/30 motion-reduce:transition-none lg:gap-4 lg:py-3.5",
|
|
149
|
+
wide && "lg:flex-nowrap"
|
|
150
|
+
)}
|
|
151
|
+
>
|
|
125
152
|
{/* The venue link, stretched. `z-[1]` — the dish link sits above it. */}
|
|
126
153
|
<LinkComponent
|
|
127
154
|
href={kitchen.href}
|
|
@@ -149,7 +176,12 @@ function KitchenRow({
|
|
|
149
176
|
/>
|
|
150
177
|
</span>
|
|
151
178
|
|
|
152
|
-
<div
|
|
179
|
+
<div
|
|
180
|
+
className={cn(
|
|
181
|
+
"flex min-w-0 flex-1 flex-col gap-0.5",
|
|
182
|
+
wide && "lg:w-[226px] lg:flex-none"
|
|
183
|
+
)}
|
|
184
|
+
>
|
|
153
185
|
{/* One line with an ellipsis on a phone, where the row is the whole
|
|
154
186
|
width; two lines from `lg`, where the column is 226px and
|
|
155
187
|
"ADRIATICA GELATERIA ITALIANA" would otherwise lose its half. */}
|
|
@@ -190,8 +222,15 @@ function KitchenRow({
|
|
|
190
222
|
one tap away on the venue's own page, with room to state it.
|
|
191
223
|
|
|
192
224
|
`order-5` puts it last in the visual order without moving it in the
|
|
193
|
-
DOM, where it belongs to the venue.
|
|
194
|
-
|
|
225
|
+
DOM, where it belongs to the venue. In the `stacked` layout it is a
|
|
226
|
+
second LINE under the venue, indented past the thumbnail. */
|
|
227
|
+
<div
|
|
228
|
+
className={cn(
|
|
229
|
+
"hidden min-w-0 basis-full pl-[57px]",
|
|
230
|
+
layout !== "phone" && "lg:block",
|
|
231
|
+
wide ? "lg:order-none lg:flex-1 lg:basis-auto lg:pl-0" : "lg:order-5 lg:pl-[74px]"
|
|
232
|
+
)}
|
|
233
|
+
>
|
|
195
234
|
<span
|
|
196
235
|
className={cn(
|
|
197
236
|
"flex items-center gap-1.5 text-[9px] font-extrabold uppercase tracking-[0.1em]",
|
|
@@ -230,7 +269,7 @@ function KitchenRow({
|
|
|
230
269
|
</div>
|
|
231
270
|
)}
|
|
232
271
|
|
|
233
|
-
<div className="ml-auto flex shrink-0 items-center gap-[11px] lg:ml-0">
|
|
272
|
+
<div className={cn("ml-auto flex shrink-0 items-center gap-[11px]", wide && "lg:ml-0")}>
|
|
234
273
|
{kitchen.priceLabel && (
|
|
235
274
|
<span className="text-base font-extrabold tabular-nums text-accent">
|
|
236
275
|
{kitchen.priceLabel}
|
|
@@ -38,6 +38,13 @@ import { cn } from "../lib/utils";
|
|
|
38
38
|
/** The ground under the photography. Fixed, and deliberately not a token. */
|
|
39
39
|
export const PHOTO_GROUND = "bg-[oklch(0.21_0.012_55)]";
|
|
40
40
|
|
|
41
|
+
/**
|
|
42
|
+
* The shadow a glyph wears when it stands on `PHOTO_GROUND` in a photograph's
|
|
43
|
+
* place — the card family's fallback treatment: a ground and a lifted glyph,
|
|
44
|
+
* never a bare icon on a flat fill. Same literal family as the ground.
|
|
45
|
+
*/
|
|
46
|
+
export const PHOTO_GLYPH_SHADOW = "drop-shadow-[0_2px_6px_oklch(0.16_0.012_55_/_0.6)]";
|
|
47
|
+
|
|
41
48
|
/** The uniform dark overlay a card may put over every photograph. */
|
|
42
49
|
const OVERLAY = "absolute inset-0 bg-[oklch(0.16_0.012_55_/_0.5)]";
|
|
43
50
|
|
|
@@ -25,4 +25,19 @@ describe("SectionHeader", () => {
|
|
|
25
25
|
expect(screen.getByText("Still to start").className).toContain("hidden");
|
|
26
26
|
expect(screen.getByText("Still to start").className).toContain("lg:block");
|
|
27
27
|
});
|
|
28
|
+
|
|
29
|
+
it("holds trailing controls at the row's end, beside the action", () => {
|
|
30
|
+
render(
|
|
31
|
+
<SectionHeader
|
|
32
|
+
title="Concepts"
|
|
33
|
+
action="See all"
|
|
34
|
+
actionHref="/concepts"
|
|
35
|
+
trailing={<button type="button">Next</button>}
|
|
36
|
+
/>
|
|
37
|
+
);
|
|
38
|
+
const next = screen.getByRole("button", { name: "Next" });
|
|
39
|
+
const action = screen.getByRole("link", { name: "See all" });
|
|
40
|
+
expect(next.parentElement).toBe(action.parentElement);
|
|
41
|
+
expect(next.parentElement).not.toContainElement(screen.getByText("Concepts"));
|
|
42
|
+
});
|
|
28
43
|
});
|
|
@@ -60,6 +60,12 @@ export type SectionHeaderProps = {
|
|
|
60
60
|
* would dilute the headings that matter. Defaults to `"h2"`.
|
|
61
61
|
*/
|
|
62
62
|
as?: "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "p" | "div";
|
|
63
|
+
/**
|
|
64
|
+
* Controls at the end of the title row, after `action` — a rail's
|
|
65
|
+
* previous / next pair on desktop. The header owns none of their
|
|
66
|
+
* behaviour; it only holds the slot.
|
|
67
|
+
*/
|
|
68
|
+
trailing?: React.ReactNode;
|
|
63
69
|
className?: string;
|
|
64
70
|
};
|
|
65
71
|
|
|
@@ -91,6 +97,7 @@ export function SectionHeader({
|
|
|
91
97
|
linkComponent: LinkComponent = DefaultLink,
|
|
92
98
|
onAction,
|
|
93
99
|
as: Heading = "h2",
|
|
100
|
+
trailing,
|
|
94
101
|
className,
|
|
95
102
|
}: SectionHeaderProps) {
|
|
96
103
|
const actionIcon = <ChevronRight className="h-3.5 w-3.5" aria-hidden />;
|
|
@@ -110,18 +117,25 @@ export function SectionHeader({
|
|
|
110
117
|
)}
|
|
111
118
|
</div>
|
|
112
119
|
|
|
113
|
-
{action
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
120
|
+
{/* The action and the trailing controls share the row's end, so the
|
|
121
|
+
action never floats to the middle when both are present. */}
|
|
122
|
+
{(action || trailing) && (
|
|
123
|
+
<div className="flex shrink-0 items-center gap-3">
|
|
124
|
+
{action &&
|
|
125
|
+
(actionHref ? (
|
|
126
|
+
<LinkComponent href={actionHref} className={actionClasses} onClick={onAction}>
|
|
127
|
+
{action}
|
|
128
|
+
{actionIcon}
|
|
129
|
+
</LinkComponent>
|
|
130
|
+
) : onAction ? (
|
|
131
|
+
<button type="button" className={actionClasses} onClick={onAction}>
|
|
132
|
+
{action}
|
|
133
|
+
{actionIcon}
|
|
134
|
+
</button>
|
|
135
|
+
) : null)}
|
|
136
|
+
{trailing}
|
|
137
|
+
</div>
|
|
138
|
+
)}
|
|
125
139
|
</div>
|
|
126
140
|
|
|
127
141
|
{subtitle && (
|
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
2
|
import { ToggleGroup, ToggleGroupItem } from "./toggle-group";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
Bold,
|
|
5
|
+
Italic,
|
|
6
|
+
Underline,
|
|
7
|
+
AlignLeft,
|
|
8
|
+
AlignCenter,
|
|
9
|
+
AlignRight,
|
|
10
|
+
LayoutGrid,
|
|
11
|
+
List,
|
|
12
|
+
} from "lucide-react";
|
|
13
|
+
import { Button } from "./button";
|
|
4
14
|
|
|
5
15
|
const meta = {
|
|
6
16
|
title: "Components/ToggleGroup",
|
|
@@ -58,3 +68,29 @@ export const Outline: Story = {
|
|
|
58
68
|
</ToggleGroup>
|
|
59
69
|
),
|
|
60
70
|
};
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* The segmented control: one 44px muted capsule, the on cell a raised card.
|
|
74
|
+
* Sits beside pill buttons at the same height — the browse toolbar's view
|
|
75
|
+
* switch. Single, always one on.
|
|
76
|
+
*/
|
|
77
|
+
export const Segmented: Story = {
|
|
78
|
+
render: () => (
|
|
79
|
+
<div className="flex items-center gap-2">
|
|
80
|
+
<Button variant="outline" size="pill">
|
|
81
|
+
Filters
|
|
82
|
+
</Button>
|
|
83
|
+
<Button variant="outline" size="pill">
|
|
84
|
+
Sort
|
|
85
|
+
</Button>
|
|
86
|
+
<ToggleGroup type="single" variant="segmented" defaultValue="list" aria-label="View">
|
|
87
|
+
<ToggleGroupItem value="list" aria-label="Rows">
|
|
88
|
+
<List />
|
|
89
|
+
</ToggleGroupItem>
|
|
90
|
+
<ToggleGroupItem value="grid" aria-label="Cards">
|
|
91
|
+
<LayoutGrid />
|
|
92
|
+
</ToggleGroupItem>
|
|
93
|
+
</ToggleGroup>
|
|
94
|
+
</div>
|
|
95
|
+
),
|
|
96
|
+
};
|
|
@@ -26,13 +26,14 @@ function ToggleGroup({
|
|
|
26
26
|
data-size={size}
|
|
27
27
|
className={cn(
|
|
28
28
|
"group/toggle-group flex w-fit items-center rounded-md data-[variant=outline]:shadow-xs",
|
|
29
|
+
// The segmented control's capsule: muted ground, the on cell a raised
|
|
30
|
+
// card inside it (see `toggleVariants.segmented`). 44px tall.
|
|
31
|
+
"data-[variant=segmented]:gap-0.5 data-[variant=segmented]:rounded-full data-[variant=segmented]:bg-muted data-[variant=segmented]:p-0.5",
|
|
29
32
|
className
|
|
30
33
|
)}
|
|
31
34
|
{...props}
|
|
32
35
|
>
|
|
33
|
-
<ToggleGroupContext.Provider value={{ variant, size }}>
|
|
34
|
-
{children}
|
|
35
|
-
</ToggleGroupContext.Provider>
|
|
36
|
+
<ToggleGroupContext.Provider value={{ variant, size }}>{children}</ToggleGroupContext.Provider>
|
|
36
37
|
</ToggleGroupPrimitive.Root>
|
|
37
38
|
);
|
|
38
39
|
}
|
|
@@ -45,18 +46,22 @@ function ToggleGroupItem({
|
|
|
45
46
|
...props
|
|
46
47
|
}: React.ComponentProps<typeof ToggleGroupPrimitive.Item> & VariantProps<typeof toggleVariants>) {
|
|
47
48
|
const context = React.useContext(ToggleGroupContext);
|
|
49
|
+
const resolved = context.variant || variant;
|
|
48
50
|
|
|
49
51
|
return (
|
|
50
52
|
<ToggleGroupPrimitive.Item
|
|
51
53
|
data-slot="toggle-group-item"
|
|
52
|
-
data-variant={
|
|
54
|
+
data-variant={resolved}
|
|
53
55
|
data-size={context.size || size}
|
|
54
56
|
className={cn(
|
|
55
57
|
toggleVariants({
|
|
56
|
-
variant:
|
|
58
|
+
variant: resolved,
|
|
57
59
|
size: context.size || size,
|
|
58
60
|
}),
|
|
59
|
-
"min-w-0 flex-1 shrink-0
|
|
61
|
+
"min-w-0 flex-1 shrink-0 shadow-none focus:z-10 focus-visible:z-10 data-[variant=outline]:border-l-0 data-[variant=outline]:first:border-l",
|
|
62
|
+
resolved === "segmented"
|
|
63
|
+
? "rounded-full px-0 data-[state=on]:shadow-[var(--shadow-card)]"
|
|
64
|
+
: "rounded-none first:rounded-l-md last:rounded-r-md",
|
|
60
65
|
className
|
|
61
66
|
)}
|
|
62
67
|
{...props}
|
|
@@ -14,6 +14,14 @@ const toggleVariants = cva(
|
|
|
14
14
|
default: "bg-transparent",
|
|
15
15
|
outline:
|
|
16
16
|
"border border-border bg-transparent hover:bg-accent/50 hover:text-accent-foreground data-[state=on]:border-primary",
|
|
17
|
+
/**
|
|
18
|
+
* One cell of a segmented control: a raised card on the group's
|
|
19
|
+
* muted capsule when on, quiet type when off. The group
|
|
20
|
+
* (`ToggleGroup variant="segmented"`) supplies the capsule; the cell
|
|
21
|
+
* never fills with the accent, which would read as a primary button.
|
|
22
|
+
*/
|
|
23
|
+
segmented:
|
|
24
|
+
"h-10 w-11 rounded-full text-muted-foreground hover:bg-transparent hover:text-foreground data-[state=on]:bg-card data-[state=on]:text-foreground data-[state=on]:shadow-[var(--shadow-card)]",
|
|
17
25
|
},
|
|
18
26
|
size: {
|
|
19
27
|
default: "h-9 px-2 min-w-9",
|
package/dist/chunk-SXQZ7DLH.js
DELETED
|
@@ -1,167 +0,0 @@
|
|
|
1
|
-
import { Separator } from './chunk-NWZGSLPU.js';
|
|
2
|
-
import { Skeleton } from './chunk-XBBEX4SF.js';
|
|
3
|
-
import { cn } from './chunk-FEK5XGZW.js';
|
|
4
|
-
import { __objRest, __spreadValues } from './chunk-MMUBH76A.js';
|
|
5
|
-
import { Star, Clock, Check, MessageSquare, ChevronRight } from 'lucide-react';
|
|
6
|
-
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
7
|
-
|
|
8
|
-
function DefaultLink(_a) {
|
|
9
|
-
var _b = _a, { href } = _b, props = __objRest(_b, ["href"]);
|
|
10
|
-
return /* @__PURE__ */ jsx("a", __spreadValues({ href }, props));
|
|
11
|
-
}
|
|
12
|
-
function MenuKitchenList({
|
|
13
|
-
kitchens,
|
|
14
|
-
labels,
|
|
15
|
-
onShowMore,
|
|
16
|
-
linkComponent = DefaultLink,
|
|
17
|
-
className
|
|
18
|
-
}) {
|
|
19
|
-
return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col", className), children: [
|
|
20
|
-
/* @__PURE__ */ jsx("ol", { children: kitchens.map((kitchen, index) => /* @__PURE__ */ jsxs("li", { className: "[&:first-child>div]:pt-0", children: [
|
|
21
|
-
index > 0 && /* @__PURE__ */ jsx(Separator, {}),
|
|
22
|
-
/* @__PURE__ */ jsx(KitchenRow, { kitchen, labels, linkComponent })
|
|
23
|
-
] }, kitchen.place.slug)) }),
|
|
24
|
-
onShowMore && labels.showMore && /* @__PURE__ */ jsx(
|
|
25
|
-
"button",
|
|
26
|
-
{
|
|
27
|
-
type: "button",
|
|
28
|
-
onClick: onShowMore,
|
|
29
|
-
className: "mt-2 self-start rounded-md py-2 text-[13px] font-bold text-accent transition-colors hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring motion-reduce:transition-none",
|
|
30
|
-
children: labels.showMore
|
|
31
|
-
}
|
|
32
|
-
)
|
|
33
|
-
] });
|
|
34
|
-
}
|
|
35
|
-
function KitchenRow({
|
|
36
|
-
kitchen,
|
|
37
|
-
labels,
|
|
38
|
-
linkComponent: LinkComponent
|
|
39
|
-
}) {
|
|
40
|
-
const { place, dish } = kitchen;
|
|
41
|
-
const openNow = place.open_now === true;
|
|
42
|
-
return /* @__PURE__ */ jsxs("div", { className: "relative flex flex-wrap items-center gap-[11px] py-3 transition-colors hover:bg-muted/30 motion-reduce:transition-none lg:flex-nowrap lg:gap-4 lg:py-3.5", children: [
|
|
43
|
-
/* @__PURE__ */ jsx(
|
|
44
|
-
LinkComponent,
|
|
45
|
-
{
|
|
46
|
-
href: kitchen.href,
|
|
47
|
-
"aria-label": labels.openVenue(place.name),
|
|
48
|
-
className: "absolute inset-0 z-[1] rounded-xl focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent"
|
|
49
|
-
}
|
|
50
|
-
),
|
|
51
|
-
/* @__PURE__ */ jsxs("span", { className: "relative h-[46px] w-[46px] shrink-0 overflow-hidden rounded-[11px] bg-[repeating-linear-gradient(135deg,oklch(0.78_0.018_55)_0_8px,oklch(0.84_0.012_58)_8px_16px)] lg:h-[58px] lg:w-[58px] lg:rounded-[13px]", children: [
|
|
52
|
-
place.thumbnail_url && // eslint-disable-next-line @next/next/no-img-element
|
|
53
|
-
/* @__PURE__ */ jsx(
|
|
54
|
-
"img",
|
|
55
|
-
{
|
|
56
|
-
src: place.thumbnail_url,
|
|
57
|
-
alt: "",
|
|
58
|
-
loading: "lazy",
|
|
59
|
-
decoding: "async",
|
|
60
|
-
className: "h-full w-full object-cover"
|
|
61
|
-
}
|
|
62
|
-
),
|
|
63
|
-
/* @__PURE__ */ jsx(
|
|
64
|
-
"span",
|
|
65
|
-
{
|
|
66
|
-
title: openNow ? labels.openNow : void 0,
|
|
67
|
-
className: cn(
|
|
68
|
-
"absolute right-[5px] top-[5px] h-[7px] w-[7px] rounded-full ring-2 ring-white/85",
|
|
69
|
-
openNow ? "bg-[var(--free,oklch(0.55_0.13_155))]" : "bg-[oklch(0.8_0.01_60)]"
|
|
70
|
-
)
|
|
71
|
-
}
|
|
72
|
-
)
|
|
73
|
-
] }),
|
|
74
|
-
/* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 flex-col gap-0.5 lg:w-[226px] lg:flex-none", children: [
|
|
75
|
-
/* @__PURE__ */ jsx("span", { className: "truncate text-[14.5px] font-bold tracking-[-0.01em] lg:line-clamp-2 lg:whitespace-normal lg:text-[15.5px] lg:leading-tight", children: place.name }),
|
|
76
|
-
/* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1 truncate text-xs text-muted-foreground", children: [
|
|
77
|
-
place.rating && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
78
|
-
/* @__PURE__ */ jsx(Star, { className: "h-[11px] w-[11px] shrink-0", "aria-hidden": true }),
|
|
79
|
-
place.rating.rating,
|
|
80
|
-
kitchen.metaLabel && " \xB7 "
|
|
81
|
-
] }),
|
|
82
|
-
kitchen.metaLabel
|
|
83
|
-
] }),
|
|
84
|
-
kitchen.hoursLabel && /* @__PURE__ */ jsxs(
|
|
85
|
-
"span",
|
|
86
|
-
{
|
|
87
|
-
className: cn(
|
|
88
|
-
"mt-[3px] inline-flex h-[21px] w-max items-center gap-[5px] rounded-md px-2 text-[10.5px] font-bold",
|
|
89
|
-
openNow ? "bg-[oklch(0.96_0.02_155)] text-[var(--free,oklch(0.45_0.13_155))]" : "bg-muted text-muted-foreground"
|
|
90
|
-
),
|
|
91
|
-
children: [
|
|
92
|
-
/* @__PURE__ */ jsx(Clock, { className: "h-2.5 w-2.5 shrink-0", "aria-hidden": true }),
|
|
93
|
-
kitchen.hoursLabel
|
|
94
|
-
]
|
|
95
|
-
}
|
|
96
|
-
)
|
|
97
|
-
] }),
|
|
98
|
-
dish && /* DESKTOP ONLY, where it is a column of its own beside the venue.
|
|
99
|
-
On the phone it wrapped to a third line under the row — kicker, dish,
|
|
100
|
-
mention count — which turned a four-line venue row into a seven-line
|
|
101
|
-
block and, ten rows down, buried the venue names the list exists to
|
|
102
|
-
be scanned for. The dish is not what a reader is choosing here; it is
|
|
103
|
-
one tap away on the venue's own page, with room to state it.
|
|
104
|
-
|
|
105
|
-
`order-5` puts it last in the visual order without moving it in the
|
|
106
|
-
DOM, where it belongs to the venue. */
|
|
107
|
-
/* @__PURE__ */ jsxs("div", { className: "hidden min-w-0 basis-full pl-[57px] lg:order-none lg:block lg:flex-1 lg:basis-auto lg:pl-0", children: [
|
|
108
|
-
/* @__PURE__ */ jsxs(
|
|
109
|
-
"span",
|
|
110
|
-
{
|
|
111
|
-
className: cn(
|
|
112
|
-
"flex items-center gap-1.5 text-[9px] font-extrabold uppercase tracking-[0.1em]",
|
|
113
|
-
dish.matched ? "text-accent" : "text-[oklch(0.72_0.01_60)]"
|
|
114
|
-
),
|
|
115
|
-
children: [
|
|
116
|
-
dish.kicker,
|
|
117
|
-
dish.matched && dish.matchLabel && /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1 rounded-[5px] bg-accent-subtle px-1.5 py-px text-[10.5px] font-bold normal-case tracking-normal text-accent", children: [
|
|
118
|
-
/* @__PURE__ */ jsx(Check, { className: "h-2.5 w-2.5 shrink-0", "aria-hidden": true }),
|
|
119
|
-
dish.matchLabel
|
|
120
|
-
] })
|
|
121
|
-
]
|
|
122
|
-
}
|
|
123
|
-
),
|
|
124
|
-
dish.href ? /* @__PURE__ */ jsx(
|
|
125
|
-
LinkComponent,
|
|
126
|
-
{
|
|
127
|
-
href: dish.href,
|
|
128
|
-
"aria-label": labels.openDish(dish.name),
|
|
129
|
-
className: "relative z-[2] mt-0.5 block truncate font-serif text-[13.5px] italic underline decoration-[oklch(0.84_0.01_60)] decoration-1 underline-offset-[3px] transition-colors hover:text-accent hover:decoration-accent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring motion-reduce:transition-none lg:text-[15px]",
|
|
130
|
-
children: dish.name
|
|
131
|
-
}
|
|
132
|
-
) : /* @__PURE__ */ jsx("span", { className: "mt-0.5 block truncate font-serif text-[13.5px] italic lg:text-[15px]", children: dish.name }),
|
|
133
|
-
dish.meta && /* @__PURE__ */ jsxs("span", { className: "mt-[3px] flex items-center gap-1 text-[11.5px] text-muted-foreground", children: [
|
|
134
|
-
!dish.matched && /* @__PURE__ */ jsx(MessageSquare, { className: "h-2.5 w-2.5 shrink-0", "aria-hidden": true }),
|
|
135
|
-
dish.meta
|
|
136
|
-
] })
|
|
137
|
-
] }),
|
|
138
|
-
/* @__PURE__ */ jsxs("div", { className: "ml-auto flex shrink-0 items-center gap-[11px] lg:ml-0", children: [
|
|
139
|
-
kitchen.priceLabel && /* @__PURE__ */ jsx("span", { className: "text-base font-extrabold tabular-nums text-accent", children: kitchen.priceLabel }),
|
|
140
|
-
/* @__PURE__ */ jsx(ChevronRight, { className: "h-[15px] w-[15px] shrink-0 text-muted-foreground", "aria-hidden": true })
|
|
141
|
-
] })
|
|
142
|
-
] });
|
|
143
|
-
}
|
|
144
|
-
function MenuKitchenListSkeleton({
|
|
145
|
-
rows = 10,
|
|
146
|
-
className
|
|
147
|
-
}) {
|
|
148
|
-
return /* @__PURE__ */ jsx("div", { className: cn("flex flex-col", className), "aria-hidden": true, children: Array.from({ length: rows }, (_, index) => /* @__PURE__ */ jsxs("div", { children: [
|
|
149
|
-
index > 0 && /* @__PURE__ */ jsx(Separator, {}),
|
|
150
|
-
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-[11px] py-3 lg:flex-nowrap lg:gap-4 lg:py-3.5", children: [
|
|
151
|
-
/* @__PURE__ */ jsx(Skeleton, { className: "h-[46px] w-[46px] rounded-[11px] lg:h-[58px] lg:w-[58px] lg:rounded-[13px]" }),
|
|
152
|
-
/* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 flex-col gap-1.5 lg:w-[226px] lg:flex-none", children: [
|
|
153
|
-
/* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-[60%]" }),
|
|
154
|
-
/* @__PURE__ */ jsx(Skeleton, { className: "h-3 w-[45%]" }),
|
|
155
|
-
/* @__PURE__ */ jsx(Skeleton, { className: "h-[21px] w-[120px] rounded-md" })
|
|
156
|
-
] }),
|
|
157
|
-
/* @__PURE__ */ jsxs("div", { className: "hidden min-w-0 basis-full pl-[57px] lg:order-none lg:block lg:flex-1 lg:basis-auto lg:pl-0", children: [
|
|
158
|
-
/* @__PURE__ */ jsx(Skeleton, { className: "h-2.5 w-[90px]" }),
|
|
159
|
-
/* @__PURE__ */ jsx(Skeleton, { className: "mt-1.5 h-4 w-[70%]" }),
|
|
160
|
-
/* @__PURE__ */ jsx(Skeleton, { className: "mt-1.5 h-3 w-[40%]" })
|
|
161
|
-
] }),
|
|
162
|
-
/* @__PURE__ */ jsx(Skeleton, { className: "ml-auto h-5 w-[56px] lg:ml-0" })
|
|
163
|
-
] })
|
|
164
|
-
] }, index)) });
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
export { MenuKitchenList, MenuKitchenListSkeleton };
|