@borisj74/bv-ds 0.1.2 → 0.1.4

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 (46) hide show
  1. package/dist/index.cjs +266 -69
  2. package/dist/index.d.cts +128 -49
  3. package/dist/index.d.ts +128 -49
  4. package/dist/index.js +266 -70
  5. package/package.json +11 -9
  6. package/src/components/Button/Button.tsx +1 -1
  7. package/src/components/ButtonDestructive/ButtonDestructive.tsx +2 -2
  8. package/src/components/CalendarCell/CalendarCell.tsx +63 -10
  9. package/src/components/CalendarCell/index.ts +5 -1
  10. package/src/components/CalendarCellDayWeekView/CalendarCellDayWeekView.tsx +28 -2
  11. package/src/components/CalendarCellDayWeekView/index.ts +5 -1
  12. package/src/components/CalendarColumnHeader/CalendarColumnHeader.tsx +21 -5
  13. package/src/components/CalendarColumnHeader/index.ts +2 -0
  14. package/src/components/CalendarEvent/CalendarEvent.tsx +69 -14
  15. package/src/components/CalendarEvent/index.ts +6 -1
  16. package/src/components/CalendarHeader/CalendarHeader.tsx +5 -1
  17. package/src/components/CalendarRowLabel/CalendarRowLabel.tsx +5 -3
  18. package/src/components/CalendarTimemarker/CalendarTimemarker.tsx +8 -2
  19. package/src/components/CalendarTimemarker/index.ts +1 -0
  20. package/src/components/CalendarViewDropdown/CalendarViewDropdown.tsx +19 -3
  21. package/src/components/ChartTooltip/ChartTooltip.tsx +1 -1
  22. package/src/components/Checkbox/checkboxBase.tsx +1 -1
  23. package/src/components/ContentFeatureText/ContentFeatureText.tsx +3 -2
  24. package/src/components/ContextMenu/ContextMenu.tsx +1 -1
  25. package/src/components/FeaturedIcon/FeaturedIcon.tsx +146 -0
  26. package/src/components/FeaturedIcon/index.ts +7 -0
  27. package/src/components/MessageAction/MessageAction.tsx +1 -1
  28. package/src/components/NavAccountCard/NavAccountCard.tsx +1 -1
  29. package/src/components/NavAccountCardMenuItem/NavAccountCardMenuItem.tsx +1 -1
  30. package/src/components/NavButton/NavButton.tsx +1 -1
  31. package/src/components/NavFeaturedCard/NavFeaturedCard.tsx +1 -1
  32. package/src/components/NavItemBase/NavItemBase.tsx +1 -1
  33. package/src/components/NavItemDropdownBase/NavItemDropdownBase.tsx +1 -1
  34. package/src/components/PageHeader/PageHeader.tsx +1 -1
  35. package/src/components/PaginationDotIndicator/PaginationDotIndicator.tsx +1 -1
  36. package/src/components/ProgressBar/ProgressBar.tsx +1 -1
  37. package/src/components/RadioGroupItem/RadioGroupItem.tsx +1 -1
  38. package/src/components/SelectMenuItem/SelectMenuItem.tsx +2 -2
  39. package/src/components/SidebarNavigation/SidebarNavigation.tsx +1 -1
  40. package/src/components/SocialButton/SocialButton.tsx +2 -2
  41. package/src/components/TableHeaderLabel/TableHeaderLabel.tsx +1 -1
  42. package/src/components/Toggle/Toggle.tsx +1 -1
  43. package/src/components/Tooltip/Tooltip.tsx +1 -1
  44. package/src/components/TreeViewItem/TreeViewItem.tsx +1 -1
  45. package/src/components/VerificationCodeInput/VerificationCodeInput.tsx +1 -1
  46. package/src/index.ts +1 -0
@@ -1,17 +1,40 @@
1
1
  import type { ReactNode } from "react";
2
2
  import clsx from "clsx";
