@bearmenu/ui 0.8.24 → 0.8.25

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 (89) hide show
  1. package/dist/{chunk-5LTILG3L.js → chunk-5HHRVJOT.js} +14 -1
  2. package/dist/{chunk-SDWN6L4O.js → chunk-AHHDC7ML.js} +1 -1
  3. package/dist/{chunk-I4RUOAU7.js → chunk-AHL45DD2.js} +11 -38
  4. package/dist/{chunk-5XREWOBB.js → chunk-IEIJP73R.js} +14 -1
  5. package/dist/{chunk-BKKQ7GCZ.js → chunk-QG6I2ILT.js} +1 -1
  6. package/dist/{chunk-TIQNM4XD.js → chunk-VEGFQSZA.js} +17 -1
  7. package/dist/components/alert-dialog.js +1 -1
  8. package/dist/components/badge.d.ts +1 -1
  9. package/dist/components/badge.js +1 -1
  10. package/dist/components/booking-row.d.ts +60 -0
  11. package/dist/components/booking-row.js +103 -0
  12. package/dist/components/booking-summary-card.d.ts +38 -0
  13. package/dist/components/booking-summary-card.js +67 -0
  14. package/dist/components/button.d.ts +2 -2
  15. package/dist/components/button.js +1 -1
  16. package/dist/components/calendar.d.ts +17 -3
  17. package/dist/components/calendar.js +100 -12
  18. package/dist/components/carousel.js +1 -1
  19. package/dist/components/coach-marks.js +1 -1
  20. package/dist/components/combobox.js +1 -1
  21. package/dist/components/concept-convergence.js +2 -2
  22. package/dist/components/drift-rail.d.ts +2 -29
  23. package/dist/components/drift-rail.js +1 -3
  24. package/dist/components/empty-state.js +1 -1
  25. package/dist/components/event-card.js +1 -1
  26. package/dist/components/event-compact-row.js +1 -1
  27. package/dist/components/event-detail-panel.js +1 -1
  28. package/dist/components/event-list-row-wide.js +2 -2
  29. package/dist/components/event-list-row.js +1 -1
  30. package/dist/components/filter-category-row.js +1 -1
  31. package/dist/components/hold-countdown.d.ts +43 -0
  32. package/dist/components/hold-countdown.js +90 -0
  33. package/dist/components/kitchen-dossier.js +1 -1
  34. package/dist/components/link-button.js +1 -1
  35. package/dist/components/menu-family-block.js +1 -1
  36. package/dist/components/menu-index-list.js +1 -1
  37. package/dist/components/menu-item.js +3 -3
  38. package/dist/components/motion-icon-set.d.ts +2 -1
  39. package/dist/components/motion-icon-set.js +27 -1
  40. package/dist/components/multi-select-combobox.js +2 -2
  41. package/dist/components/pagination.js +1 -1
  42. package/dist/components/party-counter.d.ts +36 -0
  43. package/dist/components/party-counter.js +61 -0
  44. package/dist/components/place-card.js +2 -2
  45. package/dist/components/place-details-mobile.js +1 -1
  46. package/dist/components/poster-tape.d.ts +2 -15
  47. package/dist/components/poster-tape.js +3 -12
  48. package/dist/components/searchable-select.js +1 -1
  49. package/dist/components/signature-chip.js +2 -2
  50. package/dist/components/sliding-panels.js +1 -1
  51. package/dist/components/time-slot.d.ts +73 -0
  52. package/dist/components/time-slot.js +106 -0
  53. package/dist/components/toggle-group.js +15 -2
  54. package/dist/components/toggle.d.ts +1 -1
  55. package/dist/components/toggle.js +1 -1
  56. package/dist/components/top-ranking-entry.js +3 -3
  57. package/dist/components/weekday-chip.d.ts +33 -0
  58. package/dist/components/weekday-chip.js +48 -0
  59. package/package.json +1 -1
  60. package/src/components/badge.stories.tsx +17 -0
  61. package/src/components/badge.tsx +15 -0
  62. package/src/components/booking-primitives.test.tsx +180 -0
  63. package/src/components/booking-row.stories.tsx +66 -0
  64. package/src/components/booking-row.tsx +168 -0
  65. package/src/components/booking-summary-card.stories.tsx +41 -0
  66. package/src/components/booking-summary-card.tsx +109 -0
  67. package/src/components/button.stories.tsx +23 -0
  68. package/src/components/button.tsx +13 -0
  69. package/src/components/calendar.stories.tsx +27 -0
  70. package/src/components/calendar.tsx +110 -3
  71. package/src/components/drift-rail.stories.tsx +1 -29
  72. package/src/components/drift-rail.tsx +4 -71
  73. package/src/components/hold-countdown.stories.tsx +68 -0
  74. package/src/components/hold-countdown.tsx +140 -0
  75. package/src/components/motion-icon-set.stories.tsx +2 -0
  76. package/src/components/motion-icon-set.tsx +42 -0
  77. package/src/components/party-counter.stories.tsx +57 -0
  78. package/src/components/party-counter.tsx +93 -0
  79. package/src/components/poster-tape.stories.tsx +0 -29
  80. package/src/components/poster-tape.tsx +5 -21
  81. package/src/components/time-slot.stories.tsx +85 -0
  82. package/src/components/time-slot.tsx +186 -0
  83. package/src/components/toggle-group.stories.tsx +27 -0
  84. package/src/components/toggle-group.tsx +14 -1
  85. package/src/components/toggle.tsx +16 -0
  86. package/src/components/weekday-chip.stories.tsx +54 -0
  87. package/src/components/weekday-chip.tsx +75 -0
  88. package/src/components/drift-rail.test.tsx +0 -125
  89. package/src/components/poster-tape.test.tsx +0 -42
