@bearmenu/ui 0.8.57 → 0.8.58

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.
@@ -3,10 +3,10 @@ import * as React from 'react';
3
3
  import { VariantProps } from 'class-variance-authority';
4
4
 
5
5
  declare const alertVariants: (props?: ({
6
- variant?: "success" | "warning" | "default" | "destructive" | null | undefined;
6
+ variant?: "default" | "success" | "warning" | "destructive" | null | undefined;
7
7
  } & class_variance_authority_types.ClassProp) | undefined) => string;
8
8
  declare const Alert: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLDivElement> & VariantProps<(props?: ({
9
- variant?: "success" | "warning" | "default" | "destructive" | null | undefined;
9
+ variant?: "default" | "success" | "warning" | "destructive" | null | undefined;
10
10
  } & class_variance_authority_types.ClassProp) | undefined) => string> & React.RefAttributes<HTMLDivElement>>;
11
11
  declare const AlertTitle: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLHeadingElement> & React.RefAttributes<HTMLParagraphElement>>;
12
12
  declare const AlertDescription: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLParagraphElement> & React.RefAttributes<HTMLParagraphElement>>;
@@ -4,7 +4,7 @@ import * as React from 'react';
4
4
  import { VariantProps } from 'class-variance-authority';
5
5
 
6
6
  declare const badgeVariants: (props?: ({
7
- variant?: "secondary" | "success" | "warning" | "default" | "destructive" | "outline" | "glass" | "overlay" | "brown" | "code" | "onPrimary" | "confirmed" | "pending" | "completed" | "assignee" | "softSuccess" | "softWarning" | "softDestructive" | "softNeutral" | null | undefined;
7
+ variant?: "secondary" | "default" | "success" | "warning" | "destructive" | "outline" | "glass" | "overlay" | "brown" | "code" | "onPrimary" | "confirmed" | "pending" | "completed" | "assignee" | "softSuccess" | "softWarning" | "softDestructive" | "softNeutral" | null | undefined;
8
8
  size?: "status" | "chip" | null | undefined;
9
9
  live?: boolean | null | undefined;
10
10
  } & class_variance_authority_types.ClassProp) | undefined) => string;
@@ -3,8 +3,8 @@ import * as React from 'react';
3
3
  import { VariantProps } from 'class-variance-authority';
4
4
 
5
5
  declare const buttonVariants: (props?: ({
6
- variant?: "secondary" | "link" | "warning" | "default" | "destructive" | "outline" | "ghost" | "glass" | "overlay" | "ink" | "brown" | "cta" | null | undefined;
7
- size?: "icon" | "default" | "cta" | "xs" | "sm" | "lg" | "pill" | "cta-sm" | "icon-xs" | "icon-sm" | "icon-lg" | null | undefined;
6
+ variant?: "secondary" | "default" | "link" | "warning" | "destructive" | "outline" | "ghost" | "glass" | "overlay" | "ink" | "brown" | "cta" | null | undefined;
7
+ size?: "default" | "icon" | "cta" | "xs" | "sm" | "lg" | "pill" | "cta-sm" | "icon-xs" | "icon-sm" | "icon-lg" | null | undefined;
8
8
  fullWidth?: boolean | null | undefined;
9
9
  } & class_variance_authority_types.ClassProp) | undefined) => string;
