@bearmenu/ui 0.8.51 → 0.8.53

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.
Files changed (67) hide show
  1. package/dist/chunk-GIJGVFGC.js +177 -0
  2. package/dist/chunk-H4ORZT2D.js +14 -0
  3. package/dist/chunk-RKNPPYRU.js +86 -0
  4. package/dist/{chunk-S5GDARLW.js → chunk-WY2FP76J.js} +28 -5
  5. package/dist/components/category-deck.d.ts +6 -1
  6. package/dist/components/category-deck.js +39 -35
  7. package/dist/components/event-card.d.ts +7 -0
  8. package/dist/components/event-card.js +9 -0
  9. package/dist/components/event-compact-row.d.ts +6 -0
  10. package/dist/components/event-compact-row.js +5 -3
  11. package/dist/components/event-hub-hero.d.ts +5 -0
  12. package/dist/components/event-hub-hero.js +14 -3
  13. package/dist/components/event-list-row-wide.d.ts +6 -0
  14. package/dist/components/event-list-row-wide.js +5 -3
  15. package/dist/components/event-tonight-strip.d.ts +3 -1
  16. package/dist/components/event-tonight-strip.js +3 -2
  17. package/dist/components/facet-group.d.ts +9 -2
  18. package/dist/components/facet-group.js +108 -10
  19. package/dist/components/featured-rail-item.d.ts +3 -1
  20. package/dist/components/featured-rail-item.js +3 -1
  21. package/dist/components/filter-option-chip.d.ts +15 -0
  22. package/dist/components/filter-option-chip.js +3 -0
  23. package/dist/components/filter-option-group.d.ts +37 -0
  24. package/dist/components/filter-option-group.js +10 -0
  25. package/dist/components/filter-panel-footer.d.ts +15 -0
  26. package/dist/components/filter-panel-footer.js +63 -0
  27. package/dist/components/kitchen-dossier.d.ts +7 -1
  28. package/dist/components/kitchen-dossier.js +2 -1
  29. package/dist/components/menu-hub-hero.d.ts +7 -1
  30. package/dist/components/menu-hub-hero.js +2 -0
  31. package/dist/components/place-card.d.ts +7 -0
  32. package/dist/components/place-card.js +11 -3
  33. package/dist/components/poster-stack.d.ts +2 -0
  34. package/dist/components/poster-stack.js +2 -1
  35. package/dist/components/section-header.d.ts +6 -1
  36. package/dist/components/section-header.js +4 -2
  37. package/dist/components/timeline-day.js +1 -1
  38. package/dist/components/timeline-hour.d.ts +13 -1
  39. package/dist/components/timeline-hour.js +1 -1
  40. package/package.json +6 -6
  41. package/src/components/category-deck.tsx +11 -0
  42. package/src/components/event-card.test.tsx +6 -0
  43. package/src/components/event-card.tsx +17 -0
  44. package/src/components/event-compact-row.tsx +34 -24
  45. package/src/components/event-hub-hero.tsx +15 -1
  46. package/src/components/event-list-row-wide.tsx +25 -15
  47. package/src/components/event-tonight-strip.test.tsx +12 -0
  48. package/src/components/event-tonight-strip.tsx +8 -1
  49. package/src/components/facet-group.tsx +153 -54
  50. package/src/components/featured-rail-item.tsx +5 -0
  51. package/src/components/filter-option-chip.test.tsx +50 -0
  52. package/src/components/filter-option-chip.tsx +81 -0
  53. package/src/components/filter-option-group.stories.tsx +250 -0
  54. package/src/components/filter-option-group.test.tsx +100 -0
  55. package/src/components/filter-option-group.tsx +231 -0
  56. package/src/components/filter-panel-footer.tsx +63 -0
  57. package/src/components/kitchen-dossier.tsx +14 -1
  58. package/src/components/menu-hub-hero.tsx +13 -0
  59. package/src/components/place-card.stories.tsx +10 -0
  60. package/src/components/place-card.test.tsx +31 -0
  61. package/src/components/place-card.tsx +22 -6
  62. package/src/components/poster-stack.tsx +3 -0
  63. package/src/components/section-header.tsx +8 -1
  64. package/src/components/timeline-day.stories.tsx +30 -0
  65. package/src/components/timeline-day.test.tsx +24 -0
  66. package/src/components/timeline-hour.tsx +40 -6
  67. package/src/lib/poster-overlay-frame.tsx +26 -0
@@ -3,7 +3,7 @@ import { cn } from '../chunk-FEK5XGZW.js';
3
3
  import { __objRest, __spreadValues } from '../chunk-MMUBH76A.js';
4
4
  import * as React from 'react';
5
5
  import { Clock, CalendarDays, ChevronRight } from 'lucide-react';
6
- import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
6
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
7
7
 