@@ -1,4 +1,4 @@
1
- import { buttonVariants, Button } from '../chunk-5LTILG3L.js';
1
+ import { buttonVariants, Button } from '../chunk-5HHRVJOT.js';
2
2
  import '../chunk-RIH2IS7X.js';
3
3
  import '../chunk-5YNIQ2BL.js';
4
4
  import { cn } from '../chunk-FEK5XGZW.js';
@@ -8,29 +8,80 @@ import { ChevronLeftIcon, ChevronRightIcon, ChevronDownIcon } from 'lucide-react
8
8
  import { getDefaultClassNames, DayPicker } from 'react-day-picker';
9
9
  import { jsx } from 'react/jsx-runtime';
10
10
 
11
+ var BOOKING_CLASSNAMES = {
12
+ root: "w-full",
13
+ months: "relative flex flex-col",
14
+ month: "flex w-full flex-col",
15
+ // The caption row is 15px with 7px under it; the chevrons sit in that row.
16
+ nav: "absolute inset-x-0 top-0 flex h-[15px] w-full items-center justify-between",
17
+ button_previous: "grid size-6 -m-[4.5px] cursor-pointer place-items-center rounded-full text-muted-foreground hover:bg-surface-2 hover:text-foreground aria-disabled:pointer-events-none aria-disabled:opacity-30 [&_svg]:size-3.5",
18
+ button_next: "grid size-6 -m-[4.5px] cursor-pointer place-items-center rounded-full text-muted-foreground hover:bg-surface-2 hover:text-foreground aria-disabled:pointer-events-none aria-disabled:opacity-30 [&_svg]:size-3.5",
19
+ month_caption: "mb-[7px] flex h-[15px] w-full items-center justify-center",
20
+ caption_label: "text-[12.5px] leading-[15px] font-bold text-foreground select-none",
21
+ month_grid: "w-full border-collapse",
22
+ weekdays: "grid grid-cols-7 gap-[3px]",
23
+ weekday: "pb-[3px] text-center text-[9.5px] font-bold text-muted-foreground select-none",
24
+ week: "mt-[3px] grid grid-cols-7 gap-[3px]",
25
+ day: "p-0 text-center select-none",
26
+ today: "",
27
+ outside: "invisible",
28
+ disabled: "",
29
+ hidden: "invisible"
30
+ };
11
31
  function Calendar(_a) {
12
32
  var _b = _a, {
13
33
  className,
14
34
  classNames,
15
- showOutsideDays = true,
35
+ showOutsideDays,
16
36
  captionLayout = "label",
17
37
  buttonVariant = "ghost",
38
+ appearance = "default",
18
39
  formatters,
19
- components
40
+ components,
41
+ modifiersClassNames
20
42
  } = _b, props = __objRest(_b, [
21
43
  "className",
22
44
  "classNames",
23
45
  "showOutsideDays",
24
46
  "captionLayout",
25
47
  "buttonVariant",
48
+ "appearance",
26
49
  "formatters",
27
- "components"
50
+ "components",
51
+ "modifiersClassNames"
28
52
  ]);
29
53
  const defaultClassNames = getDefaultClassNames();
54
+ if (appearance === "booking") {
55
+ return /* @__PURE__ */ jsx(
56
+ DayPicker,
57
+ __spreadValues({
58
+ showOutsideDays: showOutsideDays != null ? showOutsideDays : false,
59
+ className: cn("group/calendar w-full", className),
60
+ captionLayout: "label",
61
+ formatters: __spreadValues({
62
+ formatWeekdayName: (date) => date.toLocaleString("default", { weekday: "narrow" })
63
+ }, formatters),
64
+ classNames: __spreadValues(__spreadValues({}, BOOKING_CLASSNAMES), classNames),
65
+ modifiersClassNames: __spreadValues({ closed: "is-closed" }, modifiersClassNames),
66
+ components: __spreadValues({
67
+ Root: (_a2) => {
68
+ var _b2 = _a2, { className: className2, rootRef } = _b2, rootProps = __objRest(_b2, ["className", "rootRef"]);
69
+ return /* @__PURE__ */ jsx("div", __spreadValues({ "data-slot": "calendar", "data-appearance": "booking", ref: rootRef, className: cn(className2) }, rootProps));
70
+ },
71
+ Chevron: (_c) => {
72
+ var _d = _c, { className: className2, orientation } = _d, chevronProps = __objRest(_d, ["className", "orientation"]);
73
+ return orientation === "left" ? /* @__PURE__ */ jsx(ChevronLeftIcon, __spreadValues({ className: cn("size-4", className2) }, chevronProps)) : /* @__PURE__ */ jsx(ChevronRightIcon, __spreadValues({ className: cn("size-4", className2) }, chevronProps));
74
+ },
75
+ DayButton: BookingDayButton
76
+ }, components)
77
+ }, props)
78
+ );
79
+ }
30
80
  return /* @__PURE__ */ jsx(
31
81
  DayPicker,
32
82
  __spreadValues({
33
- showOutsideDays,
83
+ showOutsideDays: showOutsideDays != null ? showOutsideDays : true,
84
+ modifiersClassNames,
34
85
  className: cn(
35
86
  "group/calendar bg-background p-3 [--cell-size:--spacing(9)] [[data-slot=card-content]_&]:bg-transparent [[data-slot=popover-content]_&]:bg-transparent",
36
87
  String.raw`rtl:**:[.rdp-button\_next>svg]:rotate-180`,
@@ -109,12 +160,12 @@ function Calendar(_a) {
109
160
  hidden: cn("invisible", defaultClassNames.hidden)
110
161
  }, classNames),
111
162
  components: __spreadValues({
112
- Root: (_a2) => {
113
- var _b2 = _a2, { className: className2, rootRef } = _b2, props2 = __objRest(_b2, ["className", "rootRef"]);
163
+ Root: (_e) => {
164
+ var _f = _e, { className: className2, rootRef } = _f, props2 = __objRest(_f, ["className", "rootRef"]);
114
165
  return /* @__PURE__ */ jsx("div", __spreadValues({ "data-slot": "calendar", ref: rootRef, className: cn(className2) }, props2));
115
166
  },
116
- Chevron: (_c) => {
117
- var _d = _c, { className: className2, orientation } = _d, props2 = __objRest(_d, ["className", "orientation"]);
167
+ Chevron: (_g) => {
168
+ var _h = _g, { className: className2, orientation } = _h, props2 = __objRest(_h, ["className", "orientation"]);
118
169
  if (orientation === "left") {
119
170
  return /* @__PURE__ */ jsx(ChevronLeftIcon, __spreadValues({ className: cn("size-4", className2) }, props2));
120
171
  }
@@ -124,8 +175,8 @@ function Calendar(_a) {
124
175
  return /* @__PURE__ */ jsx(ChevronDownIcon, __spreadValues({ className: cn("size-4", className2) }, props2));
125
176
  },
126
177
  DayButton: CalendarDayButton,
127
- WeekNumber: (_e) => {
128
- var _f = _e, { children } = _f, props2 = __objRest(_f, ["children"]);
178
+ WeekNumber: (_i) => {
179
+ var _j = _i, { children } = _j, props2 = __objRest(_j, ["children"]);
129
180
  return /* @__PURE__ */ jsx("td", __spreadProps(__spreadValues({}, props2), { children: /* @__PURE__ */ jsx("div", { className: "flex size-(--cell-size) items-center justify-center text-center", children }) }));
130
181
  }
131
182
  }, components)
@@ -167,5 +218,42 @@ function CalendarDayButton(_a) {
167
218
  }, props)
168
219
  );
169
220
  }
221
+ function BookingDayButton(_a) {
222
+ var _b = _a, {
223
+ className,
224
+ day,
225
+ modifiers
226
+ } = _b, props = __objRest(_b, [
227
+ "className",
228
+ "day",
229
+ "modifiers"
230
+ ]);
231
+ const ref = React.useRef(null);
232
+ React.useEffect(() => {
233
+ var _a2;
234
+ if (modifiers.focused) (_a2 = ref.current) == null ? void 0 : _a2.focus();
235
+ }, [modifiers.focused]);
236
+ const closed = Boolean(modifiers.closed);
237
+ return /* @__PURE__ */ jsx(
238
+ "button",
239
+ __spreadValues({
240
+ ref,
241
+ type: "button",
242
+ "data-day": day.date.toLocaleDateString(),
243
+ "data-selected": modifiers.selected || void 0,
244
+ "data-closed": closed || void 0,
245
+ className: cn(
246
+ "grid h-8 w-full cursor-pointer place-items-center rounded-full font-mono text-[12.5px] leading-none font-medium text-ink-secondary tabular-nums",
247
+ "transition-colors duration-150 hover:bg-surface-2 motion-reduce:transition-none",
248
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background",
249
+ modifiers.selected && "bg-[image:var(--gradient-primary)] font-bold text-white hover:bg-transparent hover:brightness-[0.94]",
250
+ modifiers.disabled && !closed && "cursor-not-allowed opacity-30 hover:bg-transparent",
251
+ // A closed day is also disabled; it reads as closed, not as past.
252
+ closed && "cursor-not-allowed text-[oklch(0.7_0_0)] line-through hover:bg-transparent dark:text-ink-tertiary",
253
+ className
254
+ )
255
+ }, props)
256
+ );
257
+ }
170
258
 
171
- export { Calendar, CalendarDayButton };
259
+ export { BookingDayButton, Calendar, CalendarDayButton };
@@ -1,4 +1,4 @@
1
- import { Button } from '../chunk-5LTILG3L.js';
1
+ import { Button } from '../chunk-5HHRVJOT.js';
2
2
  import '../chunk-RIH2IS7X.js';
3
3
  import '../chunk-5YNIQ2BL.js';
4
4
  import { cn } from '../chunk-FEK5XGZW.js';
@@ -1,4 +1,4 @@
1
- import { Button } from '../chunk-5LTILG3L.js';
1
+ import { Button } from '../chunk-5HHRVJOT.js';
2
2
  import '../chunk-RIH2IS7X.js';
3
3
  import '../chunk-5YNIQ2BL.js';
4
4
  import { cn } from '../chunk-FEK5XGZW.js';
@@ -1,7 +1,7 @@
1
1
  import { Popover, PopoverTrigger, PopoverContent } from '../chunk-2ADCZXZJ.js';
2
2
  import { Command, CommandInput, CommandList, CommandLoading, CommandEmpty, CommandGroup, CommandItem } from '../chunk-MCOHLWRS.js';
3
3
  import '../chunk-PDTPO6NG.js';
4
- import { Button } from '../chunk-5LTILG3L.js';
4
+ import { Button } from '../chunk-5HHRVJOT.js';
5
5
  import '../chunk-RIH2IS7X.js';
6
6
  import '../chunk-5YNIQ2BL.js';
7
7
  import { cn } from '../chunk-FEK5XGZW.js';
@@ -1,7 +1,7 @@
1
- import { PhotoGround } from '../chunk-SWD6CJ35.js';
2
1
  import { Convergence } from '../chunk-4XDJXMLF.js';
3
2
  import '../chunk-OIV7B44M.js';
4
- import { Badge } from '../chunk-5XREWOBB.js';
3
+ import { PhotoGround } from '../chunk-SWD6CJ35.js';
4
+ import { Badge } from '../chunk-IEIJP73R.js';
5
5
  import { cn } from '../chunk-FEK5XGZW.js';
6
6
  import '../chunk-MMUBH76A.js';
7
7
  import { jsxs, jsx } from 'react/jsx-runtime';
@@ -26,28 +26,7 @@ import * as react_jsx_runtime from 'react/jsx-runtime';
26
26
  * work nobody can see. `prefers-reduced-motion` removes the drift entirely;
27
27
  * the rail stays scrollable by hand, which is more than the static strip it
28
28
  * used to collapse to.
29
- *
30
- * THE READER CAN STOP IT FOR GOOD. Hover and touch are holds; a rail that
31
- * drifts for longer than five seconds also needs a control the reader can
32
- * press (WCAG 2.2.2), and a low-vision reader who cannot follow the motion
33
- * needs it to stay stopped. Pass `labels` and the rail renders a pause/play
34
- * toggle under its right edge — the same latch, glyph and 44px target as
35
- * `WeekendPanel`'s, so the hub's rails all answer to one control. It is not
36
- * rendered under `prefers-reduced-motion`, where there is nothing to stop.
37
- *
38
- * THE DUPLICATE IS INERT. The second copy is what makes the loop seamless,
39
- * but its links are the same links again: `aria-hidden` kept them out of the
40
- * accessibility tree and left them in the tab order, so a keyboard reader
41
- * tabbed through every item twice. `inert` removes them from focus and
42
- * hit-testing as well.
43
29
  */
44
- /** Accessible names for the pause/play toggle, injected so the DS owns no copy. */
45
- type DriftRailLabels = {
46
- /** Name announced while the rail drifts (the button pauses it). */
47
- pause: string;
48
- /** Name announced while the rail is paused (the button resumes it). */
49
- play: string;
50
- };
51
30
  type DriftRailProps = {
52
31
  children: React.ReactNode;
53
32
  /** Runs the loop right-to-left instead of left-to-right. */
@@ -56,14 +35,8 @@ type DriftRailProps = {
56
35
  durationSeconds?: number;
57
36
  /** Pauses the loop while the pointer hovers the rail. */
58
37
  pauseOnHover?: boolean;
59
- /**
60
- * Accessible names for the pause/play toggle. When given, the toggle
61
- * renders whenever the rail drifts (never under `prefers-reduced-motion`);
62
- * without it there is no control, as before.
63
- */
64
- labels?: DriftRailLabels;
65
38
  className?: string;
66
39
  };
67
- declare function DriftRail({ children, reverse, durationSeconds, pauseOnHover, labels, className, }: DriftRailProps): react_jsx_runtime.JSX.Element;
40
+ declare function DriftRail({ children, reverse, durationSeconds, pauseOnHover, className, }: DriftRailProps): react_jsx_runtime.JSX.Element;
68
41
 
69
- export { DriftRail, type DriftRailLabels, type DriftRailProps };
42
+ export { DriftRail, type DriftRailProps };
@@ -1,5 +1,3 @@
1
- export { DriftRail } from '../chunk-I4RUOAU7.js';
2
- import '../chunk-4OF4T4AI.js';
3
- import '../chunk-L3VEMP5A.js';
1
+ export { DriftRail } from '../chunk-AHL45DD2.js';
4
2
  import '../chunk-FEK5XGZW.js';
5
3
  import '../chunk-MMUBH76A.js';
@@ -1,4 +1,4 @@
1
- import { Button } from '../chunk-5LTILG3L.js';
1
+ import { Button } from '../chunk-5HHRVJOT.js';
2
2
  import '../chunk-RIH2IS7X.js';
3
3
  import '../chunk-5YNIQ2BL.js';
4
4
  import { Text } from '../chunk-ZLMRVBD7.js';
@@ -1,6 +1,6 @@
1
1
  import { TagGroup } from '../chunk-PX6RICS5.js';
2
- import { Tag } from '../chunk-MWB7S54O.js';
3
2
  import { resolveEventEntryFee } from '../chunk-DNSXMHYX.js';
3
+ import { Tag } from '../chunk-MWB7S54O.js';
4
4
  import { EventPosterFallback } from '../chunk-FDITZ2VM.js';
5
5
  import { Skeleton } from '../chunk-XBBEX4SF.js';
6
6
  import { cn } from '../chunk-FEK5XGZW.js';
@@ -1,6 +1,6 @@
1
1
  import { TagGroup } from '../chunk-PX6RICS5.js';
2
- import { Tag } from '../chunk-MWB7S54O.js';
3
2
  import { resolveEventEntryFee } from '../chunk-DNSXMHYX.js';
3
+ import { Tag } from '../chunk-MWB7S54O.js';
4
4
  import { EventPosterFallback } from '../chunk-FDITZ2VM.js';
5
5
  import { Skeleton } from '../chunk-XBBEX4SF.js';
6
6
  import { cn } from '../chunk-FEK5XGZW.js';
@@ -2,7 +2,7 @@ import { resolveEventEntryFee } from '../chunk-DNSXMHYX.js';
2
2
  import { EventPosterFallback } from '../chunk-FDITZ2VM.js';
3
3
  import { Separator } from '../chunk-NWZGSLPU.js';
4
4
  import { Skeleton } from '../chunk-XBBEX4SF.js';
5
- import { Button } from '../chunk-5LTILG3L.js';
5
+ import { Button } from '../chunk-5HHRVJOT.js';
6
6
  import '../chunk-RIH2IS7X.js';
7
7
  import '../chunk-5YNIQ2BL.js';
8
8
  import { cn } from '../chunk-FEK5XGZW.js';
@@ -1,9 +1,9 @@
1
- import { Tag } from '../chunk-MWB7S54O.js';
2
1
  import { resolveEventEntryFee } from '../chunk-DNSXMHYX.js';
2
+ import { Tag } from '../chunk-MWB7S54O.js';
3
3
  import { EventPosterFallback } from '../chunk-FDITZ2VM.js';
4
4
  import { Separator } from '../chunk-NWZGSLPU.js';
5
5
  import { Skeleton } from '../chunk-XBBEX4SF.js';
6
- import { buttonVariants } from '../chunk-5LTILG3L.js';
6
+ import { buttonVariants } from '../chunk-5HHRVJOT.js';
7
7
  import '../chunk-RIH2IS7X.js';
8
8
  import '../chunk-5YNIQ2BL.js';
9
9
  import { cn } from '../chunk-FEK5XGZW.js';
@@ -1,6 +1,6 @@
1
1
  import { TagGroup } from '../chunk-PX6RICS5.js';
2
- import { Tag } from '../chunk-MWB7S54O.js';
3
2
  import { resolveEventEntryFee } from '../chunk-DNSXMHYX.js';
3
+ import { Tag } from '../chunk-MWB7S54O.js';
4
4
  import { EventPosterFallback } from '../chunk-FDITZ2VM.js';
5
5
  import { Separator } from '../chunk-NWZGSLPU.js';
6
6
  import { Skeleton } from '../chunk-XBBEX4SF.js';
@@ -1,5 +1,5 @@
1
1
  import { Text } from '../chunk-ZLMRVBD7.js';
2
- import { Badge } from '../chunk-5XREWOBB.js';
2
+ import { Badge } from '../chunk-IEIJP73R.js';
3
3
  import '../chunk-FEK5XGZW.js';
4
4
  import '../chunk-MMUBH76A.js';
5
5
  import { ChevronRight } from 'lucide-react';
@@ -0,0 +1,43 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import * as React from 'react';
3
+
4
+ /**
5
+ * HoldCountdown — "HELD FOR YOU 09:41": how long a table offered from the
6
+ * waitlist stays reserved for this guest (the booking flow's "A table opened
7
+ * up").
8
+ *
9
+ * Measured from the booking design: a card with a warning-colour hairline,
10
+ * 14px radius, 15px inset.
11
+ * - `stacked` (mobile): centred. The label at 10.5px uppercase with 0.06em
12
+ * tracking, muted; the remaining time in mono at 34px bold; a muted
13
+ * 11.5px line under it ("then it goes to the next person").
14
+ * - `inline` (desktop): one row, 18px gaps. The label over the time at
15
+ * 30px, then the explanation at 12.5px muted taking the free width, then
16
+ * the action.
17
+ *
18
+ * Counts down from `expiresAt` once a second and calls `onExpire` once, at
19
+ * zero. It never shows a negative time.
20
+ */
21
+ type HoldCountdownLabels = {
22
+ /** "Held for you" */
23
+ held: string;
24
+ /** Stacked: "then it goes to the next person". Inline: "After the countdown the table goes to the next person on the list." */
25
+ explanation: string;
26
+ /** Spoken form for assistive tech — "9 minutes 41 seconds left". */
27
+ remaining: (minutes: number, seconds: number) => string;
28
+ };
29
+ type HoldCountdownProps = {
30
+ /** When the hold ends — ISO string or epoch milliseconds. */
31
+ expiresAt: string | number;
32
+ labels: HoldCountdownLabels;
33
+ layout?: "stacked" | "inline";
34
+ /** The inline layout's action, typically a `Button variant="cta" size="cta-sm"`. */
35
+ action?: React.ReactNode;
36
+ onExpire?: () => void;
37
+ /** Clock override for stories and tests. */
38
+ now?: () => number;
39
+ className?: string;
40
+ };
41
+ declare function HoldCountdown({ expiresAt, labels, layout, action, onExpire, now, className, }: HoldCountdownProps): react_jsx_runtime.JSX.Element;
42
+
43
+ export { HoldCountdown, type HoldCountdownLabels, type HoldCountdownProps };
@@ -0,0 +1,90 @@
1
+ import { cn } from '../chunk-FEK5XGZW.js';
2
+ import '../chunk-MMUBH76A.js';
3
+ import * as React from 'react';
4
+ import { jsx, jsxs } from 'react/jsx-runtime';
5
+
6
+ var HOLD_BORDER = "border-[oklch(0.62_0.15_70)] dark:border-warning";
7
+ function remainingSeconds(expiresAt, now) {
8
+ const end = typeof expiresAt === "number" ? expiresAt : Date.parse(expiresAt);
9
+ return Math.max(0, Math.ceil((end - now) / 1e3));
10
+ }
11
+ function HoldCountdown({
12
+ expiresAt,
13
+ labels,
14
+ layout = "stacked",
15
+ action,
16
+ onExpire,
17
+ now = Date.now,
18
+ className
19
+ }) {
20
+ const [left, setLeft] = React.useState(() => remainingSeconds(expiresAt, now()));
21
+ const expired = React.useRef(false);
22
+ const onExpireRef = React.useRef(onExpire);
23
+ onExpireRef.current = onExpire;
24
+ React.useEffect(() => {
25
+ expired.current = false;
26
+ const tick = () => {
27
+ var _a;
28
+ const next = remainingSeconds(expiresAt, now());
29
+ setLeft(next);
30
+ if (next === 0 && !expired.current) {
31
+ expired.current = true;
32
+ (_a = onExpireRef.current) == null ? void 0 : _a.call(onExpireRef);
33
+ }
34
+ };
35
+ tick();
36
+ const id = window.setInterval(tick, 1e3);
37
+ return () => window.clearInterval(id);
38
+ }, [expiresAt, now]);
39
+ const minutes = Math.floor(left / 60);
40
+ const seconds = left % 60;
41
+ const display = `${String(minutes).padStart(2, "0")}:${String(seconds).padStart(2, "0")}`;
42
+ const label = /* @__PURE__ */ jsx("div", { className: "text-[10.5px] leading-[13px] tracking-[0.06em] text-muted-foreground uppercase", children: labels.held });
43
+ const time = /* @__PURE__ */ jsx(
44
+ "div",
45
+ {
46
+ role: "timer",
47
+ "aria-label": labels.remaining(minutes, seconds),
48
+ className: cn(
49
+ "font-mono font-bold tabular-nums text-foreground",
50
+ layout === "stacked" ? "mt-[5px] text-[34px] leading-[40px]" : "text-[30px] leading-[35px]"
51
+ ),
52
+ children: display
53
+ }
54
+ );
55
+ if (layout === "inline") {
56
+ return /* @__PURE__ */ jsxs(
57
+ "div",
58
+ {
59
+ "data-slot": "hold-countdown",
60
+ className: cn(
61
+ "flex items-center gap-[18px] rounded-[14px] border bg-card p-[15px] text-left",
62
+ HOLD_BORDER,
63
+ className
64
+ ),
65
+ children: [
66
+ /* @__PURE__ */ jsxs("div", { className: "shrink-0", children: [
67
+ label,
68
+ time
69
+ ] }),
70
+ /* @__PURE__ */ jsx("p", { className: "flex-1 text-[12.5px] leading-[15px] text-muted-foreground", children: labels.explanation }),
71
+ action
72
+ ]
73
+ }
74
+ );
75
+ }
76
+ return /* @__PURE__ */ jsxs(
77
+ "div",
78
+ {
79
+ "data-slot": "hold-countdown",
80
+ className: cn("rounded-[14px] border bg-card p-[15px] text-center", HOLD_BORDER, className),
81
+ children: [
82
+ label,
83
+ time,
84
+ /* @__PURE__ */ jsx("p", { className: "mt-1 text-[11.5px] leading-[14px] text-muted-foreground", children: labels.explanation })
85
+ ]
86
+ }
87
+ );
88
+ }
89
+
90
+ export { HoldCountdown };
@@ -1,5 +1,5 @@
1
- import { DIMENSION_ORDER, VenueGradeDimensions } from '../chunk-ZQ53AGML.js';
2
1
  import { VenueQuotes } from '../chunk-43RC7MHY.js';
2
+ import { DIMENSION_ORDER, VenueGradeDimensions } from '../chunk-ZQ53AGML.js';
3
3
  import { StoryProvider, StorySegments, StoryPanel } from '../chunk-F6FMZJDG.js';
4
4
  import { GlassGround } from '../chunk-ICOU5QHT.js';
5
5
  import { Separator } from '../chunk-NWZGSLPU.js';
@@ -1,4 +1,4 @@
1
- import { Button } from '../chunk-5LTILG3L.js';
1
+ import { Button } from '../chunk-5HHRVJOT.js';
2
2
  import '../chunk-RIH2IS7X.js';
3
3
  import '../chunk-5YNIQ2BL.js';
4
4
  import '../chunk-FEK5XGZW.js';
@@ -3,7 +3,7 @@ import { HueChip } from '../chunk-6VOWT4YR.js';
3
3
  import { Separator } from '../chunk-NWZGSLPU.js';
4
4
  import { PhotoGround, PHOTO_GROUND } from '../chunk-SWD6CJ35.js';
5
5
  import { Skeleton } from '../chunk-XBBEX4SF.js';
6
- import { buttonVariants } from '../chunk-5LTILG3L.js';
6
+ import { buttonVariants } from '../chunk-5HHRVJOT.js';
7
7
  import '../chunk-RIH2IS7X.js';
8
8
  import '../chunk-5YNIQ2BL.js';
9
9
  import { cn } from '../chunk-FEK5XGZW.js';
@@ -1,6 +1,6 @@
1
1
  import { Separator } from '../chunk-NWZGSLPU.js';
2
2
  import { Skeleton } from '../chunk-XBBEX4SF.js';
3
- import { Button } from '../chunk-5LTILG3L.js';
3
+ import { Button } from '../chunk-5HHRVJOT.js';
4
4
  import '../chunk-RIH2IS7X.js';
5
5
  import '../chunk-5YNIQ2BL.js';
6
6
  import { cn } from '../chunk-FEK5XGZW.js';
@@ -1,5 +1,5 @@
1
- export { MentionSignal, MenuItem } from '../chunk-SDWN6L4O.js';
2
- import '../chunk-BKKQ7GCZ.js';
3
- import '../chunk-5XREWOBB.js';
1
+ export { MentionSignal, MenuItem } from '../chunk-AHHDC7ML.js';
2
+ import '../chunk-QG6I2ILT.js';
3
+ import '../chunk-IEIJP73R.js';
4
4
  import '../chunk-FEK5XGZW.js';
5
5
  import '../chunk-MMUBH76A.js';
@@ -15,6 +15,7 @@ declare function MotionHouse(props: MotionIconProps): react_jsx_runtime.JSX.Elem
15
15
  * the silhouette alone.
16
16
  */
17
17
  declare function MotionUtensils(props: MotionIconProps): react_jsx_runtime.JSX.Element;
18
+ declare function MotionCompass(props: MotionIconProps): react_jsx_runtime.JSX.Element;
18
19
  declare function MotionMapPin(props: MotionIconProps): react_jsx_runtime.JSX.Element;
19
20
  /**
20
21
  * Lucide's calendar has no pages — it is a box, a rule and six dots, and
@@ -36,4 +37,4 @@ declare function MotionFlame(props: MotionIconProps): react_jsx_runtime.JSX.Elem
36
37
  declare function MotionCalendarClock(props: MotionIconProps): react_jsx_runtime.JSX.Element;
37
38
  declare function MotionList(props: MotionIconProps): react_jsx_runtime.JSX.Element;
38
39
 
39
- export { MotionCalendar, MotionCalendarClock, MotionFlame, MotionHouse, MotionLayoutGrid, MotionList, MotionMap, MotionMapPin, MotionUtensils, MotionWine };
40
+ export { MotionCalendar, MotionCalendarClock, MotionCompass, MotionFlame, MotionHouse, MotionLayoutGrid, MotionList, MotionMap, MotionMapPin, MotionUtensils, MotionWine };
@@ -87,6 +87,32 @@ function MotionUtensils(props) {
87
87
  ] })
88
88
  ] }));
89
89
  }
90
+ var COMPASS_TIMES = [0, 0.14, 0.58, 0.76, 0.9, 1];
91
+ var compassNeedle = {
92
+ rest: { rotate: 360, fillOpacity: 0, transition: returnTransition },
93
+ active: { rotate: 360, fillOpacity: 1, transition: returnTransition },
94
+ play: {
95
+ rotate: [0, -24, 380, 352, 364, 360],
96
+ // The fill lands with the needle: it takes as the swing dies.
97
+ fillOpacity: [0, 0, 0, 0.6, 1, 1],
98
+ transition: beats(COMPASS_TIMES, 0.7)
99
+ }
100
+ };
101
+ function MotionCompass(props) {
102
+ return /* @__PURE__ */ jsxs(MotionIcon, __spreadProps(__spreadValues({}, props), { children: [
103
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "10" }),
104
+ /* @__PURE__ */ jsx(
105
+ motion.path,
106
+ {
107
+ variants: compassNeedle,
108
+ style: pivot(12, 12),
109
+ fill: "currentColor",
110
+ fillOpacity: 0,
111
+ d: "m16.24 7.76-1.804 5.411a2 2 0 0 1-1.265 1.265L7.76 16.24l1.804-5.411a2 2 0 0 1 1.265-1.265z"
112
+ }
113
+ )
114
+ ] }));
115
+ }
90
116
  var PIN_TIMES = [0, 0.16, 0.44, 0.72, 1];
91
117
  var pinBody = {
92
118
  rest: { y: 0, scaleY: 1, transition: returnTransition },
@@ -339,4 +365,4 @@ function MotionList(props) {
339
365
  ] }));
340
366
  }