10
10
  interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement>, VariantProps<typeof buttonVariants> {
@@ -4,11 +4,11 @@ import { VariantProps } from 'class-variance-authority';
4
4
 
5
5
  declare const spinnerVariants: (props?: ({
6
6
  size?: "default" | "xs" | "sm" | "lg" | "xl" | null | undefined;
7
- tone?: "primary" | "inherit" | "default" | "destructive" | null | undefined;
7
+ tone?: "primary" | "default" | "inherit" | "destructive" | null | undefined;
8
8
  } & class_variance_authority_types.ClassProp) | undefined) => string;
9
9
  declare const Spinner: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLSpanElement> & VariantProps<(props?: ({
10
10
  size?: "default" | "xs" | "sm" | "lg" | "xl" | null | undefined;
11
- tone?: "primary" | "inherit" | "default" | "destructive" | null | undefined;
11
+ tone?: "primary" | "default" | "inherit" | "destructive" | null | undefined;
12
12
  } & class_variance_authority_types.ClassProp) | undefined) => string> & {
13
13
  /** Visually-hidden label for screen readers. Defaults to "Loading". */
14
14
  label?: string;
@@ -3,11 +3,11 @@ import * as React from 'react';
3
3
  import { VariantProps } from 'class-variance-authority';
4
4
 
5
5
  declare const tagVariants: (props?: ({
6
- variant?: "primary" | "success" | "warning" | "default" | "destructive" | "outline" | "quiet" | null | undefined;
6
+ variant?: "primary" | "default" | "success" | "warning" | "destructive" | "outline" | "quiet" | null | undefined;
7
7
  size?: "default" | "sm" | "lg" | null | undefined;
8
8
  } & class_variance_authority_types.ClassProp) | undefined) => string;
9
9
  declare const Tag: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLSpanElement> & VariantProps<(props?: ({
10
- variant?: "primary" | "success" | "warning" | "default" | "destructive" | "outline" | "quiet" | null | undefined;
10
+ variant?: "primary" | "default" | "success" | "warning" | "destructive" | "outline" | "quiet" | null | undefined;
11
11
  size?: "default" | "sm" | "lg" | null | undefined;
12
12
  } & class_variance_authority_types.ClassProp) | undefined) => string> & {
13
13
  onRemove?: () => void;
@@ -23,7 +23,7 @@ import { VariantProps } from 'class-variance-authority';
23
23
  */
24
24
  declare const textVariants: (props?: ({
25
25
  variant?: "small" | "body" | "display" | "h2" | "h3" | "label" | "caption" | "h1" | "h4" | "body-lg" | "body-sm" | "overline" | null | undefined;
26
- color?: "primary" | "inherit" | "default" | "destructive" | "muted" | "primary-foreground" | null | undefined;
26
+ color?: "primary" | "default" | "inherit" | "destructive" | "muted" | "primary-foreground" | null | undefined;
27
27
  weight?: "bold" | "medium" | "normal" | "semibold" | null | undefined;
28
28
  leading?: "none" | "normal" | "tight" | "snug" | "relaxed" | "loose" | null | undefined;
29
29
  tracking?: "normal" | "tight" | "wide" | "wider" | null | undefined;
@@ -0,0 +1,21 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import * as React from 'react';
3
+
4
+ type TimeSlotRailScrollLabels = {
5
+ /** "Earlier times" — the chevron at the start edge. */
6
+ previous: string;
7
+ /** "Later times" — the chevron at the end edge. */
8
+ next: string;
9
+ };
10
+ type TimeSlotRailProps = {
11
+ /** Names the group for assistive tech — "Start time on Wed 18 Nov". */
12
+ "aria-label": string;
13
+ /** Labels for the fine-pointer chevrons. Omit to render none. */
14
+ scrollLabels?: TimeSlotRailScrollLabels;
15
+ /** `TimeSlot`s, optionally wrapped in period groups and separators. */
16
+ children: React.ReactNode;
17
+ className?: string;
18
+ };
19
+ declare function TimeSlotRail({ "aria-label": ariaLabel, scrollLabels, children, className, }: TimeSlotRailProps): react_jsx_runtime.JSX.Element;
20
+
21
+ export { TimeSlotRail, type TimeSlotRailProps, type TimeSlotRailScrollLabels };
@@ -0,0 +1,168 @@
1
+ import { ScrollFade } from '../chunk-GNCWJWFE.js';
2
+ import { Button } from '../chunk-GHKTVHQG.js';
3
+ import '../chunk-RIH2IS7X.js';
4
+ import '../chunk-5YNIQ2BL.js';
5
+ import { cn } from '../chunk-FEK5XGZW.js';
6
+ import '../chunk-MMUBH76A.js';
7
+ import * as React from 'react';
8
+ import { ChevronLeft, ChevronRight } from 'lucide-react';
9
+ import { jsxs, jsx } from 'react/jsx-runtime';
10
+
11
+ var CHIP = '[data-slot="time-slot"]';
12
+ function chipsOf(scroller) {
13
+ return Array.from(scroller.querySelectorAll(CHIP)).filter((chip) => !chip.disabled);
14
+ }
15
+ function prefersReducedMotion() {
16
+ return typeof window.matchMedia === "function" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
17
+ }
18
+ function centreChip(scroller, chip, behavior) {
19
+ if (typeof scroller.scrollTo !== "function") return;
20
+ const s = scroller.getBoundingClientRect();
21
+ const c = chip.getBoundingClientRect();
22
+ const left = scroller.scrollLeft + (c.left - s.left) - (scroller.clientWidth - c.width) / 2;
23
+ scroller.scrollTo({ left: Math.max(0, left), behavior });
24
+ }
25
+ function TimeSlotRail({
26
+ "aria-label": ariaLabel,
27
+ scrollLabels,
28
+ children,
29
+ className
30
+ }) {
31
+ const scrollerRef = React.useRef(null);
32
+ const centredRef = React.useRef(null);
33
+ const [canScrollBack, setCanScrollBack] = React.useState(false);
34
+ const [canScrollForward, setCanScrollForward] = React.useState(false);
35
+ const syncTabStops = React.useCallback(() => {
36
+ var _a;
37
+ const scroller = scrollerRef.current;
38
+ if (!scroller) return;
39
+ const chips = chipsOf(scroller);
40
+ const stop = (_a = chips.find((chip) => chip.getAttribute("aria-pressed") === "true")) != null ? _a : chips[0];
41
+ for (const chip of chips) chip.tabIndex = chip === stop ? 0 : -1;
42
+ }, []);
43
+ const syncEdges = React.useCallback(() => {
44
+ const el = scrollerRef.current;
45
+ if (!el) return;
46
+ const overflow = el.scrollWidth - el.clientWidth;
47
+ setCanScrollBack(el.scrollLeft > 1);
48
+ setCanScrollForward(overflow > 1 && el.scrollLeft < overflow - 1);
49
+ }, []);
50
+ React.useEffect(() => {
51
+ const scroller = scrollerRef.current;
52
+ if (!scroller) return;
53
+ syncTabStops();
54
+ syncEdges();
55
+ scroller.addEventListener("scroll", syncEdges, { passive: true });
56
+ const resize = new ResizeObserver(syncEdges);
57
+ resize.observe(scroller);
58
+ const mutation = new MutationObserver(() => {
59
+ syncTabStops();
60
+ syncEdges();
61
+ });
62
+ mutation.observe(scroller, { childList: true, subtree: true, attributes: true, attributeFilter: ["aria-pressed", "disabled"] });
63
+ return () => {
64
+ scroller.removeEventListener("scroll", syncEdges);
65
+ resize.disconnect();
66
+ mutation.disconnect();
67
+ };
68
+ }, [syncTabStops, syncEdges]);
69
+ React.useEffect(() => {
70
+ const scroller = scrollerRef.current;
71
+ if (!scroller) return;
72
+ const selected = scroller.querySelector(`${CHIP}[aria-pressed="true"]`);
73
+ if (!selected || selected === centredRef.current) return;
74
+ const first = centredRef.current === null;
75
+ centredRef.current = selected;
76
+ if (!(selected instanceof HTMLElement)) return;
77
+ centreChip(scroller, selected, first || prefersReducedMotion() ? "auto" : "smooth");
78
+ });
79
+ const onKeyDown = (event) => {
80
+ const scroller = scrollerRef.current;
81
+ const target = event.target;
82
+ if (!scroller || !(target instanceof HTMLButtonElement) || !target.matches(CHIP)) return;
83
+ const chips = chipsOf(scroller);
84
+ const index = chips.indexOf(target);
85
+ if (index === -1) return;
86
+ const rtl = getComputedStyle(scroller).direction === "rtl";
87
+ let next;
88
+ switch (event.key) {
89
+ case "ArrowRight":
90
+ next = index + (rtl ? -1 : 1);
91
+ break;
92
+ case "ArrowLeft":
93
+ next = index + (rtl ? 1 : -1);
94
+ break;
95
+ case "Home":
96
+ next = 0;
97
+ break;
98
+ case "End":
99
+ next = chips.length - 1;
100
+ break;
101
+ default:
102
+ return;
103
+ }
104
+ event.preventDefault();
105
+ const chip = chips[Math.min(Math.max(next, 0), chips.length - 1)];
106
+ if (!chip) return;
107
+ for (const other of chips) other.tabIndex = other === chip ? 0 : -1;
108
+ chip.focus();
109
+ if (typeof chip.scrollIntoView === "function") chip.scrollIntoView({ inline: "nearest", block: "nearest" });
110
+ };
111
+ const onFocus = (event) => {
112
+ const scroller = scrollerRef.current;
113
+ const target = event.target;
114
+ if (!scroller || !(target instanceof HTMLButtonElement) || !target.matches(CHIP)) return;
115
+ for (const chip of chipsOf(scroller)) chip.tabIndex = chip === target ? 0 : -1;
116
+ };
117
+ const page = (direction) => {
118
+ const el = scrollerRef.current;
119
+ if (!el || typeof el.scrollBy !== "function") return;
120
+ el.scrollBy({ left: direction * el.clientWidth * 0.8, behavior: prefersReducedMotion() ? "auto" : "smooth" });
121
+ };
122
+ return /* @__PURE__ */ jsxs("div", { "data-slot": "time-slot-rail", className: "relative", children: [
123
+ /* @__PURE__ */ jsx(
124
+ ScrollFade,
125
+ {
126
+ ref: scrollerRef,
127
+ orientation: "horizontal",
128
+ fadeColor: "background",
129
+ size: "sm",
130
+ role: "group",
131
+ "aria-label": ariaLabel,
132
+ onKeyDown,
133
+ onFocus,
134
+ className,
135
+ scrollClassName: "flex snap-x snap-proximity scroll-px-10 gap-1.5 px-1.5 py-1.5 [scrollbar-width:none] [&::-webkit-scrollbar]:hidden [&_[data-slot=time-slot]]:shrink-0",
136
+ children
137
+ }
138
+ ),
139
+ scrollLabels && canScrollBack && /* @__PURE__ */ jsx(
140
+ Button,
141
+ {
142
+ type: "button",
143
+ variant: "ghost",
144
+ size: "icon-sm",
145
+ tabIndex: -1,
146
+ "aria-label": scrollLabels.previous,
147
+ onClick: () => page(-1),
148
+ className: cn("absolute top-1/2 left-0 z-20 hidden -translate-y-1/2 bg-background/90 [@media(pointer:fine)]:inline-flex"),
149
+ children: /* @__PURE__ */ jsx(ChevronLeft, { "aria-hidden": true })
150
+ }
151
+ ),
152
+ scrollLabels && canScrollForward && /* @__PURE__ */ jsx(
153
+ Button,
154
+ {
155
+ type: "button",
156
+ variant: "ghost",
157
+ size: "icon-sm",
158
+ tabIndex: -1,
159
+ "aria-label": scrollLabels.next,
160
+ onClick: () => page(1),
161
+ className: cn("absolute top-1/2 right-0 z-20 hidden -translate-y-1/2 bg-background/90 [@media(pointer:fine)]:inline-flex"),
162
+ children: /* @__PURE__ */ jsx(ChevronRight, { "aria-hidden": true })
163
+ }
164
+ )
165
+ ] });
166
+ }
167
+
168
+ export { TimeSlotRail };
@@ -18,24 +18,63 @@ import * as React from 'react';
18
18
  * 7px gaps.
19
19
  *
20
20
  * A slot is a button with `aria-pressed`: one of many times, picked once.
21
+ *
22
+ * ── size="touch" ──────────────────────────────────────────────────────────
23
+ * The default slot is 35px tall with a 9.5px meta line: under both the 44px
24
+ * touch floor and the 12px type floor, so a staff tool on a phone cannot use
25
+ * it. `touch` is `min-h-11`, 16px mono time, 12px meta, 12px radius. Selected
26
+ * is a FILL of `--accent` with `accent-foreground` type (the reasoning is
27
+ * DayRailChip's: the gradient's primary-foreground flips dark-on-gradient in
28
+ * dark mode), and the meta stays at full opacity.
29
+ *
30
+ * `fit` (touch only) says how well the slot suits the party, in words AND a
31
+ * dot, so nothing hangs on colour: `fits` (success), `tight` (warning, a join
32
+ * or a squeeze), `none` (dashed, muted — still tappable, the manager can
33
+ * override, so no strike-through and no fading).
34
+ *
35
+ * `disabled` means LOCKED, not taken: half opacity, not-allowed, no strike.
36
+ * It uses `aria-disabled` rather than the `disabled` attribute so a locked
37
+ * rail keeps focus where it was. `unavailable` is still the taken/past state.
21
38
  */
22
39
  type TimeSlotTone = "default" | "waitlist";
23
- type TimeSlotProps = {
24
- /** The time as displayed, already formatted in the venue's timezone — "19:30". */
25
- time: string;
40
+ type TimeSlotSize = "default" | "touch";
41
+ type TimeSlotFit = "fits" | "tight" | "none";
42
+ /**
43
+ * `fit` needs a `meta`: the meta text says the fit in words, and the dot is
44
+ * aria-hidden, so a fit without one would hang on colour alone.
45
+ */
46
+ type TimeSlotFitProps = {
26
47
  /** "Popular", "2 left", "Waitlist" — already localised. */
27
48
  meta?: string;
49
+ fit?: undefined;
50
+ } | {
51
+ /** Says the fit in words — "R1+R2", "Join R3+R4", "Full". */
52
+ meta: string;
53
+ /** Touch only. How well the slot suits the party. Requires `meta`. */
54
+ fit: TimeSlotFit;
55
+ };
56
+ type TimeSlotProps = TimeSlotFitProps & {
57
+ /** The time as displayed, already formatted in the venue's timezone — "19:30". */
58
+ time: string;
28
59
  /** `waitlist` renders the meta muted: the slot books onto the list, not a table. */
29
60
  tone?: TimeSlotTone;
30
61
  selected?: boolean;
31
62
  /** Taken or past: faded and struck through, not clickable. */
32
63
  unavailable?: boolean;
64
+ /** `default` is the booking flow's 35px slot; `touch` meets the 44px / 12px floors. */
65
+ size?: TimeSlotSize;
66
+ /** Locked, not taken: dimmed, not-allowed, no strike-through, still focusable. */
67
+ disabled?: boolean;
33
68
  onSelect?: () => void;
34
- /** Full accessible name, when the visible text is not enough ("19:30, popular"). */
69
+ /**
70
+ * Full accessible name, when the visible text is not enough ("19:30, popular").
71
+ * It REPLACES the visible text, so with `fit` it must say the fit in words
72
+ * ("19:30, fits at R1 and R2"): the dot is aria-hidden.
73
+ */
35
74
  "aria-label"?: string;
36
75
  className?: string;
37
76
  };
38
- declare function TimeSlot({ time, meta, tone, selected, unavailable, onSelect, "aria-label": ariaLabel, className, }: TimeSlotProps): react_jsx_runtime.JSX.Element;
77
+ declare function TimeSlot({ time, meta, tone, selected, unavailable, size, fit, disabled, onSelect, "aria-label": ariaLabel, className, }: TimeSlotProps): react_jsx_runtime.JSX.Element;
39
78
  type TimeSlotGroup = {
40
79
  /** Stable key — "early", "evening", "late". */
41
80
  key: string;
@@ -70,4 +109,4 @@ type TimeSlotGroupsSkeletonProps = {
70
109
  */
71
110
  declare function TimeSlotGroupsSkeleton({ label, rows, columns, className, }: TimeSlotGroupsSkeletonProps): react_jsx_runtime.JSX.Element;
72
111
 
73
- export { TimeSlot, type TimeSlotGroup, TimeSlotGroupLabel, TimeSlotGroups, type TimeSlotGroupsProps, TimeSlotGroupsSkeleton, type TimeSlotGroupsSkeletonProps, type TimeSlotProps, type TimeSlotTone };
112
+ export { TimeSlot, type TimeSlotFit, type TimeSlotFitProps, type TimeSlotGroup, TimeSlotGroupLabel, TimeSlotGroups, type TimeSlotGroupsProps, TimeSlotGroupsSkeleton, type TimeSlotGroupsSkeletonProps, type TimeSlotProps, type TimeSlotSize, type TimeSlotTone };
@@ -1,58 +1,102 @@
1
1
  import { Skeleton } from '../chunk-XBBEX4SF.js';
2
2
  import { cn } from '../chunk-FEK5XGZW.js';
3
3
  import { __objRest, __spreadValues } from '../chunk-MMUBH76A.js';
4
- import { jsxs, jsx } from 'react/jsx-runtime';
4
+ import { jsx, jsxs } from 'react/jsx-runtime';
5
5
 
6
+ var FIT_DOT = {
7
+ fits: "bg-success",
8
+ tight: "bg-warning-deep"
9
+ };
10
+ var FIT_META = {
11
+ fits: "text-success-ink",
12
+ tight: "text-warning-ink",
13
+ none: "text-muted-foreground"
14
+ };
6
15
  function TimeSlot({
7
16
  time,
8
17
  meta,
9
18
  tone = "default",
10
19
  selected,
11
20
  unavailable,
21
+ size = "default",
22
+ fit,
23
+ disabled,
12
24
  onSelect,
13
25
  "aria-label": ariaLabel,
14
26
  className
15
27
  }) {
28
+ const touch = size === "touch";
29
+ const activeFit = touch ? fit : void 0;
30
+ const locked = disabled && !unavailable;
31
+ const interactive = !unavailable && !locked;
32
+ const dashed = activeFit === "none" && !selected;
33
+ const fitDot = activeFit && activeFit !== "none" ? /* @__PURE__ */ jsx(
34
+ "span",
35
+ {
36
+ "aria-hidden": true,
37
+ "data-slot": "time-slot-fit-dot",
38
+ className: cn(
39
+ "size-1.5 shrink-0 rounded-full",
40
+ selected ? "bg-accent-foreground" : FIT_DOT[activeFit]
41
+ )
42
+ }
43
+ ) : null;
16
44
  return /* @__PURE__ */ jsxs(
17
45
  "button",
18
46
  {
19
47
  type: "button",
20
48
  "data-slot": "time-slot",
49
+ "data-size": size,
50
+ "data-fit": activeFit,
21
51
  "aria-pressed": selected ? true : false,
22
52
  "aria-label": ariaLabel,
53
+ "aria-disabled": locked ? true : void 0,
23
54
  disabled: unavailable,
24
- onClick: onSelect,
55
+ onClick: locked ? void 0 : onSelect,
25
56
  className: cn(
57
+ "flex w-full flex-col items-center border text-center",
26
58
  // Content sits at the top: a slot without a meta line in a row with
27
59
  // one is stretched by the grid, and the time must not drop to the middle.
28
- "flex w-full cursor-pointer flex-col items-center justify-start rounded-[11px] border px-1 py-2 text-center leading-[1.2]",
60
+ touch ? "min-h-11 justify-center gap-1 rounded-xl px-2 py-2 leading-[1.2]" : "justify-start rounded-[11px] px-1 py-2 leading-[1.2]",
61
+ interactive ? "cursor-pointer" : "cursor-not-allowed",
29
62
  // A press state, not just hover: this is the most-tapped control in
30
63
  // the flow and the primary device has no hover at all, so without it
31
64
  // a tap gives nothing back until the fill repaints.
32
- "transition-[border-color,filter,transform] duration-150 not-disabled:active:scale-[0.98] motion-reduce:transition-none",
65
+ touch ? "transition-[background-color,border-color,color,transform] duration-150 motion-reduce:transition-none" : "transition-[border-color,filter,transform] duration-150 motion-reduce:transition-none",
66
+ interactive && "active:scale-[0.98]",
33
67
  "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background",
34
- selected ? "border-transparent bg-[image:var(--gradient-primary)] text-primary-foreground hover:brightness-[0.94]" : "border-border bg-card text-foreground hover:border-accent/50",
35
- unavailable && "cursor-not-allowed opacity-[0.32] hover:border-border"
68
+ selected ? touch ? "border-accent bg-accent text-accent-foreground" : cn(
69
+ "border-transparent bg-[image:var(--gradient-primary)] text-primary-foreground",
70
+ interactive && "hover:brightness-[0.94]"
71
+ ) : dashed ? "border-dashed border-border bg-transparent text-muted-foreground" : "border-border bg-card text-foreground",
72
+ !selected && interactive && "hover:border-accent/50",
73
+ unavailable && "opacity-[0.32]",
74
+ locked && "opacity-50",
75
+ className
36
76
  ),
37
77
  children: [
38
78
  /* @__PURE__ */ jsx(
39
79
  "b",
40
80
  {
41
81
  className: cn(
42
- "block font-mono text-[14.5px] leading-none font-bold tabular-nums",
82
+ "block font-mono leading-none font-bold tabular-nums",
83
+ touch ? "text-base" : "text-[14.5px]",
43
84
  unavailable && "line-through"
44
85
  ),
45
86
  children: time
46
87
  }
47
88
  ),
48
- meta && /* @__PURE__ */ jsx(
89
+ meta && /* @__PURE__ */ jsxs(
49
90
  "span",
50
91
  {
51
92
  className: cn(
52
- "mt-[3px] block text-[9.5px] leading-[1.2]",
53
- selected ? "text-primary-foreground/80" : tone === "waitlist" ? "text-muted-foreground" : "text-accent"
93
+ touch ? "flex max-w-full items-center justify-center gap-1 text-xs leading-4" : "mt-[3px] block text-[9.5px] leading-[1.2]",
94
+ selected ? touch ? "text-accent-foreground" : "text-primary-foreground/80" : activeFit ? FIT_META[activeFit] : tone === "waitlist" ? "text-muted-foreground" : "text-accent"
54
95
  ),
55
- children: meta
96
+ children: [
97
+ fitDot,
98
+ touch ? /* @__PURE__ */ jsx("span", { className: "line-clamp-2 break-words", children: meta }) : meta
99
+ ]
56
100
  }
57
101
  )
58
102
  ]
@@ -11,7 +11,7 @@ import { VariantProps } from 'class-variance-authority';
11
11
  * `data-[state=on]:` utilities sort after `hover:` and still win while on.
12
12
  */
13
13
  declare const toggleVariants: (props?: ({
14
- variant?: "tab" | "default" | "outline" | "chip" | "segmented" | null | undefined;
14
+ variant?: "default" | "tab" | "outline" | "chip" | "segmented" | null | undefined;
15
15
  size?: "default" | "sm" | "lg" | null | undefined;
16
16
  } & class_variance_authority_types.ClassProp) | undefined) => string;
17
17
  /**