3
+ import { CalendarEvent, type CalendarEventColor } from "../CalendarEvent";
4
+
5
+ export type CalendarCellBreakpoint = "desktop" | "mobile";
6
+
7
+ export interface CalendarCellEvent {
8
+ title: string;
9
+ time?: string;
10
+ color?: CalendarEventColor;
11
+ }
3
12
 
4
13
  export interface CalendarCellProps {
5
14
  /** Day-of-month number. */
6
15
  date: string | number;
16
+ /**
17
+ * Declarative events for this day — rendered as filled `CalendarEvent`
18
+ * chips (collapsed to dots on mobile). Use `children` instead for full
19
+ * control over each chip.
20
+ */
21
+ events?: CalendarCellEvent[];
7
22
  /** Event chips for this day — typically `CalendarEvent` instances. */
8
23
  children?: ReactNode;
9
24
  /** "+N more" overflow count shown beneath the events. */
10
25
  moreCount?: number;
26
+ /** Today — date number in a brand-solid circle. */
27
+ isToday?: boolean;
28
+ /** Selected day — date number in a secondary-fill circle. */
29
+ isSelected?: boolean;
11
30
  /** Other-month / disabled styling. */
12
- muted?: boolean;
13
- /** Marks today (date number in brand). */
31
+ isDisabled?: boolean;
32
+ /** Legacy alias for `isToday`. */
14
33
  current?: boolean;
34
+ /** Legacy alias for `isDisabled`. */
35
+ muted?: boolean;
36
+ /** Desktop renders full chips; mobile collapses chips to dots. */
37
+ breakpoint?: CalendarCellBreakpoint;
15
38
  /** Renders a hover "+" add button bottom-right. */
16
39
  onAdd?: () => void;
17
40
  className?: string;
@@ -34,33 +57,63 @@ function PlusIcon() {
34
57
  /** A single day cell in the month grid. Composes CalendarEvent chips. */
35
58
  export function CalendarCell({
36
59
  date,
60
+ events,
37
61
  children,
38
62
  moreCount,
39
- muted = false,
63
+ isToday,
64
+ isSelected = false,
65
+ isDisabled,
40
66
  current = false,
67
+ muted = false,
68
+ breakpoint = "desktop",
41
69
  onAdd,
42
70
  className,
43
71
  }: CalendarCellProps) {
72
+ const today = isToday ?? current;
73
+ const disabled = isDisabled ?? muted;
74
+
44
75
  return (
45
76
  <div
46
77
  className={clsx(
47
78
  "group relative flex min-h-[120px] flex-col gap-xs bg-bg-primary p-md font-body",
48
- muted && "bg-bg-secondary",
79
+ disabled && "bg-bg-secondary",
49
80
  className,
50
81
  )}
51
82
  >
52
83
  <span
53
84
  className={clsx(
54
- "text-xs font-semibold",
55
- current
56
- ? "text-text-brand-secondary"
57
- : muted
58
- ? "text-text-quaternary"
59
- : "text-text-secondary",
85
+ "flex size-6 items-center justify-center text-xs font-semibold",
86
+ today
87
+ ? "rounded-full bg-bg-brand-solid text-white"
88
+ : isSelected
89
+ ? "rounded-full bg-bg-secondary text-text-secondary"
90
+ : disabled
91
+ ? "text-text-quaternary"
92
+ : "text-text-secondary",
60
93
  )}
61
94
  >
62
95
  {date}
63
96
  </span>
97
+ {events && events.length > 0 ? (
98
+ <div
99
+ className={clsx(
100
+ breakpoint === "mobile"
101
+ ? "flex flex-row flex-wrap gap-xxs"
102
+ : "flex flex-col gap-xs",
103
+ )}
104
+ >
105
+ {events.map((ev, i) => (
106
+ <CalendarEvent
107
+ key={i}
108
+ title={ev.title}
109
+ time={ev.time}
110
+ color={ev.color}
111
+ filled
112
+ breakpoint={breakpoint}
113
+ />
114
+ ))}
115
+ </div>
116
+ ) : null}
64
117
  {children ? <div className="flex flex-col gap-xs">{children}</div> : null}
65
118
  {moreCount && moreCount > 0 ? (
66
119
  <span className="text-xs font-medium text-text-tertiary">
@@ -1,2 +1,6 @@
1
1
  export { CalendarCell } from "./CalendarCell";
2
- export type { CalendarCellProps } from "./CalendarCell";
2
+ export type {
3
+ CalendarCellProps,
4
+ CalendarCellEvent,
5
+ CalendarCellBreakpoint,
6
+ } from "./CalendarCell";
@@ -1,7 +1,22 @@
1
1
  import type { ReactNode } from "react";
2
2
  import clsx from "clsx";
3
3
 
4
+ export type CalendarTimeslotType =
5
+ | "empty"
6
+ | "30min"
7
+ | "60min"
8
+ | "90min"
9
+ | "120min";
10
+ export type CalendarTimeslotState = "default" | "hover";
11
+
4
12
  export interface CalendarCellDayWeekViewProps {
13
+ /**
14
+ * Slot duration — drives the row height (empty/30min → 48px, 60min → 96px,
15
+ * 90min → 144px, 120min → 192px). Omit to keep the auto min-height.
16
+ */
17
+ type?: CalendarTimeslotType;
18
+ /** `hover` tints the slot (also rendered via CSS :hover). */
19
+ state?: CalendarTimeslotState;
5
20
  /** Event block(s) occupying the slot — typically CalendarEventDayWeekView. */
6
21
  children?: ReactNode;
7
22
  /** Renders a hover "+" add button bottom-right. */
@@ -11,6 +26,14 @@ export interface CalendarCellDayWeekViewProps {
11
26
  className?: string;
12
27
  }
13
28
 
29
+ const heightClasses: Record<CalendarTimeslotType, string> = {
30
+ empty: "h-[48px]",
31
+ "30min": "h-[48px]",
32
+ "60min": "h-[96px]",
33
+ "90min": "h-[144px]",
34
+ "120min": "h-[192px]",
35
+ };
36
+
14
37
  function PlusIcon() {
15
38
  return (
16
39
  <svg viewBox="0 0 20 20" fill="none" className="size-4" aria-hidden>
@@ -27,6 +50,8 @@ function PlusIcon() {
27
50
 
28
51
  /** A time-slot cell in day/week view. Holds CalendarEventDayWeekView blocks. */
29
52
  export function CalendarCellDayWeekView({
53
+ type,
54
+ state = "default",
30
55
  children,
31
56
  onAdd,
32
57
  muted = false,
@@ -35,8 +60,9 @@ export function CalendarCellDayWeekView({
35
60
  return (
36
61
  <div
37
62
  className={clsx(
38
- "group relative min-h-[64px] border-b border-border-secondary p-xs font-body",
39
- muted ? "bg-bg-secondary" : "bg-bg-primary",
63
+ "group relative border-b border-border-secondary p-xs font-body transition-colors hover:bg-bg-secondary",
64
+ type ? heightClasses[type] : "min-h-[64px]",
65
+ muted ? "bg-bg-secondary" : state === "hover" ? "bg-bg-secondary" : "bg-bg-primary",
40
66
  className,
41
67
  )}
42
68
  >
@@ -1,2 +1,6 @@
1
1
  export { CalendarCellDayWeekView } from "./CalendarCellDayWeekView";
2
- export type { CalendarCellDayWeekViewProps } from "./CalendarCellDayWeekView";
2
+ export type {
3
+ CalendarCellDayWeekViewProps,
4
+ CalendarTimeslotType,
5
+ CalendarTimeslotState,
6
+ } from "./CalendarCellDayWeekView";
@@ -1,30 +1,45 @@
1
1
  import clsx from "clsx";
2
2
 
3
3
  export type CalendarColumnHeaderOrientation = "horizontal" | "vertical";
4
+ export type CalendarColumnHeaderType = "default" | "selected" | "today";
5
+ export type CalendarBreakpoint = "desktop" | "mobile";
4
6
 
5
7
  export interface CalendarColumnHeaderProps {
6
8
  /** Weekday label (e.g. "Mon"). */
7
9
  weekday: string;
8
10
  /** Date number. */
9
11
  date: string | number;
10
- /** Active day — renders the date in a brand-solid circle. */
12
+ /**
13
+ * Cell state. `selected` → brand-solid circle behind the date; `today` →
14
+ * brand text + brand underline. Takes precedence over `current` when set.
15
+ */
16
+ type?: CalendarColumnHeaderType;
17
+ /** Legacy boolean — equivalent to `type="selected"`. Kept for back-compat. */
11
18
  current?: boolean;
12
19
  orientation?: CalendarColumnHeaderOrientation;
20
+ /** Desktop fixes the column to 160px; mobile lets it size to content. */
21
+ breakpoint?: CalendarBreakpoint;
13
22
  className?: string;
14
23
  }
15
24
 
16
25
  export function CalendarColumnHeader({
17
26
  weekday,
18
27
  date,
28
+ type,
19
29
  current = false,
20
30
  orientation = "vertical",
31
+ breakpoint = "desktop",
21
32
  className,
22
33
  }: CalendarColumnHeaderProps) {
34
+ // `type` wins; otherwise the legacy `current` flag maps to "selected".
35
+ const state: CalendarColumnHeaderType = type ?? (current ? "selected" : "default");
36
+
23
37
  return (
24
38
  <div
25
39
  className={clsx(
26
40
  "flex items-center justify-center gap-md py-md font-body",
27
41
  orientation === "vertical" && "flex-col gap-xs",
42
+ breakpoint === "desktop" && "w-[160px]",
28
43
  className,
29
44
  )}
30
45
  >
@@ -32,11 +47,12 @@ export function CalendarColumnHeader({
32
47
  <span
33
48
  className={clsx(
34
49
  "flex items-center justify-center text-xs font-semibold",
35
- current
36
- ? "size-6 rounded-full bg-brand-solid text-white"
37
- : "text-text-secondary",
50
+ state === "selected" && "size-6 rounded-full bg-bg-brand-solid text-white",
51
+ state === "today" &&
52
+ "border-b-2 border-border-brand pb-xxs text-text-brand-secondary",
53
+ state === "default" && "text-text-secondary",
38
54
  )}
39
- aria-current={current ? "date" : undefined}
55
+ aria-current={state !== "default" ? "date" : undefined}
40
56
  >
41
57
  {date}
42
58
  </span>
@@ -2,4 +2,6 @@ export { CalendarColumnHeader } from "./CalendarColumnHeader";
2
2
  export type {
3
3
  CalendarColumnHeaderProps,
4
4
  CalendarColumnHeaderOrientation,
5
+ CalendarColumnHeaderType,
6
+ CalendarBreakpoint,
5
7
  } from "./CalendarColumnHeader";
@@ -11,6 +11,9 @@ export type CalendarEventColor =
11
11
  | "orange"
12
12
  | "amber";
13
13
 
14
+ export type CalendarEventBreakpoint = "desktop" | "mobile";
15
+ export type CalendarEventState = "default" | "hover";
16
+
14
17
  export interface CalendarEventProps {
15
18
  /** Event title. */
16
19
  title: string;
@@ -19,10 +22,14 @@ export interface CalendarEventProps {
19
22
  color?: CalendarEventColor;
20
23
  /** Filled colour-fill style vs the subtle white style. */
21
24
  filled?: boolean;
25
+ /** Desktop renders the full chip; mobile collapses to an 8px dot. */
26
+ breakpoint?: CalendarEventBreakpoint;
27
+ /** `hover` darkens the fill one shade (filled chips only). */
28
+ state?: CalendarEventState;
22
29
  className?: string;
23
30
  }
24
31
 
25
- // Static class maps (utility-<c>-{100,500,700}) — all present in the preset.
32
+ // Status dot (utility-<c>-500) — also the mobile collapsed form.
26
33
  const dotClasses: Record<CalendarEventColor, string> = {
27
34
  neutral: "bg-utility-neutral-500",
28
35
  brand: "bg-utility-brand-500",
@@ -35,16 +42,43 @@ const dotClasses: Record<CalendarEventColor, string> = {
35
42
  amber: "bg-utility-amber-500",
36
43
  };
37
44
 
45
+ // Filled chip — Figma-confirmed: bg-50 / border-200 / title text-700 (node 7991:54084).
38
46
  const filledClasses: Record<CalendarEventColor, string> = {
39
- neutral: "bg-utility-neutral-100 text-utility-neutral-700",
40
- brand: "bg-utility-brand-100 text-utility-brand-700",
41
- emerald: "bg-utility-emerald-100 text-utility-emerald-700",
42
- blue: "bg-utility-blue-100 text-utility-blue-700",
43
- indigo: "bg-utility-indigo-100 text-utility-indigo-700",
44
- purple: "bg-utility-purple-100 text-utility-purple-700",
45
- pink: "bg-utility-pink-100 text-utility-pink-700",
46
- orange: "bg-utility-orange-100 text-utility-orange-700",
47
- amber: "bg-utility-amber-100 text-utility-amber-700",
47
+ neutral: "bg-utility-neutral-50 border-utility-neutral-200 text-utility-neutral-700",
48
+ brand: "bg-utility-brand-50 border-utility-brand-200 text-utility-brand-700",
49
+ emerald: "bg-utility-emerald-50 border-utility-emerald-200 text-utility-emerald-700",
50
+ blue: "bg-utility-blue-50 border-utility-blue-200 text-utility-blue-700",
51
+ indigo: "bg-utility-indigo-50 border-utility-indigo-200 text-utility-indigo-700",
52
+ purple: "bg-utility-purple-50 border-utility-purple-200 text-utility-purple-700",
53
+ pink: "bg-utility-pink-50 border-utility-pink-200 text-utility-pink-700",
54
+ orange: "bg-utility-orange-50 border-utility-orange-200 text-utility-orange-700",
55
+ amber: "bg-utility-amber-50 border-utility-amber-200 text-utility-amber-700",
56
+ };
57
+
58
+ // Hover fill — one shade darker (bg-100).
59
+ const filledHoverClasses: Record<CalendarEventColor, string> = {
60
+ neutral: "bg-utility-neutral-100",
61
+ brand: "bg-utility-brand-100",
62
+ emerald: "bg-utility-emerald-100",
63
+ blue: "bg-utility-blue-100",
64
+ indigo: "bg-utility-indigo-100",
65
+ purple: "bg-utility-purple-100",
66
+ pink: "bg-utility-pink-100",
67
+ orange: "bg-utility-orange-100",
68
+ amber: "bg-utility-amber-100",
69
+ };
70
+
71
+ // Time-label colour for filled chips (utility-<c>-600).
72
+ const timeClasses: Record<CalendarEventColor, string> = {
73
+ neutral: "text-utility-neutral-600",
74
+ brand: "text-utility-brand-600",
75
+ emerald: "text-utility-emerald-600",
76
+ blue: "text-utility-blue-600",
77
+ indigo: "text-utility-indigo-600",
78
+ purple: "text-utility-purple-600",
79
+ pink: "text-utility-pink-600",
80
+ orange: "text-utility-orange-600",
81
+ amber: "text-utility-amber-600",
48
82
  };
49
83
 
50
84
  export function CalendarEvent({
@@ -52,22 +86,43 @@ export function CalendarEvent({
52
86
  time,
53
87
  color = "neutral",
54
88
  filled = false,
89
+ breakpoint = "desktop",
90
+ state = "default",
55
91
  className,
56
92
  }: CalendarEventProps) {
93
+ // Mobile view collapses the chip down to a single 8px status dot.
94
+ if (breakpoint === "mobile") {
95
+ return (
96
+ <span
97
+ className={clsx("inline-block size-2 rounded-full", dotClasses[color], className)}
98
+ role="img"
99
+ aria-label={time ? `${title}, ${time}` : title}
100
+ />
101
+ );
102
+ }
103
+
57
104
  return (
58
105
  <div
59
106
  className={clsx(
60
- "flex w-full items-center gap-md rounded-sm px-md py-xs font-body text-xs",
107
+ "flex w-full items-center gap-md rounded-sm border px-md py-xs font-body text-xs",
61
108
  filled
62
- ? filledClasses[color]
63
- : "border border-border-secondary bg-bg-primary text-text-secondary",
109
+ ? clsx(filledClasses[color], state === "hover" && filledHoverClasses[color])
110
+ : clsx(
111
+ "border-border-secondary bg-bg-primary text-text-secondary",
112
+ state === "hover" && "bg-bg-primary-hover",
113
+ ),
64
114
  className,
65
115
  )}
66
116
  >
67
117
  <span className={clsx("size-2 shrink-0 rounded-full", dotClasses[color])} aria-hidden />
68
118
  <span className="truncate font-semibold">{title}</span>
69
119
  {time ? (
70
- <span className={clsx("ml-auto shrink-0 font-normal", !filled && "text-text-tertiary")}>
120
+ <span
121
+ className={clsx(
122
+ "ml-auto shrink-0 font-normal",
123
+ filled ? timeClasses[color] : "text-text-tertiary",
124
+ )}
125
+ >
71
126
  {time}
72
127
  </span>
73
128
  ) : null}
@@ -1,2 +1,7 @@
1
1
  export { CalendarEvent } from "./CalendarEvent";
2
- export type { CalendarEventProps, CalendarEventColor } from "./CalendarEvent";
2
+ export type {
3
+ CalendarEventProps,
4
+ CalendarEventColor,
5
+ CalendarEventBreakpoint,
6
+ CalendarEventState,
7
+ } from "./CalendarEvent";
@@ -6,6 +6,8 @@ export interface CalendarHeaderProps {
6
6
  title: string;
7
7
  /** Date range subtitle, e.g. "Jan 1, 2027 – Jan 31, 2027". */
8
8
  range?: string;
9
+ /** Alias for `range` — Batch-36 prop name. `range` wins if both set. */
10
+ supportingText?: string;
9
11
  /** Badge beside the title (e.g. a PillBadge "Week 1"). */
10
12
  badge?: ReactNode;
11
13
  /** Leading date chip — typically a CalendarDateIcon. */
@@ -19,11 +21,13 @@ export interface CalendarHeaderProps {
19
21
  export function CalendarHeader({
20
22
  title,
21
23
  range,
24
+ supportingText,
22
25
  badge,
23
26
  dateIcon,
24
27
  actions,
25
28
  className,
26
29
  }: CalendarHeaderProps) {
30
+ const subtitle = range ?? supportingText;
27
31
  return (
28
32
  <div
29
33
  className={clsx(
@@ -38,7 +42,7 @@ export function CalendarHeader({
38
42
  <h2 className="text-lg font-bold text-text-primary">{title}</h2>
39
43
  {badge}
40
44
  </div>
41
- {range ? <p className="text-sm text-text-tertiary">{range}</p> : null}
45
+ {subtitle ? <p className="text-sm text-text-tertiary">{subtitle}</p> : null}
42
46
  </div>
43
47
  </div>
44
48
  {actions ? <div className="flex items-center gap-md">{actions}</div> : null}
@@ -2,12 +2,14 @@ import clsx from "clsx";
2
2
 
3
3
  export interface CalendarRowLabelProps {
4
4
  /** Time label for the row gutter (e.g. "9 AM"). */
5
- label: string;
5
+ label?: string;
6
+ /** Alias for `label` — Batch-36 prop name. One of the two is required. */
7
+ time?: string;
6
8
  className?: string;
7
9
  }
8
10
 
9
11
  /** Left-gutter time label aligned to the top of a day/week-view row. */
10
- export function CalendarRowLabel({ label, className }: CalendarRowLabelProps) {
12
+ export function CalendarRowLabel({ label, time, className }: CalendarRowLabelProps) {
11
13
  return (
12
14
  <div
13
15
  className={clsx(
@@ -15,7 +17,7 @@ export function CalendarRowLabel({ label, className }: CalendarRowLabelProps) {
15
17
  className,
16
18
  )}
17
19
  >
18
- {label}
20
+ {time ?? label}
19
21
  </div>
20
22
  );
21
23
  }
@@ -1,12 +1,15 @@
1
1
  import clsx from "clsx";
2
2
 
3
3
  export type CalendarTimemarkerAlign = "left" | "center";
4
+ export type CalendarTimemarkerBreakpoint = "desktop" | "mobile";
4
5
 
5
6
  export interface CalendarTimemarkerProps {
6
7
  /** Time label (e.g. "2:20 PM"). */
7
8
  time: string;
8
9
  /** Label position: leading, or centered between two line halves. */
9
10
  align?: CalendarTimemarkerAlign;
11
+ /** Desktop shows the time label; mobile collapses to dot + line only. */
12
+ breakpoint?: CalendarTimemarkerBreakpoint;
10
13
  className?: string;
11
14
  }
12
15
 
@@ -23,20 +26,23 @@ const Label = ({ time }: { time: string }) => (
23
26
  export function CalendarTimemarker({
24
27
  time,
25
28
  align = "left",
29
+ breakpoint = "desktop",
26
30
  className,
27
31
  }: CalendarTimemarkerProps) {
32
+ const showLabel = breakpoint === "desktop";
33
+
28
34
  return (
29
35
  <div className={clsx("flex w-full items-center gap-xs font-body", className)}>
30
36
  {align === "center" ? (
31
37
  <>
32
38
  <Dot />
33
39
  <Line />
34
- <Label time={time} />
40
+ {showLabel ? <Label time={time} /> : null}
35
41
  <Line />
36
42
  </>
37
43
  ) : (
38
44
  <>
39
- <Label time={time} />
45
+ {showLabel ? <Label time={time} /> : null}
40
46
  <Dot />
41
47
  <Line />
42
48
  </>
@@ -2,4 +2,5 @@ export { CalendarTimemarker } from "./CalendarTimemarker";
2
2
  export type {
3
3
  CalendarTimemarkerProps,
4
4
  CalendarTimemarkerAlign,
5
+ CalendarTimemarkerBreakpoint,
5
6
  } from "./CalendarTimemarker";
@@ -12,6 +12,12 @@ export interface CalendarViewOption {
12
12
  export interface CalendarViewDropdownProps {
13
13
  value: CalendarView;
14
14
  onChange?: (value: CalendarView) => void;
15
+ /** Alias for `onChange` — Batch-36 prop name. Both fire on selection. */
16
+ onSelect?: (value: CalendarView) => void;
17
+ /** Controlled open state. Omit to let the component manage its own. */
18
+ open?: boolean;
19
+ /** Fires whenever the menu wants to open/close (controlled or not). */
20
+ onOpenChange?: (open: boolean) => void;
15
21
  options?: CalendarViewOption[];
16
22
  className?: string;
17
23
  }
@@ -39,10 +45,19 @@ function Chevron() {
39
45
  export function CalendarViewDropdown({
40
46
  value,
41
47
  onChange,
48
+ onSelect,
49
+ open: openProp,
50
+ onOpenChange,
42
51
  options = DEFAULT_OPTIONS,
43
52
  className,
44
53
  }: CalendarViewDropdownProps) {
45
- const [open, setOpen] = useState(false);
54
+ const [internalOpen, setInternalOpen] = useState(false);
55
+ const isControlled = openProp !== undefined;
56
+ const open = isControlled ? openProp : internalOpen;
57
+ const setOpen = (next: boolean) => {
58
+ if (!isControlled) setInternalOpen(next);
59
+ onOpenChange?.(next);
60
+ };
46
61
  const selected = options.find((o) => o.value === value) ?? options[0];
47
62
 
48
63
  return (
@@ -51,7 +66,7 @@ export function CalendarViewDropdown({
51
66
  type="button"
52
67
  aria-haspopup="listbox"
53
68
  aria-expanded={open}
54
- onClick={() => setOpen((v) => !v)}
69
+ onClick={() => setOpen(!open)}
55
70
  className="inline-flex items-center gap-md rounded-md border border-border-primary bg-bg-primary px-lg py-md text-sm font-semibold text-text-secondary shadow-skeuomorphic transition-colors hover:bg-bg-primary-hover focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-brand focus-visible:ring-offset-2 focus-visible:ring-offset-bg-primary"
56
71
  >
57
72
  {selected.label}
@@ -72,6 +87,7 @@ export function CalendarViewDropdown({
72
87
  type="button"
73
88
  onClick={() => {
74
89
  onChange?.(opt.value);
90
+ onSelect?.(opt.value);
75
91
  setOpen(false);
76
92
  }}
77
93
  className="flex w-full items-center gap-md rounded-sm px-md py-xs text-sm font-medium text-text-secondary transition-colors hover:bg-bg-primary-hover"
@@ -83,7 +99,7 @@ export function CalendarViewDropdown({
83
99
  )}
84
100
  >
85
101
  {isSelected ? (
86
- <span className="size-2 rounded-full bg-brand-solid" />
102
+ <span className="size-2 rounded-full bg-bg-brand-solid" />
87
103
  ) : null}
88
104
  </span>
89
105
  <span className="flex-1 text-left">{opt.label}</span>
@@ -20,7 +20,7 @@ export interface ChartTooltipProps {
20
20
 
21
21
  /**
22
22
  * Themed tooltip card for the Recharts chart wrappers — pass as the chart's
23
- * `<Tooltip content={<ChartTooltip />} />`. Renders a `bg-primary` card with the
23
+ * `<Tooltip content={<ChartTooltip />} />`. Renders a `bg-bg-primary` card with the
24
24
  * point label and one colored-dot row per series. Returns null when inactive.
25
25
  */
26
26
  export function ChartTooltip({ active, payload, label, formatValue }: ChartTooltipProps) {
@@ -46,7 +46,7 @@ export function CheckControlVisual({
46
46
  disabled
47
47
  ? "border-border-primary bg-bg-tertiary"
48
48
  : active
49
- ? "border-brand-solid bg-brand-solid"
49
+ ? "border-bg-brand-solid bg-bg-brand-solid"
50
50
  : "border-border-primary bg-bg-primary",
51
51
  )}
52
52
  >
@@ -14,12 +14,13 @@ export interface ContentFeatureTextProps {
14
14
  }
15
15
 
16
16
  // md verified (p-4xl, rounded-2xl, heading text-xl, body text-lg).
17
- // sm inferred (scaled down a step).
17
+ // sm verified 2026-06-27 (node 3959:413352): p-3xl, rounded-2xl, heading text-lg,
18
+ // body text-md. Radius corrected rounded-xl→rounded-2xl (was inferred wrong).
18
19
  const sizeConfig: Record<
19
20
  ContentFeatureTextSize,
20
21
  { box: string; heading: string; body: string }
21
22
  > = {
22
- sm: { box: "p-3xl rounded-xl", heading: "text-lg", body: "text-md" },
23
+ sm: { box: "p-3xl rounded-2xl", heading: "text-lg", body: "text-md" },
23
24
  md: { box: "p-4xl rounded-2xl", heading: "text-xl", body: "text-lg" },
24
25
  };
25
26
 
@@ -18,7 +18,7 @@ export interface ContextMenuProps {
18
18
 
19
19
  /**
20
20
  * A thin positioned panel for right-click menus. Reuses the dropdown panel
21
- * tokens (shadow-lg, border-secondary-alt, radius-md) and renders whatever
21
+ * tokens (shadow-lg, border-border-secondary-alt, radius-md) and renders whatever
22
22
  * rows you pass — compose DropdownMenuListItem children; it does NOT
23
23
  * reimplement list items. Pair with `useContextMenu`.
24
24
  */