341
367
 
342
- export { MotionCalendar, MotionCalendarClock, MotionFlame, MotionHouse, MotionLayoutGrid, MotionList, MotionMap, MotionMapPin, MotionUtensils, MotionWine };
368
+ export { MotionCalendar, MotionCalendarClock, MotionCompass, MotionFlame, MotionHouse, MotionLayoutGrid, MotionList, MotionMap, MotionMapPin, MotionUtensils, MotionWine };
@@ -2,10 +2,10 @@ import { Popover, PopoverTrigger, PopoverContent } from '../chunk-2ADCZXZJ.js';
2
2
  import { Command, CommandInput, CommandList, CommandEmpty, CommandGroup, CommandItem } from '../chunk-MCOHLWRS.js';
3
3
  import '../chunk-PDTPO6NG.js';
4
4
  import { Checkbox } from '../chunk-NMEVJT5Q.js';
5
- import { Button } from '../chunk-5LTILG3L.js';
5
+ import { Button } from '../chunk-5HHRVJOT.js';
6
6
  import '../chunk-RIH2IS7X.js';
7
7
  import '../chunk-5YNIQ2BL.js';
8
- import { Badge } from '../chunk-5XREWOBB.js';
8
+ import { Badge } from '../chunk-IEIJP73R.js';
9
9
  import { cn } from '../chunk-FEK5XGZW.js';