8
8
  function DefaultLink(_a) {
9
9
  var _b = _a, { href } = _b, props = __objRest(_b, ["href"]);
@@ -16,10 +16,11 @@ function EventTonightStrip({
16
16
  actionLabel,
17
17
  href,
18
18
  linkComponent: LinkComponent = DefaultLink,
19
+ liveIndicator,
19
20
  className
20
21
  }) {
21
22
  const interactive = !!href;
22
- const mark = tone === "live" ? /* @__PURE__ */ jsxs("span", { className: "relative flex h-2.5 w-2.5", "aria-hidden": true, children: [
23
+ const mark = tone === "live" && liveIndicator ? /* @__PURE__ */ jsx("span", { className: "flex h-4 w-4 items-center justify-center", "aria-hidden": true, children: liveIndicator }) : tone === "live" ? /* @__PURE__ */ jsxs("span", { className: "relative flex h-2.5 w-2.5", "aria-hidden": true, children: [
23
24
  /* @__PURE__ */ jsx("span", { className: "absolute inline-flex h-full w-full rounded-full bg-success/40 motion-live-pulse motion-reduce:animate-none" }),
24
25
  /* @__PURE__ */ jsx("span", { className: "relative inline-flex h-2.5 w-2.5 rounded-full bg-success" })
25
26
  ] }) : tone === "tonight" ? /* @__PURE__ */ jsx(Clock, { className: "h-4 w-4 text-muted-foreground", "aria-hidden": true }) : /* @__PURE__ */ jsx(
@@ -58,6 +58,12 @@ type FacetGroupProps = Omit<ComponentPropsWithoutRef<"fieldset">, "title" | "chi
58
58
  */
59
59
  name?: string;
60
60
  size?: FacetGroupSize;
61
+ /** Wrapping native-input chips for filter panels. Rows remain the default. */
62
+ layout?: "rows" | "chips";
63
+ /** Optional bounded chip list; selected options always stay visible. */
64
+ visibleLimit?: number;
65
+ showAllLabel?: string | ((count: number) => string);
66
+ showLessLabel?: string;
61
67
  /**
62
68
  * Turns the legend into a disclosure toggle for this group's options.
63
69
  *
@@ -84,7 +90,7 @@ type FacetGroupProps = Omit<ComponentPropsWithoutRef<"fieldset">, "title" | "chi
84
90
  moreExpanded?: boolean;
85
91
  className?: string;
86
92
  };
87
- declare function FacetGroup({ title, icon: Icon, options, selection, name, size, collapsible, defaultOpen, onToggle, moreLabel, onMore, moreExpanded, className, ...rest }: FacetGroupProps): react_jsx_runtime.JSX.Element;
93
+ declare function FacetGroup({ title, icon: Icon, options, selection, name, size, layout, visibleLimit, showAllLabel, showLessLabel, collapsible, defaultOpen, onToggle, moreLabel, onMore, moreExpanded, className, ...rest }: FacetGroupProps): react_jsx_runtime.JSX.Element;
88
94
  type FacetGroupSkeletonProps = {
89
95
  /** How many option rows to draw — match the expected/last-known option count. */
90
96
  optionCount?: number;
@@ -92,9 +98,10 @@ type FacetGroupSkeletonProps = {
92
98
  showMore?: boolean;
93
99
  /** Must match the `<FacetGroup>` it stands in for, or rows shift when data lands. */
94
100
  size?: FacetGroupSize;
101
+ layout?: "rows" | "chips";
95
102
  className?: string;
96
103
  };
97
104
  /** Mirrors `<FacetGroup>`'s geometry exactly so nothing shifts when data lands. */
98
- declare function FacetGroupSkeleton({ optionCount, showMore, size, className, }: FacetGroupSkeletonProps): react_jsx_runtime.JSX.Element;
105
+ declare function FacetGroupSkeleton({ optionCount, showMore, size, className, layout, }: FacetGroupSkeletonProps): react_jsx_runtime.JSX.Element;
99
106
 
100
107
  export { FacetGroup, type FacetGroupProps, type FacetGroupSelection, type FacetGroupSize, FacetGroupSkeleton, type FacetGroupSkeletonProps, type FacetOption };
@@ -1,9 +1,17 @@
1
+ import { FilterOptionGroup } from '../chunk-GIJGVFGC.js';
2
+ import '../chunk-RKNPPYRU.js';
3
+ import '../chunk-NWZGSLPU.js';
1
4
  import { Skeleton } from '../chunk-XBBEX4SF.js';
5
+ import { Button } from '../chunk-GHKTVHQG.js';
6
+ import '../chunk-RIH2IS7X.js';
7
+ import '../chunk-5YNIQ2BL.js';
8
+ import '../chunk-ZLMRVBD7.js';
9
+ import '../chunk-RYPPN2GZ.js';
2
10
  import { cn } from '../chunk-FEK5XGZW.js';
3
11
  import { __objRest, __spreadProps, __spreadValues } from '../chunk-MMUBH76A.js';
4
12
  import { useId, useState } from 'react';
5
13
  import { ChevronDown, Check } from 'lucide-react';
6
- import { jsxs, jsx } from 'react/jsx-runtime';
14
+ import { jsx, jsxs } from 'react/jsx-runtime';
7
15
 
8
16
  var ROW_SIZE = {
9
17
  comfortable: "-mx-2 min-h-11 rounded-lg px-2",
@@ -21,6 +29,10 @@ function FacetGroup(_a) {
21
29
  selection = "multiple",
22
30
  name,
23
31
  size = "comfortable",
32
+ layout = "rows",
33
+ visibleLimit,
34
+ showAllLabel,
35
+ showLessLabel,
24
36
  collapsible = false,
25
37
  defaultOpen = true,
26
38
  onToggle,
@@ -35,6 +47,10 @@ function FacetGroup(_a) {
35
47
  "selection",
36
48
  "name",
37
49
  "size",
50
+ "layout",
51
+ "visibleLimit",
52
+ "showAllLabel",
53
+ "showLessLabel",
38
54
  "collapsible",
39
55
  "defaultOpen",
40
56
  "onToggle",
@@ -50,6 +66,48 @@ function FacetGroup(_a) {
50
66
  const setOpen = setIsOpen;
51
67
  const fallbackName = useId();
52
68
  const groupName = name != null ? name : fallbackName;
69
+ if (layout === "chips") {
70
+ const value = options.filter((option) => option.checked).map((option) => option.id);
71
+ return /* @__PURE__ */ jsx(
72
+ FilterOptionGroup,
73
+ {
74
+ title,
75
+ options: options.map((option) => __spreadProps(__spreadValues({}, option), { value: option.id })),
76
+ value,
77
+ selection,
78
+ name: groupName,
79
+ defaultOpen,
80
+ collapsible,
81
+ divider: false,
82
+ visibleLimit,
83
+ showAllLabel,
84
+ showLessLabel,
85
+ disabled: rest.disabled,
86
+ className,
87
+ onValueChange: (next) => {
88
+ if (selection === "single") {
89
+ if (next[0] != null) onToggle == null ? void 0 : onToggle(String(next[0]), true);
90
+ return;
91
+ }
92
+ for (const option of options) {
93
+ const checked = next.includes(option.id);
94
+ if (checked !== !!option.checked) onToggle == null ? void 0 : onToggle(option.id, checked);
95
+ }
96
+ },
97
+ footer: moreLabel && /* @__PURE__ */ jsx(
98
+ Button,
99
+ {
100
+ type: "button",
101
+ variant: "link",
102
+ className: "min-h-11 px-0",
103
+ onClick: onMore,
104
+ "aria-expanded": moreExpanded,
105
+ children: moreLabel
106
+ }
107
+ )
108
+ }
109
+ );
110
+ }
53
111
  return (
54
112
  // NO SEPARATOR OF ITS OWN. This drew `<Separator className="absolute
55
113
  // inset-x-0 top-0" />`, which looked like a rule above each group and was
@@ -98,7 +156,14 @@ function FacetGroup(_a) {
98
156
  open ? "mb-[11px]" : "mb-0"
99
157
  ),
100
158
  children: [
101
- Icon && /* @__PURE__ */ jsx(Icon, { "aria-hidden": true, className: "size-3.5 shrink-0", strokeWidth: 2.25 }),
159
+ Icon && /* @__PURE__ */ jsx(
160
+ Icon,
161
+ {
162
+ "aria-hidden": true,
163
+ className: "size-3.5 shrink-0",
164
+ strokeWidth: 2.25
165
+ }
166
+ ),
102
167
  title,
103
168
  /* @__PURE__ */ jsx(
104
169
  ChevronDown,
@@ -114,7 +179,14 @@ function FacetGroup(_a) {
114
179
  }
115
180
  )
116
181
  ) : /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1.5", children: [
117
- Icon && /* @__PURE__ */ jsx(Icon, { "aria-hidden": true, className: "size-3.5 shrink-0", strokeWidth: 2.25 }),
182
+ Icon && /* @__PURE__ */ jsx(
183
+ Icon,
184
+ {
185
+ "aria-hidden": true,
186
+ className: "size-3.5 shrink-0",
187
+ strokeWidth: 2.25
188
+ }
189
+ ),
118
190
  title
119
191
  ] })
120
192
  }
@@ -164,7 +236,13 @@ function FacetGroup(_a) {
164
236
  "peer-focus-visible:ring-2 peer-focus-visible:ring-ring peer-focus-visible:ring-offset-2",
165
237
  checked ? "border-primary bg-primary" : "border-border bg-background"
166
238
  ),
167
- children: checked && (isSingle ? /* @__PURE__ */ jsx("span", { className: "size-[7px] rounded-full bg-primary-foreground" }) : /* @__PURE__ */ jsx(Check, { className: "h-[11px] w-[11px] text-primary-foreground", strokeWidth: 3 }))
239
+ children: checked && (isSingle ? /* @__PURE__ */ jsx("span", { className: "size-[7px] rounded-full bg-primary-foreground" }) : /* @__PURE__ */ jsx(
240
+ Check,
241
+ {
242
+ className: "h-[11px] w-[11px] text-primary-foreground",
243
+ strokeWidth: 3
244
+ }
245
+ ))
168
246
  }
169
247
  ),
170
248
  /* @__PURE__ */ jsx(
@@ -205,18 +283,38 @@ function FacetGroupSkeleton({
205
283
  optionCount = 4,
206
284
  showMore,
207
285
  size = "comfortable",
208
- className
286
+ className,
287
+ layout = "rows"
209
288
  }) {
289
+ if (layout === "chips")
290
+ return /* @__PURE__ */ jsxs("div", { "aria-hidden": "true", className: cn("py-3", className), children: [
291
+ /* @__PURE__ */ jsx(Skeleton, { className: "mb-4 h-4 w-24" }),
292
+ /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-2", children: Array.from({ length: optionCount }, (_, index) => /* @__PURE__ */ jsx(
293
+ Skeleton,
294
+ {
295
+ className: cn("h-11 rounded-2xl", index % 2 ? "w-28" : "w-24")
296
+ },
297
+ index
298
+ )) }),
299
+ showMore && /* @__PURE__ */ jsx(Skeleton, { className: "mt-3 h-4 w-20" })
300
+ ] });
210
301
  return (
211
302
  // Matches the live group: no rule of its own, `py-3`, and the row gap keyed
212
303
  // to the same density — or the panel shifts on load by the difference.
213
304
  /* @__PURE__ */ jsxs("div", { "aria-hidden": "true", className: cn("py-3", className), children: [
214
305
  /* @__PURE__ */ jsx(Skeleton, { className: "mb-[11px] h-3 w-20" }),
215
- /* @__PURE__ */ jsx("div", { className: cn("flex flex-col", ROW_GAP[size]), children: Array.from({ length: optionCount }).map((_, index) => /* @__PURE__ */ jsxs("div", { className: cn("flex items-center gap-[9px]", ROW_SIZE[size]), children: [
216
- /* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-4 rounded-[5px]" }),
217
- /* @__PURE__ */ jsx(Skeleton, { className: "h-3.5 w-24" }),
218
- /* @__PURE__ */ jsx(Skeleton, { className: "ml-auto h-3 w-5" })
219
- ] }, index)) }),
306
+ /* @__PURE__ */ jsx("div", { className: cn("flex flex-col", ROW_GAP[size]), children: Array.from({ length: optionCount }).map((_, index) => /* @__PURE__ */ jsxs(
307
+ "div",
308
+ {
309
+ className: cn("flex items-center gap-[9px]", ROW_SIZE[size]),
310
+ children: [
311
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-4 rounded-[5px]" }),
312
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-3.5 w-24" }),
313
+ /* @__PURE__ */ jsx(Skeleton, { className: "ml-auto h-3 w-5" })
314
+ ]
315
+ },
316
+ index
317
+ )) }),
220
318
  showMore && /* @__PURE__ */ jsx(Skeleton, { className: "mt-[11px] h-3 w-16" })
221
319
  ] })
222
320
  );
@@ -24,6 +24,8 @@ type FeaturedRailItemProps = {
24
24
  note: string;
25
25
  /** Top-left overlay pill. Include its icon. */
26
26
  tagLabel?: React.ReactNode;
27
+ /** Decorative overlay inside the poster box (`relative`, clipping): an absolutely positioned, `aria-hidden` ornament. */
28
+ posterOverlay?: React.ReactNode;
27
29
  posterUrl?: string;
28
30
  href?: string;
29
31
  /** Defaults to a plain `<a>`. Pass `next/link` in a Next app. */
@@ -33,7 +35,7 @@ type FeaturedRailItemProps = {
33
35
  /** Rendered instead of the poster when it's absent or failed to load. Defaults to `<EventPosterFallback />`. */
34
36
  fallback?: ReactNode;
35
37
  };
36
- declare function FeaturedRailItem({ title, venue, note, tagLabel, posterUrl, href, linkComponent: LinkComponent, onClick, className, fallback, }: FeaturedRailItemProps): react_jsx_runtime.JSX.Element;
38
+ declare function FeaturedRailItem({ title, venue, note, tagLabel, posterOverlay, posterUrl, href, linkComponent: LinkComponent, onClick, className, fallback, }: FeaturedRailItemProps): react_jsx_runtime.JSX.Element;
37
39
  type FeaturedRailItemSkeletonProps = {
38
40
  className?: string;
39
41
  };
@@ -14,6 +14,7 @@ function FeaturedRailItem({
14
14
  venue,
15
15
  note,
16
16
  tagLabel,
17
+ posterOverlay,
17
18
  posterUrl,
18
19
  href,
19
20
  linkComponent: LinkComponent = DefaultLink,
@@ -40,7 +41,8 @@ function FeaturedRailItem({
40
41
  }
41
42
  )
42
43
  ) : fallback,
43
- tagLabel && /* @__PURE__ */ jsx("div", { className: "absolute inset-x-2 top-2 flex items-start", children: /* @__PURE__ */ jsx("span", { className: "inline-flex h-6 max-w-full items-center gap-1 rounded-full bg-black/55 px-2 text-[11px] font-semibold text-white", children: tagLabel }) })
44
+ tagLabel && /* @__PURE__ */ jsx("div", { className: "absolute inset-x-2 top-2 flex items-start", children: /* @__PURE__ */ jsx("span", { className: "inline-flex h-6 max-w-full items-center gap-1 rounded-full bg-black/55 px-2 text-[11px] font-semibold text-white", children: tagLabel }) }),
45
+ posterOverlay
44
46
  ] }),
45
47
  /* @__PURE__ */ jsxs("div", { className: "px-0.5 pt-2.5", children: [
46
48
  /* @__PURE__ */ jsx("h3", { className: "text-[13.5px] font-bold leading-[1.3]", children: title }),
@@ -0,0 +1,15 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { ComponentPropsWithoutRef, ReactNode } from 'react';
3
+
4
+ type FilterOptionChipProps = Omit<ComponentPropsWithoutRef<"input">, "type" | "children" | "size"> & {
5
+ label: string;
6
+ /** Radios in one group must share a name. */
7
+ type?: "checkbox" | "radio";
8
+ /** Omit while counts are unresolved. Zero is a valid, selectable count. */
9
+ count?: number | null;
10
+ icon?: ReactNode;
11
+ };
12
+ /** A wrapping filter option with native selection and keyboard semantics. */
13
+ declare function FilterOptionChip({ label, type, count, icon, className, id, disabled, ...input }: FilterOptionChipProps): react_jsx_runtime.JSX.Element;
14
+
15
+ export { FilterOptionChip, type FilterOptionChipProps };
@@ -0,0 +1,3 @@
1
+ export { FilterOptionChip } from '../chunk-RKNPPYRU.js';
2
+ import '../chunk-FEK5XGZW.js';
3
+ import '../chunk-MMUBH76A.js';
@@ -0,0 +1,37 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { ReactNode } from 'react';
3
+
4
+ type FilterOptionValue = string | number;
5
+ type FilterOptionGroupProps = {
6
+ title: string;
7
+ options: {
8
+ value: FilterOptionValue;
9
+ label: string;
10
+ count?: number | null;
11
+ icon?: ReactNode;
12
+ section?: string;
13
+ }[];
14
+ value: FilterOptionValue[];
15
+ onValueChange: (value: FilterOptionValue[]) => void;
16
+ selection?: "multiple" | "single";
17
+ name?: string;
18
+ defaultOpen?: boolean;
19
+ collapsible?: boolean;
20
+ visibleLimit?: number;
21
+ showAllLabel?: string | ((count: number) => string);
22
+ showLessLabel?: string;
23
+ /** Extra controls or a selection explanation, before the options. */
24
+ children?: ReactNode;
25
+ footer?: ReactNode;
26
+ clearLabel?: string;
27
+ onClear?: () => void;
28
+ /** Omit neutral choices from the active summary without changing radio selection. */
29
+ summaryValues?: FilterOptionValue[];
30
+ className?: string;
31
+ divider?: boolean;
32
+ disabled?: boolean;
33
+ };
34
+ /** Presentation only: callers own filter rules, URL state, counts and translations. */
35
+ declare function FilterOptionGroup({ title, options, value, onValueChange, selection, name, defaultOpen, collapsible, visibleLimit, showAllLabel, showLessLabel, children, footer, clearLabel, onClear, summaryValues, className, divider, disabled, }: FilterOptionGroupProps): react_jsx_runtime.JSX.Element;
36
+
37
+ export { FilterOptionGroup, type FilterOptionGroupProps, type FilterOptionValue };
@@ -0,0 +1,10 @@
1
+ export { FilterOptionGroup } from '../chunk-GIJGVFGC.js';
2
+ import '../chunk-RKNPPYRU.js';
3
+ import '../chunk-NWZGSLPU.js';
4
+ import '../chunk-GHKTVHQG.js';
5
+ import '../chunk-RIH2IS7X.js';
6
+ import '../chunk-5YNIQ2BL.js';
7
+ import '../chunk-ZLMRVBD7.js';
8
+ import '../chunk-RYPPN2GZ.js';
9
+ import '../chunk-FEK5XGZW.js';
10
+ import '../chunk-MMUBH76A.js';
@@ -0,0 +1,15 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+
3
+ type FilterPanelFooterProps = {
4
+ resetLabel: string;
5
+ resultsLabel: string;
6
+ onReset: () => void;
7
+ onResults: () => void;
8
+ canReset: boolean;
9
+ pending?: boolean;
10
+ resetHint?: string;
11
+ };
12
+ /** Persistent actions belong outside the panel's scroll container. */
13
+ declare function FilterPanelFooter({ resetLabel, resultsLabel, onReset, onResults, canReset, pending, resetHint, }: FilterPanelFooterProps): react_jsx_runtime.JSX.Element;
14
+
15
+ export { FilterPanelFooter, type FilterPanelFooterProps };
@@ -0,0 +1,63 @@
1
+ import { Button } from '../chunk-GHKTVHQG.js';
2
+ import '../chunk-RIH2IS7X.js';
3
+ import '../chunk-5YNIQ2BL.js';
4
+ import { Text } from '../chunk-ZLMRVBD7.js';
5
+ import '../chunk-FEK5XGZW.js';
6
+ import '../chunk-MMUBH76A.js';
7
+ import { jsxs, jsx } from 'react/jsx-runtime';
8
+
9
+ function FilterPanelFooter({
10
+ resetLabel,
11
+ resultsLabel,
12
+ onReset,
13
+ onResults,
14
+ canReset,
15
+ pending,
16
+ resetHint
17
+ }) {
18
+ return /* @__PURE__ */ jsxs(
19
+ "footer",
20
+ {
21
+ className: "shrink-0 border-t border-border bg-card px-4 pt-3 pb-[max(1rem,env(safe-area-inset-bottom))]",
22
+ "data-slot": "filter-panel-footer",
23
+ children: [
24
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-2", children: [
25
+ /* @__PURE__ */ jsx(
26
+ Button,
27
+ {
28
+ type: "button",
29
+ variant: "outline",
30
+ className: "min-h-11 h-auto flex-1 whitespace-normal py-2",
31
+ disabled: !canReset,
32
+ onClick: onReset,
33
+ children: resetLabel
34
+ }
35
+ ),
36
+ /* @__PURE__ */ jsx(
37
+ Button,
38
+ {
39
+ type: "button",
40
+ className: "min-h-11 h-auto flex-[2] whitespace-normal py-2",
41
+ disabled: pending,
42
+ "aria-busy": pending || void 0,
43
+ onClick: onResults,
44
+ children: resultsLabel
45
+ }
46
+ )
47
+ ] }),
48
+ resetHint && /* @__PURE__ */ jsx(
49
+ Text,
50
+ {
51
+ as: "p",
52
+ variant: "caption",
53
+ color: "muted",
54
+ className: "mt-2 leading-relaxed",
55
+ children: resetHint
56
+ }
57
+ )
58
+ ]
59
+ }
60
+ );
61
+ }
62
+
63
+ export { FilterPanelFooter };
@@ -83,9 +83,15 @@ type KitchenDossierProps = {
83
83
  partDurationMs?: number;
84
84
  /** The foot's action — pass a rendered link or button. */
85
85
  action?: ReactNode;
86
+ /**
87
+ * Drawn in place of the open-until pill's dot, in the dot's slot (roughly
88
+ * 16-22px square). Decorative and `aria-hidden`; only rendered with
89
+ * `labels.openUntil`.
90
+ */
91
+ openIndicator?: ReactNode;
86
92
  linkComponent?: MenuLinkComponent;
87
93
  className?: string;
88
94
  };
89
- declare function KitchenDossier({ place, href, meta, signature, quotes, grade, labels, formatPrice, formatScore, partDurationMs, action, linkComponent: LinkComponent, className, }: KitchenDossierProps): react_jsx_runtime.JSX.Element;
95
+ declare function KitchenDossier({ place, href, meta, signature, quotes, grade, labels, formatPrice, formatScore, partDurationMs, action, openIndicator, linkComponent: LinkComponent, className, }: KitchenDossierProps): react_jsx_runtime.JSX.Element;
90
96
 
91
97
  export { KitchenDossier, type KitchenDossierLabels, type KitchenDossierLinkProps, type KitchenDossierProps };
@@ -32,6 +32,7 @@ function KitchenDossier({
32
32
  formatScore,
33
33
  partDurationMs,
34
34
  action,
35
+ openIndicator,
35
36
  linkComponent: LinkComponent = DefaultLink,
36
37
  className
37
38
  }) {
@@ -129,7 +130,7 @@ function KitchenDossier({
129
130
  }
130
131
  ),
131
132
  /* @__PURE__ */ jsx("div", { className: "relative min-h-[184px] flex-1", children: labels.openUntil && /* @__PURE__ */ jsxs("span", { className: "absolute left-0 top-3 inline-flex h-[30px] items-center gap-[7px] rounded-full bg-[oklch(0.16_0.012_55_/_0.78)] px-3 text-[11.5px] font-bold text-white", children: [
132
- /* @__PURE__ */ jsx("i", { className: "h-[7px] w-[7px] rounded-full bg-success", "aria-hidden": true }),
133
+ openIndicator ? /* @__PURE__ */ jsx("span", { className: "flex shrink-0 items-center justify-center", "aria-hidden": true, children: openIndicator }) : /* @__PURE__ */ jsx("i", { className: "h-[7px] w-[7px] rounded-full bg-success", "aria-hidden": true }),
133
134
  labels.openUntil
134
135
  ] }) }),
135
136
  /* @__PURE__ */ jsxs("div", { className: "relative flex flex-col gap-3.5", children: [
@@ -64,8 +64,14 @@ type MenuHubHeroProps = {
64
64
  * `h2` so the document has one h1, not two.
65
65
  */
66
66
  headingLevel?: "h1" | "h2";
67
+ /**
68
+ * A decorative layer over the photographs and their scrim, under the text:
69
+ * fill the banner with `absolute inset-0`. Clipped by the banner's rounded
70
+ * edge. Decorative and `aria-hidden`.
71
+ */
72
+ sceneOverlay?: React.ReactNode;
67
73
  className?: string;
68
74
  };
69
- declare function MenuHubHero({ kicker, kickerIcon, headline, body, figures, proof, proofIcon, action, backgroundUrl, tileUrls, headingLevel: Heading, className, }: MenuHubHeroProps): react_jsx_runtime.JSX.Element;
75
+ declare function MenuHubHero({ kicker, kickerIcon, headline, body, figures, proof, proofIcon, action, backgroundUrl, tileUrls, headingLevel: Heading, sceneOverlay, className, }: MenuHubHeroProps): react_jsx_runtime.JSX.Element;
70
76
 
71
77
  export { MenuHubHero, type MenuHubHeroFigure, type MenuHubHeroProps };
@@ -16,6 +16,7 @@ function MenuHubHero({
16
16
  backgroundUrl,
17
17
  tileUrls,
18
18
  headingLevel: Heading = "h1",
19
+ sceneOverlay,
19
20
  className
20
21
  }) {
21
22
  return /* @__PURE__ */ jsxs(
@@ -81,6 +82,7 @@ function MenuHubHero({
81
82
  )
82
83
  }
83
84
  ),
85
+ sceneOverlay && /* @__PURE__ */ jsx("div", { "aria-hidden": true, className: "pointer-events-none absolute inset-0", children: sceneOverlay }),
84
86
  /* @__PURE__ */ jsxs("div", { className: "relative z-[1] flex w-full flex-col justify-center gap-3 p-5 text-white lg:gap-4 lg:px-12 lg:py-11", children: [
85
87
  kicker && /* @__PURE__ */ jsxs("span", { className: "inline-flex w-max items-center gap-[7px] rounded-full bg-white/15 px-3 py-1.5 text-[11px] font-extrabold tracking-[0.11em]", children: [
86
88
  kickerIcon,
@@ -81,6 +81,13 @@ type PlaceCardProps = {
81
81
  * trending badge on the API's `trending` flag).
82
82
  */
83
83
  photoOverlay?: ReactNode;
84
+ /**
85
+ * Drawn in place of the green open-now dot, in the dot's top-right slot
86
+ * (roughly 16-20px square). Decorative: it is only rendered when
87
+ * `place.open_now === true` and should be `aria-hidden`. The closed badge is
88
+ * untouched.
89
+ */
90
+ openIndicator?: ReactNode;
84
91
  className?: string;
85
92
  };
86
93
  declare const PlaceCard: React$1.NamedExoticComponent<PlaceCardProps>;
@@ -1,6 +1,6 @@
1
+ import { HOVER_IMAGE_MOTION, HOVER_IMAGE_WRAPPER, HOVER_MOTION, HOVER_LIFT_GRID_GROUP } from '../chunk-XCB3F6SF.js';
1
2
  import { NEUTRAL_ICON_FILTER } from '../chunk-LQWGZIYA.js';
2
3
  import { OVERLAY_PILL } from '../chunk-EZG2NELX.js';
3
- import { HOVER_IMAGE_MOTION, HOVER_IMAGE_WRAPPER, HOVER_MOTION, HOVER_LIFT_GRID_GROUP } from '../chunk-XCB3F6SF.js';
4
4
  import { GradeAtom } from '../chunk-YE4ZQUTI.js';
5
5
  import { showsGradeAtom } from '../chunk-I5V446BX.js';
6
6
  import { Separator } from '../chunk-NWZGSLPU.js';
@@ -74,6 +74,7 @@ var PlaceCard = memo(function PlaceCard2({
74
74
  onOpen,
75
75
  footer,
76
76
  photoOverlay,
77
+ openIndicator,
77
78
  className
78
79
  }) {
79
80
  var _a, _b, _c, _d, _e, _f, _g, _h, _i;
@@ -132,13 +133,20 @@ var PlaceCard = memo(function PlaceCard2({
132
133
  }
133
134
  ) })
134
135
  ),
135
- place.open_now === true && /* @__PURE__ */ jsx(
136
+ place.open_now === true && (openIndicator ? /* @__PURE__ */ jsx(
137
+ "span",
138
+ {
139
+ className: "pointer-events-none absolute right-2 top-2 flex items-center justify-center",
140
+ "aria-hidden": true,
141
+ children: openIndicator
142
+ }
143
+ ) : /* @__PURE__ */ jsx(
136
144
  "span",
137
145
  {
138
146
  className: "absolute right-2.5 top-2.5 h-2 w-2 rounded-full bg-green-500 ring-2 ring-white/70",
139
147
  "aria-hidden": true
140
148
  }
141
- ),
149
+ )),
142
150
  place.open_now === false && /* `Badge variant="overlay"` + the shared cover geometry: this pill and
143
151
  the caller's `photoOverlay` sit on the same photo and must match. */
144
152
  /* @__PURE__ */ jsx(Badge, { variant: "overlay", className: cn(OVERLAY_PILL, "absolute right-2 top-2"), children: labels.closed })
@@ -50,6 +50,8 @@ type PosterStackPoster = {
50
50
  caption?: string;
51
51
  /** Rendered when `src` is absent or fails. Defaults to `<EventPosterFallback />`. */
52
52
  fallback?: React.ReactNode;
53
+ /** Decorative overlay on the poster face (it tilts and floats with the poster): an absolutely positioned, `aria-hidden` ornament. */
54
+ overlay?: React.ReactNode;
53
55
  /** Load now at high priority instead of lazily. For the poster that is the
54
56
  * page's LCP element; every other poster stays lazy. */
55
57
  eager?: boolean;
@@ -96,7 +96,8 @@ function PosterStack({
96
96
  }
97
97
  )
98
98
  ) : (_a = poster.fallback) != null ? _a : /* @__PURE__ */ jsx(EventPosterFallback, {}),
99
- poster.caption && /* @__PURE__ */ jsx("span", { className: "absolute inset-x-0 bottom-0 bg-gradient-to-t from-black/70 to-transparent px-2 pb-2 pt-6", children: /* @__PURE__ */ jsx("span", { className: "line-clamp-2 text-[11px] font-semibold leading-[1.25] text-white md:text-[12.5px]", children: poster.caption }) })
99
+ poster.caption && /* @__PURE__ */ jsx("span", { className: "absolute inset-x-0 bottom-0 bg-gradient-to-t from-black/70 to-transparent px-2 pb-2 pt-6", children: /* @__PURE__ */ jsx("span", { className: "line-clamp-2 text-[11px] font-semibold leading-[1.25] text-white md:text-[12.5px]", children: poster.caption }) }),
100
+ poster.overlay
100
101
  ] });
101
102
  return /* @__PURE__ */ jsx(
102
103
  "div",
@@ -56,9 +56,14 @@ type SectionHeaderProps = {
56
56
  * behaviour; it only holds the slot.
57
57
  */
58
58
  trailing?: React.ReactNode;
59
+ /**
60
+ * Absolute decoration anchored to the title group's own box. Takes no
61
+ * layout; the group is `relative` for it. Decorative and `aria-hidden`.
62
+ */
63
+ titleAdornment?: React.ReactNode;
59
64
  className?: string;
60
65
  };
61
- declare function SectionHeader({ title, count, subtitle, action, actionHref, actionTone, subtitleFrom, linkComponent: LinkComponent, onAction, as: Heading, trailing, className, }: SectionHeaderProps): react_jsx_runtime.JSX.Element;
66
+ declare function SectionHeader({ title, count, subtitle, action, actionHref, actionTone, subtitleFrom, linkComponent: LinkComponent, onAction, as: Heading, trailing, titleAdornment, className, }: SectionHeaderProps): react_jsx_runtime.JSX.Element;
62
67
  type SectionHeaderSkeletonProps = {
63
68
  /** Shows the inline count placeholder, matching `SectionHeaderProps["count"]`. */
64
69
  showCount?: boolean;
@@ -32,15 +32,17 @@ function SectionHeader({
32
32
  onAction,
33
33
  as: Heading = "h2",
34
34
  trailing,
35
+ titleAdornment,
35
36
  className
36
37
  }) {
37
38
  const actionIcon = /* @__PURE__ */ jsx(ChevronRight, { className: "h-3.5 w-3.5", "aria-hidden": true });
38
39
  const actionClasses = cn(ACTION_BASE_CLASSES, ACTION_TONE_CLASSES[actionTone]);
39
40
  return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col mb-3.5", className), children: [
40
41
  /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2", children: [
41
- /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-baseline", children: [
42
+ /* @__PURE__ */ jsxs("div", { className: "relative flex min-w-0 items-baseline", children: [
42
43
  /* @__PURE__ */ jsx(Heading, { className: "truncate text-[17px] font-bold tracking-[-0.01em] text-foreground md:text-[18px]", children: title }),
43
- 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 })
44
+ 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 }),
45
+ titleAdornment
44
46
  ] }),
45
47
  (action || trailing) && /* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center gap-3", children: [
46
48
  action && (actionHref ? /* @__PURE__ */ jsxs(LinkComponent, { href: actionHref, className: actionClasses, onClick: onAction, children: [
@@ -1,4 +1,4 @@
1
- import { TimelineHourSkeleton } from '../chunk-S5GDARLW.js';
1
+ import { TimelineHourSkeleton } from '../chunk-WY2FP76J.js';
2
2
  import '../chunk-BRXI6EDO.js';
3
3
  import { Separator } from '../chunk-NWZGSLPU.js';
4
4
  import { Skeleton } from '../chunk-XBBEX4SF.js';