@bearmenu/ui 0.8.6 → 0.8.8

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 (88) hide show
  1. package/dist/{chunk-637L7XQ3.js → chunk-3TNPSN4D.js} +28 -9
  2. package/dist/chunk-AHL45DD2.js +145 -0
  3. package/dist/chunk-IYDH5IU6.js +66 -0
  4. package/dist/chunk-OPVKL4VS.js +18 -0
  5. package/dist/components/alert.d.ts +2 -2
  6. package/dist/components/badge.d.ts +1 -1
  7. package/dist/components/button.d.ts +1 -1
  8. package/dist/components/card.d.ts +2 -2
  9. package/dist/components/category-deck.js +10 -1
  10. package/dist/components/command.d.ts +15 -15
  11. package/dist/components/coverflow.d.ts +11 -3
  12. package/dist/components/coverflow.js +34 -51
  13. package/dist/components/drift-rail.d.ts +24 -10
  14. package/dist/components/drift-rail.js +1 -1
  15. package/dist/components/event-card.d.ts +8 -0
  16. package/dist/components/event-card.js +68 -35
  17. package/dist/components/event-compact-row.d.ts +34 -6
  18. package/dist/components/event-compact-row.js +120 -38
  19. package/dist/components/event-day-header.js +3 -1
  20. package/dist/components/event-day-separator.d.ts +7 -1
  21. package/dist/components/event-day-separator.js +3 -2
  22. package/dist/components/event-hub-hero.d.ts +25 -4
  23. package/dist/components/event-hub-hero.js +89 -19
  24. package/dist/components/event-list-row.d.ts +19 -6
  25. package/dist/components/event-list-row.js +78 -34
  26. package/dist/components/event-types.d.ts +5 -1
  27. package/dist/components/hour-band.d.ts +38 -40
  28. package/dist/components/hour-band.js +55 -27
  29. package/dist/components/hour-group.js +1 -1
  30. package/dist/components/item.d.ts +2 -2
  31. package/dist/components/poster-tape.js +1 -1
  32. package/dist/components/poster-wall.d.ts +3 -3
  33. package/dist/components/poster-wall.js +73 -17
  34. package/dist/components/section-header.d.ts +16 -1
  35. package/dist/components/section-header.js +26 -7
  36. package/dist/components/snap-rail.d.ts +34 -0
  37. package/dist/components/snap-rail.js +51 -0
  38. package/dist/components/spinner.d.ts +2 -2
  39. package/dist/components/tag.d.ts +2 -2
  40. package/dist/components/text.d.ts +3 -3
  41. package/dist/components/timeline-band.d.ts +20 -2
  42. package/dist/components/timeline-band.js +1 -1
  43. package/dist/components/timeline-playhead.d.ts +26 -0
  44. package/dist/components/timeline-playhead.js +25 -0
  45. package/dist/components/weekend-panel.d.ts +5 -4
  46. package/dist/components/weekend-panel.js +85 -71
  47. package/dist/hooks/use-drag-swipe.d.ts +44 -0
  48. package/dist/hooks/use-drag-swipe.js +2 -0
  49. package/dist/hooks/use-media-query.d.ts +17 -0
  50. package/dist/hooks/use-media-query.js +2 -0
  51. package/package.json +1 -1
  52. package/src/components/category-deck.tsx +19 -8
  53. package/src/components/coverflow.test.tsx +28 -1
  54. package/src/components/coverflow.tsx +37 -43
  55. package/src/components/drift-rail.tsx +167 -42
  56. package/src/components/event-card.test.tsx +11 -0
  57. package/src/components/event-card.tsx +67 -14
  58. package/src/components/event-compact-row.stories.tsx +30 -0
  59. package/src/components/event-compact-row.test.tsx +100 -0
  60. package/src/components/event-compact-row.tsx +166 -40
  61. package/src/components/event-day-header.tsx +3 -1
  62. package/src/components/event-day-separator.tsx +8 -1
  63. package/src/components/event-hub-hero.stories.tsx +30 -0
  64. package/src/components/event-hub-hero.tsx +102 -30
  65. package/src/components/event-list-row.stories.tsx +34 -0
  66. package/src/components/event-list-row.test.tsx +53 -0
  67. package/src/components/event-list-row.tsx +135 -87
  68. package/src/components/event-types.ts +5 -1
  69. package/src/components/hour-band.stories.tsx +28 -5
  70. package/src/components/hour-band.tsx +90 -70
  71. package/src/components/poster-wall.test.tsx +28 -0
  72. package/src/components/poster-wall.tsx +96 -23
  73. package/src/components/section-header.stories.tsx +24 -0
  74. package/src/components/section-header.test.tsx +28 -0
  75. package/src/components/section-header.tsx +45 -7
  76. package/src/components/snap-rail.stories.tsx +79 -0
  77. package/src/components/snap-rail.test.tsx +18 -0
  78. package/src/components/snap-rail.tsx +67 -0
  79. package/src/components/timeline-band.tsx +52 -12
  80. package/src/components/timeline-playhead.stories.tsx +30 -0
  81. package/src/components/timeline-playhead.test.tsx +11 -0
  82. package/src/components/timeline-playhead.tsx +46 -0
  83. package/src/components/weekend-panel.test.tsx +43 -0
  84. package/src/components/weekend-panel.tsx +142 -108
  85. package/src/hooks/use-drag-swipe.test.tsx +66 -0
  86. package/src/hooks/use-drag-swipe.ts +105 -0
  87. package/src/hooks/use-media-query.ts +31 -0
  88. package/dist/chunk-GPH6L4DX.js +0 -55