10
10
  import '../chunk-MMUBH76A.js';
11
11
  import * as React from 'react';
@@ -1,4 +1,4 @@
1
- import { buttonVariants } from '../chunk-5LTILG3L.js';
1
+ import { buttonVariants } from '../chunk-5HHRVJOT.js';
2
2
  import '../chunk-RIH2IS7X.js';
3
3
  import '../chunk-5YNIQ2BL.js';
4
4
  import { cn } from '../chunk-FEK5XGZW.js';
@@ -0,0 +1,36 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+
3
+ /**
4
+ * PartyCounter — how many people, in the booking flow's "When are you
5
+ * coming?" and "Change party size" screens.
6
+ *
7
+ * Measured from the booking design (`.stepper`): a 52px bordered field on the
8
+ * card ground, 12px radius, 8px side inset. A round 36px minus on the muted
9
+ * surface at the left, a round 36px plus filled with `--gradient-primary` at
10
+ * the right — the plus is the direction the screen is asking about — and the
11
+ * count between them at 19px bold with its unit at 12px muted.
12
+ *
13
+ * At an end the button is disabled, not hidden, so the value never shifts.
14
+ * The value is announced politely on change; the buttons carry their own
15
+ * localised names, because a lone "−" reads as nothing to a screen reader.
16
+ */
17
+ type PartyCounterLabels = {
18
+ /** "Fewer people" */
19
+ decrease: string;
20
+ /** "More people" */
21
+ increase: string;
22
+ /** The unit after the number: "people" / "person" — already pluralised by the caller. */
23
+ unit: (count: number) => string;
24
+ };
25
+ type PartyCounterProps = {
26
+ value: number;
27
+ onValueChange: (value: number) => void;
28
+ min?: number;
29
+ max?: number;
30
+ labels: PartyCounterLabels;
31
+ disabled?: boolean;
32
+ className?: string;
33
+ };
34
+ declare function PartyCounter({ value, onValueChange, min, max, labels, disabled, className, }: PartyCounterProps): react_jsx_runtime.JSX.Element;
35
+
36
+ export { PartyCounter, type PartyCounterLabels, type PartyCounterProps };