@@ -5,7 +5,7 @@ type PosterWallTile = {
5
5
  src?: string;
6
6
  };
7
7
  type PosterWallProps = {
8
- /** Background tiles. Desktop renders 48, `compact` renders 36 shorter arrays are padded with placeholders. */
8
+ /** Background tiles. The grid sizes itself to how many there are (see above); with none, a placeholder grid renders. */
9
9
  tiles?: PosterWallTile[];
10
10
  /** `compact` is the bottom-aligned mobile treatment (fewer, larger tiles, a top-fading veil). */
11
11
  variant?: "default" | "compact";
@@ -13,13 +13,13 @@ type PosterWallProps = {
13
13
  children?: React.ReactNode;
14
14
  className?: string;
15
15
  };
16
- declare function PosterWall({ tiles, variant, children, className }: PosterWallProps): react_jsx_runtime.JSX.Element;
16
+ declare function PosterWall({ tiles, variant, children, className, }: PosterWallProps): react_jsx_runtime.JSX.Element;
17
17
  type PosterWallSkeletonProps = {
18
18
  /** `compact` is the bottom-aligned mobile treatment (fewer, larger tiles, a top-fading veil). */
19
19
  variant?: "default" | "compact";
20
20
  className?: string;
21
21
  };
22
22
  /** The tile grid at rest — the sweeping light never renders in the loading state. */
23
- declare function PosterWallSkeleton({ variant, className }: PosterWallSkeletonProps): react_jsx_runtime.JSX.Element;
23
+ declare function PosterWallSkeleton({ variant, className, }: PosterWallSkeletonProps): react_jsx_runtime.JSX.Element;
24
24
 
25
25
  export { PosterWall, type PosterWallProps, PosterWallSkeleton, type PosterWallSkeletonProps, type PosterWallTile };
@@ -25,24 +25,60 @@ function useInViewport() {
25
25
  React.useEffect(() => {
26
26
  const node = ref.current;
27
27
  if (!node || typeof IntersectionObserver === "undefined") return;
28
- const observer = new IntersectionObserver(([entry]) => setInView(entry.isIntersecting), {
29
- threshold: 0
30
- });
28
+ const observer = new IntersectionObserver(
29
+ ([entry]) => setInView(entry.isIntersecting),
30
+ {
31
+ threshold: 0
32
+ }
33
+ );
31
34
  observer.observe(node);
32
35
  return () => observer.disconnect();
33
36
  }, []);
34
37
  return [ref, inView];
35
38
  }
36
- function PosterWall({ tiles, variant = "default", children, className }) {
39
+ function PosterWall({
40
+ tiles,
41
+ variant = "default",
42
+ children,
43
+ className
44
+ }) {
37
45
  const isCompact = variant === "compact";
38
- const tileCount = isCompact ? POSTER_WALL_COMPACT_TILE_COUNT : POSTER_WALL_DESKTOP_TILE_COUNT;
39
- const slots = Array.from({ length: tileCount }, (_, index) => {
40
- var _a;
41
- return (_a = tiles == null ? void 0 : tiles[index]) != null ? _a : {};
42
- });
46
+ const maxCols = isCompact ? 6 : 12;
47
+ const maxSlots = isCompact ? POSTER_WALL_COMPACT_TILE_COUNT : POSTER_WALL_DESKTOP_TILE_COUNT;
48
+ const heightPx = isCompact ? 330 : 420;
49
+ const pictures = React.useMemo(
50
+ () => (tiles != null ? tiles : []).filter((tile) => !!tile.src),
51
+ [tiles]
52
+ );
43
53
  const prefersReducedMotion = usePrefersReducedMotion();
44
54
  const [wallRef, inView] = useInViewport();
45
55
  const isSweepPaused = prefersReducedMotion || !inView;
56
+ const [width, setWidth] = React.useState(isCompact ? 390 : 1200);
57
+ React.useEffect(() => {
58
+ const node = wallRef.current;
59
+ if (!node || typeof ResizeObserver === "undefined") return;
60
+ const observer = new ResizeObserver(
61
+ ([entry]) => setWidth(entry.contentRect.width)
62
+ );
63
+ observer.observe(node);
64
+ return () => observer.disconnect();
65
+ }, [wallRef]);
66
+ let cols = maxCols;
67
+ let slots;
68
+ if (pictures.length === 0) {
69
+ slots = Array.from({ length: maxSlots }, () => ({}));
70
+ } else {
71
+ const shown = Math.min(pictures.length, maxSlots);
72
+ cols = Math.min(
73
+ maxCols,
74
+ Math.max(2, Math.round(Math.sqrt(shown * width / heightPx)))
75
+ );
76
+ const rows = Math.max(1, Math.ceil(shown / cols));
77
+ slots = Array.from(
78
+ { length: cols * rows },
79
+ (_, index) => pictures[index % pictures.length]
80
+ );
81
+ }
46
82
  return /* @__PURE__ */ jsxs(
47
83
  "div",
48
84
  {
@@ -62,10 +98,8 @@ function PosterWall({ tiles, variant = "default", children, className }) {
62
98
  /* @__PURE__ */ jsx(
63
99
  "div",
64
100
  {
65
- className: cn(
66
- "absolute inset-0 grid auto-rows-fr gap-[2px] opacity-50",
67
- isCompact ? "grid-cols-6" : "grid-cols-12"
68
- ),
101
+ className: "absolute inset-0 grid auto-rows-fr gap-[2px] opacity-50",
102
+ style: { gridTemplateColumns: `repeat(${cols}, minmax(0, 1fr))` },
69
103
  "aria-hidden": true,
70
104
  children: slots.map(
71
105
  (tile, index) => tile.src ? (
@@ -130,7 +164,10 @@ function PosterWall({ tiles, variant = "default", children, className }) {
130
164
  }
131
165
  );
132
166
  }
133
- function PosterWallSkeleton({ variant = "default", className }) {
167
+ function PosterWallSkeleton({
168
+ variant = "default",
169
+ className
170
+ }) {
134
171
  const isCompact = variant === "compact";
135
172
  const tileCount = isCompact ? POSTER_WALL_COMPACT_TILE_COUNT : POSTER_WALL_DESKTOP_TILE_COUNT;
136
173
  return /* @__PURE__ */ jsxs(
@@ -150,7 +187,13 @@ function PosterWallSkeleton({ variant = "default", className }) {
150
187
  "absolute inset-0 grid auto-rows-fr gap-[2px] opacity-50",
151
188
  isCompact ? "grid-cols-6" : "grid-cols-12"
152
189
  ),
153
- children: Array.from({ length: tileCount }, (_, index) => /* @__PURE__ */ jsx(Skeleton, { className: "h-full w-full rounded-none bg-white/10" }, index))
190
+ children: Array.from({ length: tileCount }, (_, index) => /* @__PURE__ */ jsx(
191
+ Skeleton,
192
+ {
193
+ className: "h-full w-full rounded-none bg-white/10"
194
+ },
195
+ index
196
+ ))
154
197
  }
155
198
  ),
156
199
  /* @__PURE__ */ jsx(
@@ -170,8 +213,21 @@ function PosterWallSkeleton({ variant = "default", className }) {
170
213
  isCompact ? "justify-end gap-[11px] p-5" : "justify-center gap-[15px] px-12"
171
214
  ),
172
215
  children: [
173
- /* @__PURE__ */ jsx(Skeleton, { className: cn("bg-white/15", isCompact ? "h-[11px] w-40" : "h-3 w-52") }),
174
- /* @__PURE__ */ jsx(Skeleton, { className: cn("bg-white/20", isCompact ? "h-7 w-64" : "h-10 w-96") })
216
+ /* @__PURE__ */ jsx(
217
+ Skeleton,
218
+ {
219
+ className: cn(
220
+ "bg-white/15",
221
+ isCompact ? "h-[11px] w-40" : "h-3 w-52"
222
+ )
223
+ }
224
+ ),
225
+ /* @__PURE__ */ jsx(
226
+ Skeleton,
227
+ {
228
+ className: cn("bg-white/20", isCompact ? "h-7 w-64" : "h-10 w-96")
229
+ }
230
+ )
175
231
  ]
176
232
  }
177
233
  )
@@ -25,6 +25,21 @@ type SectionHeaderProps = {
25
25
  /** Trailing action label, e.g. "See all". */
26
26
  action?: string;
27
27
  actionHref?: string;
28
+ /**
29
+ * `muted` (default): 14px in `--muted-foreground`, at every width. `app`:
30
+ * BELOW `lg`, 12.5px semibold in `--accent` — the way an app's "All ›" reads
31
+ * at the end of a card's title row, quieter in size and louder in colour so
32
+ * it sits in the count's tier rather than competing with the title. From
33
+ * `lg` it falls back to `muted`: the app treatment is a phone treatment, and
34
+ * the desktop page it shares a header with is unchanged by it.
35
+ */
36
+ actionTone?: "muted" | "app";
37
+ /**
38
+ * `always` (default) shows the subtitle at every width. `lg` hides it below
39
+ * the `lg` breakpoint for surfaces that narrate on desktop and not on a
40
+ * phone, where a card title with a count is the whole header.
41
+ */
42
+ subtitleFrom?: "always" | "lg";
28
43
  /** Defaults to a plain `<a>`. Pass `next/link` in a Next app. */
29
44
  linkComponent?: React.ComponentType<SectionHeaderLinkProps>;
30
45
  onAction?: (mouseEvent: React.MouseEvent) => void;
@@ -37,7 +52,7 @@ type SectionHeaderProps = {
37
52
  as?: "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "p" | "div";
38
53
  className?: string;
39
54
  };
40
- declare function SectionHeader({ title, count, subtitle, action, actionHref, linkComponent: LinkComponent, onAction, as: Heading, className, }: SectionHeaderProps): react_jsx_runtime.JSX.Element;
55
+ declare function SectionHeader({ title, count, subtitle, action, actionHref, actionTone, subtitleFrom, linkComponent: LinkComponent, onAction, as: Heading, className, }: SectionHeaderProps): react_jsx_runtime.JSX.Element;
41
56
  type SectionHeaderSkeletonProps = {
42
57
  /** Shows the inline count placeholder, matching `SectionHeaderProps["count"]`. */
43
58
  showCount?: boolean;
@@ -8,38 +8,57 @@ function DefaultLink(_a) {
8
8
  var _b = _a, { href } = _b, props = __objRest(_b, ["href"]);
9
9
  return /* @__PURE__ */ jsx("a", __spreadValues({ href }, props));
10
10
  }
11
- var ACTION_CLASSES = cn(
12
- "inline-flex shrink-0 items-center gap-0.5 rounded-sm text-[14px] text-muted-foreground",
13
- "transition-colors duration-150 hover:text-foreground motion-reduce:transition-none",
11
+ var ACTION_BASE_CLASSES = cn(
12
+ "inline-flex min-h-11 shrink-0 items-center gap-0.5 rounded-sm -my-2 py-2 -mr-1 pr-1",
13
+ "transition-colors duration-150 motion-reduce:transition-none",
14
14
  "cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2"
15
15
  );
16
+ var ACTION_TONE_CLASSES = {
17
+ muted: "text-[14px] text-muted-foreground hover:text-foreground",
18
+ app: cn(
19
+ "text-[12.5px] font-semibold text-accent hover:text-accent/80",
20
+ "lg:text-[14px] lg:font-normal lg:text-muted-foreground lg:hover:text-foreground"
21
+ )
22
+ };
16
23
  function SectionHeader({
17
24
  title,
18
25
  count,
19
26
  subtitle,
20
27
  action,
21
28
  actionHref,
29
+ actionTone = "muted",
30
+ subtitleFrom = "always",
22
31
  linkComponent: LinkComponent = DefaultLink,
23
32
  onAction,
24
33
  as: Heading = "h2",
25
34
  className
26
35
  }) {
27
36
  const actionIcon = /* @__PURE__ */ jsx(ChevronRight, { className: "h-3.5 w-3.5", "aria-hidden": true });
37
+ const actionClasses = cn(ACTION_BASE_CLASSES, ACTION_TONE_CLASSES[actionTone]);
28
38
  return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col mb-3.5", className), children: [
29
39
  /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2", children: [
30
40
  /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-baseline", children: [
31
41
  /* @__PURE__ */ jsx(Heading, { className: "truncate text-[17px] font-bold tracking-[-0.01em] text-foreground md:text-[18px]", children: title }),
32
- count !== void 0 && /* @__PURE__ */ jsx("span", { className: "ml-0.5 shrink-0 text-[12px] text-muted-foreground", children: count })
42
+ count !== void 0 && /* @__PURE__ */ jsx("span", { className: "ml-1.5 shrink-0 text-[12px] tabular-nums text-muted-foreground lg:ml-0.5", children: count })
33
43
  ] }),
34
- action && (actionHref ? /* @__PURE__ */ jsxs(LinkComponent, { href: actionHref, className: ACTION_CLASSES, onClick: onAction, children: [
44
+ action && (actionHref ? /* @__PURE__ */ jsxs(LinkComponent, { href: actionHref, className: actionClasses, onClick: onAction, children: [
35
45
  action,
36
46
  actionIcon
37
- ] }) : onAction ? /* @__PURE__ */ jsxs("button", { type: "button", className: ACTION_CLASSES, onClick: onAction, children: [
47
+ ] }) : onAction ? /* @__PURE__ */ jsxs("button", { type: "button", className: actionClasses, onClick: onAction, children: [
38
48
  action,
39
49
  actionIcon
40
50
  ] }) : null)
41
51
  ] }),
42
- subtitle && /* @__PURE__ */ jsx("p", { className: "mt-0.5 text-[13px] text-muted-foreground md:text-[14px]", children: subtitle })
52
+ subtitle && /* @__PURE__ */ jsx(
53
+ "p",
54
+ {
55
+ className: cn(
56
+ "mt-0.5 text-[13px] text-muted-foreground md:text-[14px]",
57
+ subtitleFrom === "lg" && "hidden lg:block"
58
+ ),
59
+ children: subtitle
60
+ }
61
+ )
43
62
  ] });
44
63
  }
45
64
  function SectionHeaderSkeleton({
@@ -0,0 +1,34 @@
1
+ import * as class_variance_authority_types from 'class-variance-authority/types';
2
+ import * as React from 'react';
3
+ import { VariantProps } from 'class-variance-authority';
4
+
5
+ /**
6
+ * SnapRail — a horizontal, finger-driven rail with mandatory snap points, the
7
+ * way a native app lays out a row of cards. Not `DriftRail`: that one moves on
8
+ * its own and is for ambience; this one waits to be swiped and is for content
9
+ * the reader is expected to reach.
10
+ *
11
+ * Every direct child becomes a snap target and takes the rail's item width, so
12
+ * callers pass bare cards — no wrapper, no per-item width. Widths are
13
+ * percentages of the rail, so the peek of the next card is what tells the
14
+ * reader there is more; there is no scrollbar and no edge fade.
15
+ *
16
+ * `bleed` cancels the host's horizontal padding and restores it as scroll
17
+ * padding, so the first card lines up with the host's content edge and the
18
+ * rail still snaps to that same edge after a swipe. Without it, the rail
19
+ * snaps flush to the host's rounded corner and loads looking pre-scrolled.
20
+ * `card` is the 14px inset of a hub card; `page` is the 16px page gutter.
21
+ */
22
+ declare const snapRailVariants: (props?: ({
23
+ itemWidth?: "sm" | "lg" | "md" | null | undefined;
24
+ gap?: "default" | "sm" | null | undefined;
25
+ bleed?: "none" | "page" | "card" | null | undefined;
26
+ } & class_variance_authority_types.ClassProp) | undefined) => string;
27
+ type SnapRailProps = React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof snapRailVariants>;
28
+ declare const SnapRail: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLDivElement> & VariantProps<(props?: ({
29
+ itemWidth?: "sm" | "lg" | "md" | null | undefined;
30
+ gap?: "default" | "sm" | null | undefined;
31
+ bleed?: "none" | "page" | "card" | null | undefined;
32
+ } & class_variance_authority_types.ClassProp) | undefined) => string> & React.RefAttributes<HTMLDivElement>>;
33
+
34
+ export { SnapRail, type SnapRailProps };
@@ -0,0 +1,51 @@
1
+ import { cn } from '../chunk-FEK5XGZW.js';
2
+ import { __objRest, __spreadValues } from '../chunk-3GWWZKX7.js';
3
+ import * as React from 'react';
4
+ import { cva } from 'class-variance-authority';
5
+ import { jsx } from 'react/jsx-runtime';
6
+
7
+ var snapRailVariants = cva(
8
+ cn(
9
+ "flex snap-x snap-mandatory overflow-x-auto pb-0.5 scrollbar-hide",
10
+ "[&>*]:shrink-0 [&>*]:snap-start"
11
+ ),
12
+ {
13
+ variants: {
14
+ itemWidth: {
15
+ sm: "[&>*]:w-[46%]",
16
+ md: "[&>*]:w-[60%]",
17
+ lg: "[&>*]:w-[80%]"
18
+ },
19
+ gap: {
20
+ sm: "gap-2",
21
+ default: "gap-3"
22
+ },
23
+ bleed: {
24
+ none: "",
25
+ card: "-mx-3.5 px-3.5 scroll-px-3.5",
26
+ page: "-mx-4 px-4 scroll-px-4"
27
+ }
28
+ },
29
+ defaultVariants: {
30
+ itemWidth: "sm",
31
+ gap: "default",
32
+ bleed: "none"
33
+ }
34
+ }
35
+ );
36
+ var SnapRail = React.forwardRef(
37
+ (_a, ref) => {
38
+ var _b = _a, { className, itemWidth, gap, bleed } = _b, props = __objRest(_b, ["className", "itemWidth", "gap", "bleed"]);
39
+ return /* @__PURE__ */ jsx(
40
+ "div",
41
+ __spreadValues({
42
+ ref,
43
+ "data-slot": "snap-rail",
44
+ className: cn(snapRailVariants({ itemWidth, gap, bleed }), className)
45
+ }, props)
46
+ );
47
+ }
48
+ );
49
+ SnapRail.displayName = "SnapRail";
50
+
51
+ export { SnapRail };
@@ -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?: "default" | "destructive" | "inherit" | "primary" | null | undefined;
7
+ tone?: "inherit" | "default" | "destructive" | "primary" | 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?: "default" | "destructive" | "inherit" | "primary" | null | undefined;
11
+ tone?: "inherit" | "default" | "destructive" | "primary" | 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?: "default" | "destructive" | "warning" | "success" | "primary" | "outline" | "quiet" | null | undefined;
6
+ variant?: "success" | "warning" | "default" | "destructive" | "outline" | "primary" | "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?: "default" | "destructive" | "warning" | "success" | "primary" | "outline" | "quiet" | null | undefined;
10
+ variant?: "success" | "warning" | "default" | "destructive" | "outline" | "primary" | "quiet" | null | undefined;
11
11
  size?: "default" | "sm" | "lg" | null | undefined;
12
12
  } & class_variance_authority_types.ClassProp) | undefined) => string> & {
13
13
  onRemove?: () => void;
@@ -22,9 +22,9 @@ import { VariantProps } from 'class-variance-authority';
22
22
  * Weights are 400 / 600 / 700. 500 is not in the system.
23
23
  */
24
24
  declare const textVariants: (props?: ({
25
- variant?: "small" | "display" | "h1" | "h2" | "h3" | "h4" | "body-lg" | "body" | "body-sm" | "caption" | "label" | "overline" | null | undefined;
26
- color?: "default" | "destructive" | "inherit" | "muted" | "primary" | "primary-foreground" | null | undefined;
27
- weight?: "bold" | "normal" | "medium" | "semibold" | null | undefined;
25
+ variant?: "body" | "caption" | "h1" | "h2" | "h3" | "h4" | "label" | "small" | "display" | "body-lg" | "body-sm" | "overline" | null | undefined;
26
+ color?: "inherit" | "default" | "destructive" | "muted" | "primary" | "primary-foreground" | null | undefined;
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;
30
30
  wrap?: "balance" | "pretty" | "nowrap" | null | undefined;
@@ -1,6 +1,14 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import * as React from 'react';
3
3
 
4
+ /**
5
+ * The mobile axis gutter, shared by `HourBand` and `TimelinePlayhead` so the
6
+ * hour chips and the current-time label sit in one column. 58px is the width
7
+ * of a five-glyph 24-hour label ("20:00") in the chip's mono type plus its
8
+ * padding; a 12-hour locale's "09:00 AM" overflows it, which is one of the
9
+ * reasons the events surfaces format 24-hour everywhere.
10
+ */
11
+ declare const TIMELINE_MOBILE_GUTTER_CLASS = "w-[58px]";
4
12
  type TimelineHourPuckProps = {
5
13
  /** The hour, already formatted and localised — or an em dash for a quiet day. */
6
14
  time: string;
@@ -8,6 +16,16 @@ type TimelineHourPuckProps = {
8
16
  isLive?: boolean;
9
17
  /** Shown under the puck when live, e.g. "NOW ON". Required to render it. */
10
18
  liveLabel?: string;
19
+ /**
20
+ * `station` (default): the bordered puck that sits ON the hairline, for the
21
+ * desktop spine. `chip`: a filled, fixed-width lozenge with the hairline
22
+ * starting BELOW it, for the mobile gutter — an app's time label, not a
23
+ * station on a rail. In `chip` the live label is visually hidden: the tinted
24
+ * chip plus a small on-air glyph are the visible signal (the glyph so the
25
+ * state never rests on colour alone once the pulse is reduced away) and the
26
+ * label stays for screen readers only.
27
+ */
28
+ variant?: "station" | "chip";
11
29
  className?: string;
12
30
  };
13
31
  /**
@@ -25,7 +43,7 @@ type TimelineHourPuckProps = {
25
43
  * centre this puck with `-translate-y-1/2`, and a two-item column would centre
26
44
  * the STACK, lifting the puck off the line it exists to sit on.
27
45
  */
28
- declare function TimelineHourPuck({ time, isLive, liveLabel, className }: TimelineHourPuckProps): react_jsx_runtime.JSX.Element;
46
+ declare function TimelineHourPuck({ time, isLive, liveLabel, variant, className, }: TimelineHourPuckProps): react_jsx_runtime.JSX.Element;
29
47
  type TimelineBandProps = {
30
48
  /** The lane column — usually a poster card, or empty for a quiet hour. */
31
49
  lane: React.ReactNode;
@@ -43,4 +61,4 @@ type TimelineBandSkeletonProps = {
43
61
  */
44
62
  declare function TimelineBandSkeleton({ className }: TimelineBandSkeletonProps): react_jsx_runtime.JSX.Element;
45
63
 
46
- export { TimelineBand, type TimelineBandProps, TimelineBandSkeleton, type TimelineBandSkeletonProps, TimelineHourPuck, type TimelineHourPuckProps };
64
+ export { TIMELINE_MOBILE_GUTTER_CLASS, TimelineBand, type TimelineBandProps, TimelineBandSkeleton, type TimelineBandSkeletonProps, TimelineHourPuck, type TimelineHourPuckProps };
@@ -1,4 +1,4 @@
1
- export { TimelineBand, TimelineBandSkeleton, TimelineHourPuck } from '../chunk-637L7XQ3.js';
1
+ export { TIMELINE_MOBILE_GUTTER_CLASS, TimelineBand, TimelineBandSkeleton, TimelineHourPuck } from '../chunk-3TNPSN4D.js';
2
2
  import '../chunk-WSRWFA6K.js';
3
3
  import '../chunk-QXHMPJ35.js';
4
4
  import '../chunk-FEK5XGZW.js';
@@ -0,0 +1,26 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+
3
+ /**
4
+ * TimelinePlayhead — the "you are here" mark on the mobile timeline: the
5
+ * current time in the axis gutter, a dashed rule across the content column,
6
+ * and a filled NOW pill at the far edge.
7
+ *
8
+ * It sits BETWEEN hour bands, at the point where the wall clock falls, so the
9
+ * reader can see at a glance which hours are behind them and which are ahead.
10
+ * The live hour band's tinted chip says "this hour is running"; the playhead
11
+ * says exactly how far into it we are. Both are needed: a band spans an hour
12
+ * and the hour is mostly not "now".
13
+ *
14
+ * The time label shares the gutter width with `HourBand` so it lines up under
15
+ * the hour chips. Both strings arrive formatted — the DS holds no clock.
16
+ */
17
+ type TimelinePlayheadProps = {
18
+ /** The current time, already formatted — "21:38". */
19
+ time: string;
20
+ /** The pill text, e.g. "NOW". */
21
+ label: string;
22
+ className?: string;
23
+ };
24
+ declare function TimelinePlayhead({ time, label, className }: TimelinePlayheadProps): react_jsx_runtime.JSX.Element;
25
+
26
+ export { TimelinePlayhead, type TimelinePlayheadProps };
@@ -0,0 +1,25 @@
1
+ import { TIMELINE_MOBILE_GUTTER_CLASS } from '../chunk-3TNPSN4D.js';
2
+ import '../chunk-WSRWFA6K.js';
3
+ import '../chunk-QXHMPJ35.js';
4
+ import { cn } from '../chunk-FEK5XGZW.js';
5
+ import '../chunk-3GWWZKX7.js';
6
+ import { jsxs, jsx } from 'react/jsx-runtime';
7
+
8
+ function TimelinePlayhead({ time, label, className }) {
9
+ return /* @__PURE__ */ jsxs("div", { className: cn("flex items-center gap-2.5 pt-3", className), children: [
10
+ /* @__PURE__ */ jsx(
11
+ "span",
12
+ {
13
+ className: cn(
14
+ TIMELINE_MOBILE_GUTTER_CLASS,
15
+ "shrink-0 text-center font-mono text-[11px] font-bold tabular-nums text-success"
16
+ ),
17
+ children: time
18
+ }
19
+ ),
20
+ /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "h-0 flex-1 border-t-[1.5px] border-dashed border-success/50" }),
21
+ /* @__PURE__ */ jsx("span", { className: "rounded-full bg-success px-2 py-0.5 text-[9.5px] font-extrabold tracking-[0.1em] text-success-foreground", children: label })
22
+ ] });
23
+ }
24
+
25
+ export { TimelinePlayhead };
@@ -22,14 +22,15 @@ type WeekendPanelProps = {
22
22
  linkComponent?: React.ComponentType<WeekendPanelLinkProps>;
23
23
  onActionClick?: () => void;
24
24
  isHighlighted?: boolean;
25
- /** Loop duration for the slide/dots animation. Default 7 sibling panels typically stagger at 8.5 and 10. */
25
+ /** Desktop autoplay loop duration the page turns every half of it. Default 7; sibling panels typically stagger at 8.5 and 10. */
26
26
  durationSeconds?: number;
27
27
  /**
28
28
  * Accessible names for the pause/play toggle. Required — the control is
29
- * only omitted under `prefers-reduced-motion`, where the labels go unused.
29
+ * only rendered on desktop and not under `prefers-reduced-motion`, but the
30
+ * DS owns no copy.
30
31
  */
31
32
  labels: WeekendPanelLabels;
32
- /** The event cards. The animation is built around exactly four (two pages of two). */
33
+ /** The event cards. Built around exactly four (two pages of two). */
33
34
  children?: React.ReactNode;
34
35
  className?: string;
35
36
  };
@@ -39,6 +40,6 @@ type WeekendPanelSkeletonProps = {
39
40
  className?: string;
40
41
  };
41
42
  /** The panel at rest: the two visible cards of the two-up carousel, no slide. */
42
- declare function WeekendPanelSkeleton({ isHighlighted, className }: WeekendPanelSkeletonProps): react_jsx_runtime.JSX.Element;
43
+ declare function WeekendPanelSkeleton({ isHighlighted, className, }: WeekendPanelSkeletonProps): react_jsx_runtime.JSX.Element;
43
44
 
44
45
  export { WeekendPanel, type WeekendPanelLabels, type WeekendPanelLinkProps, type WeekendPanelProps, WeekendPanelSkeleton, type WeekendPanelSkeletonProps };