@devalok/shilp-sutra 0.52.0 → 0.54.0

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 (123) hide show
  1. package/AGENTS.md +7 -1
  2. package/BREAKING.json +29 -0
  3. package/MIGRATION.md +61 -0
  4. package/dist/_chunks/motion-provider.js +7 -6
  5. package/dist/_chunks/motion-provider.js.map +1 -1
  6. package/dist/_chunks/success.js +53 -53
  7. package/dist/ai/command-bar.js +158 -158
  8. package/dist/ai/command-bar.js.map +1 -1
  9. package/dist/ai/conversation.js +5 -5
  10. package/dist/composed/avatar-group.d.ts +11 -9
  11. package/dist/composed/avatar-group.d.ts.map +1 -1
  12. package/dist/composed/avatar-group.js +108 -140
  13. package/dist/composed/avatar-group.js.map +1 -1
  14. package/dist/composed/bulk-action-bar.d.ts +19 -11
  15. package/dist/composed/bulk-action-bar.d.ts.map +1 -1
  16. package/dist/composed/bulk-action-bar.js +81 -110
  17. package/dist/composed/bulk-action-bar.js.map +1 -1
  18. package/dist/composed/command-palette.js +17 -17
  19. package/dist/composed/command-palette.js.map +1 -1
  20. package/dist/composed/error-boundary.d.ts +15 -3
  21. package/dist/composed/error-boundary.d.ts.map +1 -1
  22. package/dist/composed/error-boundary.js +43 -26
  23. package/dist/composed/error-boundary.js.map +1 -1
  24. package/dist/composed/loading-skeleton.d.ts +2 -0
  25. package/dist/composed/loading-skeleton.d.ts.map +1 -1
  26. package/dist/composed/loading-skeleton.js +79 -56
  27. package/dist/composed/loading-skeleton.js.map +1 -1
  28. package/dist/composed/master-detail.d.ts +21 -8
  29. package/dist/composed/master-detail.d.ts.map +1 -1
  30. package/dist/composed/master-detail.js +91 -77
  31. package/dist/composed/master-detail.js.map +1 -1
  32. package/dist/composed/page-skeletons.d.ts +6 -0
  33. package/dist/composed/page-skeletons.d.ts.map +1 -1
  34. package/dist/composed/page-skeletons.js +93 -71
  35. package/dist/composed/page-skeletons.js.map +1 -1
  36. package/dist/composed/priority-indicator.d.ts +15 -6
  37. package/dist/composed/priority-indicator.d.ts.map +1 -1
  38. package/dist/composed/priority-indicator.js +37 -88
  39. package/dist/composed/priority-indicator.js.map +1 -1
  40. package/dist/composed/schedule-view.d.ts +17 -2
  41. package/dist/composed/schedule-view.d.ts.map +1 -1
  42. package/dist/composed/schedule-view.js +163 -64
  43. package/dist/composed/schedule-view.js.map +1 -1
  44. package/dist/motion/motion-provider.d.ts.map +1 -1
  45. package/dist/shell/bottom-navbar.d.ts +32 -6
  46. package/dist/shell/bottom-navbar.d.ts.map +1 -1
  47. package/dist/shell/bottom-navbar.js +156 -129
  48. package/dist/shell/bottom-navbar.js.map +1 -1
  49. package/dist/shell/index.d.ts +5 -3
  50. package/dist/shell/index.d.ts.map +1 -1
  51. package/dist/shell/index.js +2 -3
  52. package/dist/shell/notification-preferences.d.ts.map +1 -1
  53. package/dist/shell/notification-preferences.js +2 -0
  54. package/dist/shell/notification-preferences.js.map +1 -1
  55. package/dist/tokens/primitives.css +6 -2
  56. package/dist/tokens/utilities.css +6 -0
  57. package/dist/ui/autocomplete.d.ts +34 -32
  58. package/dist/ui/autocomplete.d.ts.map +1 -1
  59. package/dist/ui/autocomplete.js +115 -110
  60. package/dist/ui/autocomplete.js.map +1 -1
  61. package/dist/ui/combobox.d.ts +1 -1
  62. package/dist/ui/combobox.d.ts.map +1 -1
  63. package/dist/ui/combobox.js +133 -128
  64. package/dist/ui/combobox.js.map +1 -1
  65. package/dist/ui/file-upload.d.ts.map +1 -1
  66. package/dist/ui/file-upload.js +124 -138
  67. package/dist/ui/file-upload.js.map +1 -1
  68. package/dist/ui/form.d.ts +7 -0
  69. package/dist/ui/form.d.ts.map +1 -1
  70. package/dist/ui/form.js +4 -2
  71. package/dist/ui/form.js.map +1 -1
  72. package/dist/ui/icon-button.d.ts +2 -1
  73. package/dist/ui/icon-button.d.ts.map +1 -1
  74. package/dist/ui/icon-button.js +1 -1
  75. package/dist/ui/icon-button.js.map +1 -1
  76. package/dist/ui/index.js +30 -30
  77. package/dist/ui/label.d.ts.map +1 -1
  78. package/dist/ui/label.js +7 -6
  79. package/dist/ui/label.js.map +1 -1
  80. package/dist/ui/search-input.js +4 -4
  81. package/dist/ui/sidebar.js +7 -7
  82. package/dist/ui/spinner.d.ts.map +1 -1
  83. package/dist/ui/spinner.js +50 -44
  84. package/dist/ui/spinner.js.map +1 -1
  85. package/dist/ui/switch.d.ts.map +1 -1
  86. package/dist/ui/switch.js +24 -24
  87. package/dist/ui/switch.js.map +1 -1
  88. package/dist/ui/table.js +1 -1
  89. package/dist/ui/table.js.map +1 -1
  90. package/docs/components/composed/bulk-action-bar.md +9 -3
  91. package/docs/components/composed/error-boundary.md +18 -2
  92. package/docs/components/composed/master-detail.md +12 -2
  93. package/docs/components/composed/page-skeletons.md +2 -2
  94. package/docs/components/composed/priority-indicator.md +22 -11
  95. package/docs/components/composed/rich-chat-input.md +18 -6
  96. package/docs/components/composed/schedule-view.md +20 -5
  97. package/docs/components/composed/simple-tooltip.md +1 -1
  98. package/docs/components/shell/app-command-palette.md +2 -1
  99. package/docs/components/shell/bottom-navbar.md +28 -9
  100. package/docs/components/shell/command-registry.md +2 -2
  101. package/docs/components/shell/link-context.md +2 -2
  102. package/docs/components/shell/top-bar.md +1 -1
  103. package/docs/components/ui/autocomplete.md +26 -10
  104. package/docs/components/ui/search-input.md +2 -2
  105. package/docs/components/ui/slider.md +2 -2
  106. package/docs/components/ui/split-button.md +3 -3
  107. package/llms.txt +2 -2
  108. package/make-kit/Guidelines.md +1 -1
  109. package/make-kit/components/overview.md +2 -2
  110. package/make-kit/foundations/surfaces.md +1 -1
  111. package/mcp-manifest.json +581 -172
  112. package/package.json +1 -6
  113. package/skill/SKILL.md +1 -1
  114. package/skill/chat-skill/README.md +18 -0
  115. package/skill/chat-skill/SKILL.md +78 -0
  116. package/skill/chat-skill/chatgpt-instructions.md +26 -0
  117. package/skill/chat-skill/gemini-instructions.md +25 -0
  118. package/skill/references/components.md +2 -2
  119. package/dist/shell/sidebar.d.ts +0 -113
  120. package/dist/shell/sidebar.d.ts.map +0 -1
  121. package/dist/shell/sidebar.js +0 -293
  122. package/dist/shell/sidebar.js.map +0 -1
  123. package/docs/components/shell/sidebar.md +0 -103
@@ -24,71 +24,111 @@ function h(e) {
24
24
  return e === 0 ? "12 AM" : e < 12 ? `${e} AM` : e === 12 ? "12 PM" : `${e - 12} PM`;
25
25
  }
26
26
  function g(e, t, n) {
27
- let r = (n - t) * 60, i = (c(e.start) - t) * 60 + l(e.start), a = o(e.end, e.start), s = i / r * 100, u = a / r * 100;
28
- return {
29
- top: `${s}%`,
30
- height: `${u}%`
27
+ let r = (n - t) * 60, i = [...e].sort((e, t) => e.start.getTime() - t.start.getTime()), a = [], s = [], u = -Infinity, d = () => {
28
+ let e = s.reduce((e, t) => Math.max(e, t.col + 1), 0);
29
+ for (let t of s) t.cols = e;
30
+ a.push(...s), s = [], u = -Infinity;
31
31
  };
32
+ for (let e of i) {
33
+ let n = (c(e.start) - t) * 60 + l(e.start), i = Math.max(o(e.end, e.start), 1), a = e.start.getTime();
34
+ a >= u && s.length > 0 && d();
35
+ let f = new Set(s.filter((e) => e.event.end.getTime() > a).map((e) => e.col)), p = 0;
36
+ for (; f.has(p);) p++;
37
+ s.push({
38
+ event: e,
39
+ top: n / r * 100,
40
+ height: i / r * 100,
41
+ col: p,
42
+ cols: 1
43
+ }), u = Math.max(u, e.end.getTime());
44
+ }
45
+ return s.length > 0 && d(), a;
32
46
  }
33
47
  function _({ startHour: e, endHour: t }) {
34
48
  let n = [];
35
49
  for (let r = e; r < t; r++) n.push(r);
36
50
  let i = 100 / (t - e);
37
51
  return /* @__PURE__ */ r("div", {
38
- className: "relative shrink-0 w-[60px] border-r border-surface-border-strong",
52
+ className: "relative w-ds-11 shrink-0 border-e border-surface-border-strong",
39
53
  "aria-hidden": "true",
40
54
  children: n.map((e) => /* @__PURE__ */ r("div", {
41
- className: "text-caption text-surface-fg-muted pr-ds-02 text-right",
55
+ className: "pe-ds-02 text-end text-caption text-surface-fg-muted",
42
56
  style: { height: `${i}%` },
43
57
  children: h(e)
44
58
  }, e))
45
59
  });
46
60
  }
47
- function v({ date: n, events: a, startHour: o, endHour: f, slotDuration: h, onEventClick: _, onSlotClick: v, showHeader: y }) {
48
- let b = a.filter((e) => u(e.start, n)), x = (f - o) * 60, S = x / h, C = [];
49
- for (let e = 0; e < S; e++) {
50
- let t = new Date(n);
51
- t.setHours(o, e * h, 0, 0);
52
- let r = new Date(t);
53
- r.setMinutes(r.getMinutes() + h), C.push({
54
- start: t,
55
- end: r
56
- });
57
- }
58
- let w = d(n), T = null;
59
- if (w) {
60
- let e = /* @__PURE__ */ new Date(), t = c(e), n = l(e);
61
- t >= o && t < f && (T = ((t - o) * 60 + n) / x * 100);
61
+ function v({ dayIndex: a, date: o, events: f, startHour: h, endHour: _, slotDuration: v, now: y, active: b, interactive: x, onSlotKeyDown: S, selectedEventId: C, renderEvent: w, onEventClick: T, onSlotClick: E, showHeader: D, nowLineRef: O }) {
62
+ let k = n.useMemo(() => g(f.filter((e) => u(e.start, o)), h, _), [
63
+ f,
64
+ o,
65
+ h,
66
+ _
67
+ ]), A = (_ - h) * 60, j = A / v, M = n.useMemo(() => {
68
+ let e = [];
69
+ for (let t = 0; t < j; t++) {
70
+ let n = new Date(o);
71
+ n.setHours(h, t * v, 0, 0);
72
+ let r = new Date(n);
73
+ r.setMinutes(r.getMinutes() + v), e.push({
74
+ start: n,
75
+ end: r
76
+ });
77
+ }
78
+ return e;
79
+ }, [
80
+ o,
81
+ h,
82
+ v,
83
+ j
84
+ ]), N = d(o), P = null;
85
+ if (N) {
86
+ let e = c(y);
87
+ e >= h && e < _ && (P = ((e - h) * 60 + l(y)) / A * 100);
62
88
  }
63
89
  return /* @__PURE__ */ i("div", {
64
- className: "flex flex-1 flex-col min-w-ds-11",
65
- children: [y && /* @__PURE__ */ r("div", {
66
- className: e("text-center text-body-sm font-semibold py-ds-02 border-b border-surface-border-strong", w ? "text-accent-11 bg-accent-2" : "text-surface-fg bg-surface-raised"),
67
- children: s(n, "EEE d")
90
+ className: "flex min-w-ds-11 flex-1 flex-col",
91
+ children: [D && /* @__PURE__ */ r("div", {
92
+ className: e("border-b border-surface-border-strong py-ds-02 text-center text-body-sm font-semibold", N ? "bg-accent-2 text-accent-11" : "bg-surface-raised text-surface-fg"),
93
+ children: s(o, "EEE d")
68
94
  }), /* @__PURE__ */ i("div", {
69
95
  className: "relative flex-1",
70
96
  children: [
71
- C.map((t, n) => /* @__PURE__ */ r("button", {
97
+ M.map((t, n) => x ? /* @__PURE__ */ r("button", {
72
98
  type: "button",
73
- className: e("block w-full border-b border-surface-border hover:bg-surface-raised-hover transition-colors ease-productive-standard", "focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-accent-9", n % 2 == 0 ? "border-surface-border-strong" : "border-surface-border-subtle"),
74
- style: { height: `${100 / S}%` },
75
- onClick: () => v?.(t.start, t.end),
76
- "aria-label": `${s(t.start, "h:mm a")} to ${s(t.end, "h:mm a")}`
99
+ "data-day": a,
100
+ "data-slot": n,
101
+ tabIndex: a === b.day && n === b.slot ? 0 : -1,
102
+ className: e("block w-full min-h-ds-06 border-b transition-colors ease-productive-standard duration-fast-02", "hover:bg-surface-raised-hover", "focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-accent-9", n % 2 == 0 ? "border-surface-border-strong" : "border-surface-border-subtle"),
103
+ style: { height: `${100 / j}%` },
104
+ onClick: () => E?.(t.start, t.end),
105
+ onKeyDown: S,
106
+ "aria-label": `${s(t.start, "EEE h:mm a")} to ${s(t.end, "h:mm a")}`
107
+ }, n) : /* @__PURE__ */ r("div", {
108
+ "aria-hidden": "true",
109
+ className: e("w-full border-b", n % 2 == 0 ? "border-surface-border-strong" : "border-surface-border-subtle"),
110
+ style: { height: `${100 / j}%` }
77
111
  }, n)),
78
- b.map((t) => {
79
- let n = g(t, o, f), a = t.color ?? "accent", c = p[a], l = m[a];
112
+ k.map(({ event: t, top: n, height: a, col: o, cols: c }) => {
113
+ let l = t.color ?? "accent", u = 100 / c, d = t.id === C;
80
114
  return /* @__PURE__ */ r("button", {
81
115
  type: "button",
82
- className: e("absolute left-ds-01 right-ds-01 rounded-control-inner px-ds-02 py-ds-01", "text-left text-body-xs font-medium overflow-hidden cursor-pointer", "hover:shadow-raised transition-[box-shadow] duration-fast-02 ease-productive-standard", "focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9", c),
83
- style: n,
116
+ "aria-pressed": d || void 0,
117
+ className: e("absolute overflow-hidden rounded-control-inner px-ds-02 py-ds-01", "cursor-pointer text-start text-body-xs font-medium", "transition-[box-shadow] ease-productive-standard duration-fast-02 hover:shadow-raised", "focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9", "motion-safe:active:scale-[0.98]", d && "ring-2 ring-accent-9", p[l]),
118
+ style: {
119
+ top: `${n}%`,
120
+ height: `${a}%`,
121
+ insetInlineStart: `calc(${o * u}% + 2px)`,
122
+ width: `calc(${u}% - 4px)`
123
+ },
84
124
  onClick: (e) => {
85
- e.stopPropagation(), _?.(t);
125
+ e.stopPropagation(), T?.(t);
86
126
  },
87
127
  "aria-label": `${t.title}, ${s(t.start, "h:mm a")} to ${s(t.end, "h:mm a")}`,
88
- children: /* @__PURE__ */ i("span", {
128
+ children: w ? w(t) : /* @__PURE__ */ i("span", {
89
129
  className: "flex items-start gap-ds-02",
90
130
  children: [/* @__PURE__ */ r("span", {
91
- className: e("mt-[3px] h-ds-03 w-ds-03 shrink-0 rounded-pill", l),
131
+ className: e("mt-0.5 h-ds-03 w-ds-03 shrink-0 rounded-pill", m[l]),
92
132
  "aria-hidden": "true"
93
133
  }), /* @__PURE__ */ r("span", {
94
134
  className: "line-clamp-2",
@@ -97,48 +137,107 @@ function v({ date: n, events: a, startHour: o, endHour: f, slotDuration: h, onEv
97
137
  })
98
138
  }, t.id);
99
139
  }),
100
- T != null && /* @__PURE__ */ r("div", {
101
- className: "absolute left-0 right-0 h-ds-01 bg-error-9 z-10 pointer-events-none",
102
- style: { top: `${T}%` },
140
+ P != null && /* @__PURE__ */ r("div", {
141
+ ref: O,
142
+ className: "pointer-events-none absolute inset-x-0 z-raised h-ds-01 bg-error-9",
143
+ style: { top: `${P}%` },
103
144
  "aria-hidden": "true",
104
145
  children: /* @__PURE__ */ r(t, {
105
146
  color: "error",
106
147
  size: "lg",
107
148
  pulse: !0,
108
- className: "absolute -left-[5px] -top-[4px]"
149
+ className: "absolute -translate-x-1/2 -translate-y-1/2"
109
150
  })
110
151
  })
111
152
  ]
112
153
  })]
113
154
  });
114
155
  }
115
- var y = n.forwardRef(({ view: t, date: o, events: c, onEventClick: l, onSlotClick: u, startHour: d = 8, endHour: p = 18, slotDuration: m = 30, className: h, ...g }, y) => {
116
- let b = n.useMemo(() => {
156
+ var y = n.forwardRef(({ view: t, date: o, events: c, onEventClick: l, onSlotClick: d, startHour: p = 8, endHour: m = 18, slotDuration: h = 30, selectedEventId: g, renderEvent: y, header: b, emptyState: x, height: S = 480, className: C, ...w }, T) => {
157
+ let E = n.useMemo(() => {
117
158
  if (t === "day") return [o];
118
159
  let e = f(o, { weekStartsOn: 1 });
119
160
  return Array.from({ length: 7 }, (t, n) => a(e, n));
120
- }, [t, o]);
161
+ }, [t, o]), D = !!d, O = (m - p) * 60 / h, [k, A] = n.useState(() => /* @__PURE__ */ new Date());
162
+ n.useEffect(() => {
163
+ let e = setInterval(() => A(/* @__PURE__ */ new Date()), 6e4);
164
+ return () => clearInterval(e);
165
+ }, []);
166
+ let j = n.useRef(null), M = n.useRef(null), [N, P] = n.useState({
167
+ day: 0,
168
+ slot: 0
169
+ });
170
+ n.useEffect(() => {
171
+ M.current?.scrollIntoView({ block: "center" });
172
+ }, []);
173
+ let F = (e) => {
174
+ if (!D) return;
175
+ let t = e.target, n = t.getAttribute("data-day"), r = t.getAttribute("data-slot");
176
+ if (n == null || r == null) return;
177
+ let i = Number(n), a = Number(r), o = getComputedStyle(j.current ?? t).direction === "rtl", s = i, c = a;
178
+ switch (e.key) {
179
+ case "ArrowDown":
180
+ c = Math.min(a + 1, O - 1);
181
+ break;
182
+ case "ArrowUp":
183
+ c = Math.max(a - 1, 0);
184
+ break;
185
+ case "ArrowRight":
186
+ s = o ? Math.max(i - 1, 0) : Math.min(i + 1, E.length - 1);
187
+ break;
188
+ case "ArrowLeft":
189
+ s = o ? Math.min(i + 1, E.length - 1) : Math.max(i - 1, 0);
190
+ break;
191
+ case "Home":
192
+ c = 0;
193
+ break;
194
+ case "End":
195
+ c = O - 1;
196
+ break;
197
+ default: return;
198
+ }
199
+ e.preventDefault(), P({
200
+ day: s,
201
+ slot: c
202
+ }), j.current?.querySelector(`[data-day="${s}"][data-slot="${c}"]`)?.focus();
203
+ }, I = c.length === 0;
121
204
  return /* @__PURE__ */ i("div", {
122
- ref: y,
123
- role: "region",
124
- "aria-label": t === "day" ? `Schedule for ${s(o, "EEEE, MMMM d, yyyy")}` : `Week schedule starting ${s(b[0], "MMMM d, yyyy")}`,
125
- className: e("flex rounded-control border border-card-strong bg-surface-raised overflow-hidden", "h-[480px]", h),
126
- ...g,
127
- children: [/* @__PURE__ */ r(_, {
128
- startHour: d,
129
- endHour: p
130
- }), /* @__PURE__ */ r("div", {
131
- className: "flex flex-1 divide-x divide-surface-border overflow-x-auto",
132
- children: b.map((e) => /* @__PURE__ */ r(v, {
133
- date: e,
134
- events: c,
135
- startHour: d,
136
- endHour: p,
137
- slotDuration: m,
138
- onEventClick: l,
139
- onSlotClick: u,
140
- showHeader: t === "week"
141
- }, e.toISOString()))
205
+ ref: T,
206
+ className: e("flex flex-col", C),
207
+ ...w,
208
+ children: [b, /* @__PURE__ */ i("div", {
209
+ ref: j,
210
+ role: "region",
211
+ "aria-label": t === "day" ? `Schedule for ${s(o, "EEEE, MMMM d, yyyy")}` : `Week schedule starting ${s(E[0], "MMMM d, yyyy")}`,
212
+ className: "flex overflow-hidden rounded-surface border border-surface-border-strong bg-surface-raised",
213
+ style: { height: typeof S == "number" ? `${S}px` : S },
214
+ children: [/* @__PURE__ */ r(_, {
215
+ startHour: p,
216
+ endHour: m
217
+ }), /* @__PURE__ */ i("div", {
218
+ className: "relative flex flex-1 divide-x divide-surface-border overflow-x-auto overflow-y-auto",
219
+ children: [E.map((e, n) => /* @__PURE__ */ r(v, {
220
+ dayIndex: n,
221
+ date: e,
222
+ events: c,
223
+ startHour: p,
224
+ endHour: m,
225
+ slotDuration: h,
226
+ now: k,
227
+ active: N,
228
+ interactive: D,
229
+ onSlotKeyDown: F,
230
+ selectedEventId: g,
231
+ renderEvent: y,
232
+ onEventClick: l,
233
+ onSlotClick: d,
234
+ showHeader: t === "week",
235
+ nowLineRef: n === 0 || u(e, k) ? M : void 0
236
+ }, e.toISOString())), I && /* @__PURE__ */ r("div", {
237
+ className: "pointer-events-none absolute inset-0 flex items-center justify-center p-ds-05 text-center text-body-sm text-surface-fg-muted",
238
+ children: x ?? "No events scheduled."
239
+ })]
240
+ })]
142
241
  })]
143
242
  });
144
243
  });
@@ -1 +1 @@
1
- {"version":3,"file":"schedule-view.js","names":[],"sources":["../../src/composed/schedule-view.tsx"],"sourcesContent":["'use client'\n\nimport {\n addDays,\n differenceInMinutes,\n format,\n getHours,\n getMinutes,\n isSameDay,\n isToday,\n startOfWeek,\n} from 'date-fns'\nimport * as React from 'react'\n\nimport { Dot } from '../ui/dot'\nimport { cn } from '../ui/lib/utils'\n\n/* ------------------------------------------------------------------ */\n/* Types */\n/* ------------------------------------------------------------------ */\n\nexport interface ScheduleEvent {\n id: string\n title: string\n start: Date\n end: Date\n color?: 'accent' | 'success' | 'warning' | 'error' | 'info' | 'neutral'\n}\n\n/**\n * A day/week calendar schedule grid. Renders time-slotted events with\n * proportional height, color coding, click handlers, and a current-time indicator.\n */\nexport interface ScheduleViewProps extends React.HTMLAttributes<HTMLDivElement> {\n /** Display mode: single-day column or full-week (7 columns). */\n view: 'day' | 'week'\n /** Current day (day view) or any date within the target week (week view). */\n date: Date\n /** Events to display on the calendar grid. */\n events: ScheduleEvent[]\n /** Called when an event block is clicked. */\n onEventClick?: (event: ScheduleEvent) => void\n /** Called when an empty time slot is clicked, with the slot's start and end times. */\n onSlotClick?: (start: Date, end: Date) => void\n /** First visible hour (default 8). */\n startHour?: number\n /** Last visible hour -- exclusive (default 18). */\n endHour?: number\n /** Slot duration in minutes (default 30). */\n slotDuration?: number\n}\n\n/* ------------------------------------------------------------------ */\n/* Color map */\n/* ------------------------------------------------------------------ */\n\nconst eventColorMap: Record<\n NonNullable<ScheduleEvent['color']>,\n string\n> = {\n accent: 'bg-accent-2 text-accent-11',\n success: 'bg-success-3 text-success-11',\n warning: 'bg-warning-3 text-warning-11',\n error: 'bg-error-3 text-error-11',\n info: 'bg-info-3 text-info-11',\n neutral: 'bg-surface-raised text-surface-fg-muted',\n}\n\n// Solid category dot — the color-blind-safe, shape-based signal that replaces the\n// former left accent rail (the AI tell). Tint above carries the ambient color.\nconst eventDotMap: Record<\n NonNullable<ScheduleEvent['color']>,\n string\n> = {\n accent: 'bg-accent-9',\n success: 'bg-success-9',\n warning: 'bg-warning-9',\n error: 'bg-error-9',\n info: 'bg-info-9',\n neutral: 'bg-surface-fg-subtle',\n}\n\n/* ------------------------------------------------------------------ */\n/* Helpers */\n/* ------------------------------------------------------------------ */\n\nfunction formatHourLabel(hour: number): string {\n if (hour === 0) return '12 AM'\n if (hour < 12) return `${hour} AM`\n if (hour === 12) return '12 PM'\n return `${hour - 12} PM`\n}\n\nfunction getEventStyle(\n event: ScheduleEvent,\n startHour: number,\n endHour: number,\n) {\n const totalMinutes = (endHour - startHour) * 60\n const eventStartMinutes =\n (getHours(event.start) - startHour) * 60 + getMinutes(event.start)\n const durationMinutes = differenceInMinutes(event.end, event.start)\n\n const top = (eventStartMinutes / totalMinutes) * 100\n const height = (durationMinutes / totalMinutes) * 100\n\n return {\n top: `${top}%`,\n height: `${height}%`,\n }\n}\n\n/* ------------------------------------------------------------------ */\n/* Sub-components */\n/* ------------------------------------------------------------------ */\n\ninterface TimeColumnProps {\n startHour: number\n endHour: number\n}\n\nfunction TimeColumn({ startHour, endHour }: TimeColumnProps) {\n const hours: number[] = []\n for (let h = startHour; h < endHour; h++) {\n hours.push(h)\n }\n const slotHeight = 100 / (endHour - startHour)\n\n return (\n <div\n className=\"relative shrink-0 w-[60px] border-r border-surface-border-strong\"\n aria-hidden=\"true\"\n >\n {hours.map((hour) => (\n <div\n key={hour}\n className=\"text-caption text-surface-fg-muted pr-ds-02 text-right\"\n style={{ height: `${slotHeight}%` }}\n >\n {formatHourLabel(hour)}\n </div>\n ))}\n </div>\n )\n}\n\ninterface DayColumnProps {\n date: Date\n events: ScheduleEvent[]\n startHour: number\n endHour: number\n slotDuration: number\n onEventClick?: (event: ScheduleEvent) => void\n onSlotClick?: (start: Date, end: Date) => void\n showHeader?: boolean\n}\n\nfunction DayColumn({\n date,\n events,\n startHour,\n endHour,\n slotDuration,\n onEventClick,\n onSlotClick,\n showHeader,\n}: DayColumnProps) {\n const dayEvents = events.filter((e) => isSameDay(e.start, date))\n const totalMinutes = (endHour - startHour) * 60\n const slotCount = totalMinutes / slotDuration\n\n const slots: { start: Date; end: Date }[] = []\n for (let i = 0; i < slotCount; i++) {\n const slotStart = new Date(date)\n slotStart.setHours(startHour, i * slotDuration, 0, 0)\n const slotEnd = new Date(slotStart)\n slotEnd.setMinutes(slotEnd.getMinutes() + slotDuration)\n slots.push({ start: slotStart, end: slotEnd })\n }\n\n const todayInView = isToday(date)\n\n // Current time indicator position\n let nowIndicatorTop: number | null = null\n if (todayInView) {\n const now = new Date()\n const nowHour = getHours(now)\n const nowMin = getMinutes(now)\n if (nowHour >= startHour && nowHour < endHour) {\n const nowMinutes = (nowHour - startHour) * 60 + nowMin\n nowIndicatorTop = (nowMinutes / totalMinutes) * 100\n }\n }\n\n return (\n <div className=\"flex flex-1 flex-col min-w-ds-11\">\n {showHeader && (\n <div\n className={cn(\n 'text-center text-body-sm font-semibold py-ds-02 border-b border-surface-border-strong',\n todayInView\n ? 'text-accent-11 bg-accent-2'\n : 'text-surface-fg bg-surface-raised',\n )}\n >\n {format(date, 'EEE d')}\n </div>\n )}\n <div className=\"relative flex-1\">\n {/* Slot grid lines */}\n {slots.map((slot, i) => (\n <button\n key={i}\n type=\"button\"\n className={cn(\n 'block w-full border-b border-surface-border hover:bg-surface-raised-hover transition-colors ease-productive-standard',\n 'focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-accent-9',\n i % 2 === 0 ? 'border-surface-border-strong' : 'border-surface-border-subtle',\n )}\n style={{ height: `${100 / slotCount}%` }}\n onClick={() => onSlotClick?.(slot.start, slot.end)}\n aria-label={`${format(slot.start, 'h:mm a')} to ${format(slot.end, 'h:mm a')}`}\n />\n ))}\n\n {/* Events */}\n {dayEvents.map((event) => {\n const style = getEventStyle(event, startHour, endHour)\n const eventColor = event.color ?? 'accent'\n const colorClass = eventColorMap[eventColor]\n const dotClass = eventDotMap[eventColor]\n return (\n <button\n key={event.id}\n type=\"button\"\n className={cn(\n 'absolute left-ds-01 right-ds-01 rounded-control-inner px-ds-02 py-ds-01',\n 'text-left text-body-xs font-medium overflow-hidden cursor-pointer',\n 'hover:shadow-raised transition-[box-shadow] duration-fast-02 ease-productive-standard',\n 'focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9',\n colorClass,\n )}\n style={style}\n onClick={(e) => {\n e.stopPropagation()\n onEventClick?.(event)\n }}\n aria-label={`${event.title}, ${format(event.start, 'h:mm a')} to ${format(event.end, 'h:mm a')}`}\n >\n <span className=\"flex items-start gap-ds-02\">\n <span\n className={cn('mt-[3px] h-ds-03 w-ds-03 shrink-0 rounded-pill', dotClass)}\n aria-hidden=\"true\"\n />\n <span className=\"line-clamp-2\">{event.title}</span>\n </span>\n </button>\n )\n })}\n\n {/* Current time indicator */}\n {nowIndicatorTop != null && (\n <div\n className=\"absolute left-0 right-0 h-ds-01 bg-error-9 z-10 pointer-events-none\"\n style={{ top: `${nowIndicatorTop}%` }}\n aria-hidden=\"true\"\n >\n <Dot color=\"error\" size=\"lg\" pulse className=\"absolute -left-[5px] -top-[4px]\" />\n </div>\n )}\n </div>\n </div>\n )\n}\n\n/* ------------------------------------------------------------------ */\n/* ScheduleView */\n/* ------------------------------------------------------------------ */\n\nconst ScheduleView = React.forwardRef<HTMLDivElement, ScheduleViewProps>(\n (\n {\n view,\n date,\n events,\n onEventClick,\n onSlotClick,\n startHour = 8,\n endHour = 18,\n slotDuration = 30,\n className,\n ...props\n },\n ref,\n ) => {\n const days: Date[] = React.useMemo(() => {\n if (view === 'day') return [date]\n const weekStart = startOfWeek(date, { weekStartsOn: 1 }) // Monday\n return Array.from({ length: 7 }, (_, i) => addDays(weekStart, i))\n }, [view, date])\n\n return (\n <div\n ref={ref}\n role=\"region\"\n aria-label={\n view === 'day'\n ? `Schedule for ${format(date, 'EEEE, MMMM d, yyyy')}`\n : `Week schedule starting ${format(days[0], 'MMMM d, yyyy')}`\n }\n className={cn(\n 'flex rounded-control border border-card-strong bg-surface-raised overflow-hidden',\n 'h-[480px]',\n className,\n )}\n {...props}\n >\n <TimeColumn startHour={startHour} endHour={endHour} />\n <div className=\"flex flex-1 divide-x divide-surface-border overflow-x-auto\">\n {days.map((day) => (\n <DayColumn\n key={day.toISOString()}\n date={day}\n events={events}\n startHour={startHour}\n endHour={endHour}\n slotDuration={slotDuration}\n onEventClick={onEventClick}\n onSlotClick={onSlotClick}\n showHeader={view === 'week'}\n />\n ))}\n </div>\n </div>\n )\n },\n)\nScheduleView.displayName = 'ScheduleView'\n\nexport { ScheduleView }\n"],"mappings":"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"}
1
+ {"version":3,"file":"schedule-view.js","names":[],"sources":["../../src/composed/schedule-view.tsx"],"sourcesContent":["'use client'\n\nimport {\n addDays,\n differenceInMinutes,\n format,\n getHours,\n getMinutes,\n isSameDay,\n isToday,\n startOfWeek,\n} from 'date-fns'\nimport * as React from 'react'\n\nimport { Dot } from '../ui/dot'\nimport { cn } from '../ui/lib/utils'\n\n/* ------------------------------------------------------------------ */\n/* Types */\n/* ------------------------------------------------------------------ */\n\nexport interface ScheduleEvent {\n id: string\n title: string\n start: Date\n end: Date\n color?: 'accent' | 'success' | 'warning' | 'error' | 'info' | 'neutral'\n}\n\n/**\n * A day/week calendar schedule grid. Renders time-slotted events with\n * proportional height, color coding, overlapping-event columns, a live\n * current-time indicator, and keyboard-navigable slots (when `onSlotClick` is\n * set). Presentational — drive `events`/`date`/`view` from the parent.\n */\nexport interface ScheduleViewProps extends React.HTMLAttributes<HTMLDivElement> {\n /** Display mode: single-day column or full-week (7 columns). */\n view: 'day' | 'week'\n /** Current day (day view) or any date within the target week (week view). */\n date: Date\n /** Events to display on the calendar grid. */\n events: ScheduleEvent[]\n /** Called when an event block is clicked. */\n onEventClick?: (event: ScheduleEvent) => void\n /**\n * Called when an empty time slot is clicked. When omitted, slots render as\n * non-interactive grid lines (no keyboard/AT tab stops).\n */\n onSlotClick?: (start: Date, end: Date) => void\n /** First visible hour (default 8). */\n startHour?: number\n /** Last visible hour -- exclusive (default 18). */\n endHour?: number\n /** Slot duration in minutes (default 30). */\n slotDuration?: number\n /** Currently-selected event id — rendered with a ring. */\n selectedEventId?: string\n /** Custom event content renderer. Receives the event; return the block body. */\n renderEvent?: (event: ScheduleEvent) => React.ReactNode\n /** Optional toolbar/header rendered above the grid. */\n header?: React.ReactNode\n /** Content shown when there are no events in view. */\n emptyState?: React.ReactNode\n /** Grid body height (CSS length or px number). @default 480 */\n height?: number | string\n}\n\n/* ------------------------------------------------------------------ */\n/* Color map */\n/* ------------------------------------------------------------------ */\n\nconst eventColorMap: Record<NonNullable<ScheduleEvent['color']>, string> = {\n accent: 'bg-accent-2 text-accent-11',\n success: 'bg-success-3 text-success-11',\n warning: 'bg-warning-3 text-warning-11',\n error: 'bg-error-3 text-error-11',\n info: 'bg-info-3 text-info-11',\n neutral: 'bg-surface-raised text-surface-fg-muted',\n}\n\nconst eventDotMap: Record<NonNullable<ScheduleEvent['color']>, string> = {\n accent: 'bg-accent-9',\n success: 'bg-success-9',\n warning: 'bg-warning-9',\n error: 'bg-error-9',\n info: 'bg-info-9',\n neutral: 'bg-surface-fg-subtle',\n}\n\n/* ------------------------------------------------------------------ */\n/* Helpers */\n/* ------------------------------------------------------------------ */\n\nfunction formatHourLabel(hour: number): string {\n if (hour === 0) return '12 AM'\n if (hour < 12) return `${hour} AM`\n if (hour === 12) return '12 PM'\n return `${hour - 12} PM`\n}\n\ninterface PositionedEvent {\n event: ScheduleEvent\n top: number\n height: number\n col: number\n cols: number\n}\n\n/**\n * Position a day's events, partitioning concurrent events into side-by-side\n * columns so overlaps stay legible (greedy interval colouring per overlap\n * cluster).\n */\nfunction layoutDayEvents(\n events: ScheduleEvent[],\n startHour: number,\n endHour: number,\n): PositionedEvent[] {\n const totalMinutes = (endHour - startHour) * 60\n const sorted = [...events].sort((a, b) => a.start.getTime() - b.start.getTime())\n\n const result: PositionedEvent[] = []\n let cluster: PositionedEvent[] = []\n let clusterEnd = -Infinity\n\n const flush = () => {\n const cols = cluster.reduce((m, e) => Math.max(m, e.col + 1), 0)\n for (const e of cluster) e.cols = cols\n result.push(...cluster)\n cluster = []\n clusterEnd = -Infinity\n }\n\n for (const event of sorted) {\n const startM = (getHours(event.start) - startHour) * 60 + getMinutes(event.start)\n const durationM = Math.max(differenceInMinutes(event.end, event.start), 1)\n const startMs = event.start.getTime()\n\n // New cluster when this event starts after everything so far has ended.\n if (startMs >= clusterEnd && cluster.length > 0) flush()\n\n // First free column within the cluster.\n const taken = new Set(cluster.filter((c) => c.event.end.getTime() > startMs).map((c) => c.col))\n let col = 0\n while (taken.has(col)) col++\n\n cluster.push({\n event,\n top: (startM / totalMinutes) * 100,\n height: (durationM / totalMinutes) * 100,\n col,\n cols: 1,\n })\n clusterEnd = Math.max(clusterEnd, event.end.getTime())\n }\n if (cluster.length > 0) flush()\n\n return result\n}\n\n/* ------------------------------------------------------------------ */\n/* Time column */\n/* ------------------------------------------------------------------ */\n\nfunction TimeColumn({ startHour, endHour }: { startHour: number; endHour: number }) {\n const hours: number[] = []\n for (let h = startHour; h < endHour; h++) hours.push(h)\n const slotHeight = 100 / (endHour - startHour)\n\n return (\n <div className=\"relative w-ds-11 shrink-0 border-e border-surface-border-strong\" aria-hidden=\"true\">\n {hours.map((hour) => (\n <div\n key={hour}\n className=\"pe-ds-02 text-end text-caption text-surface-fg-muted\"\n style={{ height: `${slotHeight}%` }}\n >\n {formatHourLabel(hour)}\n </div>\n ))}\n </div>\n )\n}\n\n/* ------------------------------------------------------------------ */\n/* Day column */\n/* ------------------------------------------------------------------ */\n\ninterface DayColumnProps {\n dayIndex: number\n date: Date\n events: ScheduleEvent[]\n startHour: number\n endHour: number\n slotDuration: number\n now: Date\n active: { day: number; slot: number }\n interactive: boolean\n onSlotKeyDown?: (e: React.KeyboardEvent<HTMLButtonElement>) => void\n selectedEventId?: string\n renderEvent?: (event: ScheduleEvent) => React.ReactNode\n onEventClick?: (event: ScheduleEvent) => void\n onSlotClick?: (start: Date, end: Date) => void\n showHeader?: boolean\n nowLineRef?: React.Ref<HTMLDivElement>\n}\n\nfunction DayColumn({\n dayIndex,\n date,\n events,\n startHour,\n endHour,\n slotDuration,\n now,\n active,\n interactive,\n onSlotKeyDown,\n selectedEventId,\n renderEvent,\n onEventClick,\n onSlotClick,\n showHeader,\n nowLineRef,\n}: DayColumnProps) {\n const dayEvents = React.useMemo(\n () => layoutDayEvents(events.filter((e) => isSameDay(e.start, date)), startHour, endHour),\n [events, date, startHour, endHour],\n )\n const totalMinutes = (endHour - startHour) * 60\n const slotCount = totalMinutes / slotDuration\n\n const slots = React.useMemo(() => {\n const out: { start: Date; end: Date }[] = []\n for (let i = 0; i < slotCount; i++) {\n const slotStart = new Date(date)\n slotStart.setHours(startHour, i * slotDuration, 0, 0)\n const slotEnd = new Date(slotStart)\n slotEnd.setMinutes(slotEnd.getMinutes() + slotDuration)\n out.push({ start: slotStart, end: slotEnd })\n }\n return out\n }, [date, startHour, slotDuration, slotCount])\n\n const todayInView = isToday(date)\n let nowIndicatorTop: number | null = null\n if (todayInView) {\n const nowHour = getHours(now)\n if (nowHour >= startHour && nowHour < endHour) {\n const nowMinutes = (nowHour - startHour) * 60 + getMinutes(now)\n nowIndicatorTop = (nowMinutes / totalMinutes) * 100\n }\n }\n\n return (\n <div className=\"flex min-w-ds-11 flex-1 flex-col\">\n {showHeader && (\n <div\n className={cn(\n 'border-b border-surface-border-strong py-ds-02 text-center text-body-sm font-semibold',\n todayInView ? 'bg-accent-2 text-accent-11' : 'bg-surface-raised text-surface-fg',\n )}\n >\n {format(date, 'EEE d')}\n </div>\n )}\n <div className=\"relative flex-1\">\n {/* Slot lines — interactive buttons only when onSlotClick is set,\n otherwise inert grid lines (no tab stops, hidden from AT). */}\n {slots.map((slot, i) =>\n interactive ? (\n <button\n key={i}\n type=\"button\"\n data-day={dayIndex}\n data-slot={i}\n tabIndex={dayIndex === active.day && i === active.slot ? 0 : -1}\n className={cn(\n 'block w-full min-h-ds-06 border-b transition-colors ease-productive-standard duration-fast-02',\n 'hover:bg-surface-raised-hover',\n 'focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-accent-9',\n i % 2 === 0 ? 'border-surface-border-strong' : 'border-surface-border-subtle',\n )}\n style={{ height: `${100 / slotCount}%` }}\n onClick={() => onSlotClick?.(slot.start, slot.end)}\n onKeyDown={onSlotKeyDown}\n aria-label={`${format(slot.start, 'EEE h:mm a')} to ${format(slot.end, 'h:mm a')}`}\n />\n ) : (\n <div\n key={i}\n aria-hidden=\"true\"\n className={cn(\n 'w-full border-b',\n i % 2 === 0 ? 'border-surface-border-strong' : 'border-surface-border-subtle',\n )}\n style={{ height: `${100 / slotCount}%` }}\n />\n ),\n )}\n\n {/* Events — column-partitioned so overlaps sit side by side */}\n {dayEvents.map(({ event, top, height, col, cols }) => {\n const eventColor = event.color ?? 'accent'\n const colWidth = 100 / cols\n const selected = event.id === selectedEventId\n return (\n <button\n key={event.id}\n type=\"button\"\n aria-pressed={selected || undefined}\n className={cn(\n 'absolute overflow-hidden rounded-control-inner px-ds-02 py-ds-01',\n 'cursor-pointer text-start text-body-xs font-medium',\n 'transition-[box-shadow] ease-productive-standard duration-fast-02 hover:shadow-raised',\n 'focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9',\n 'motion-safe:active:scale-[0.98]',\n selected && 'ring-2 ring-accent-9',\n eventColorMap[eventColor],\n )}\n style={{\n top: `${top}%`,\n height: `${height}%`,\n insetInlineStart: `calc(${col * colWidth}% + 2px)`,\n width: `calc(${colWidth}% - 4px)`,\n }}\n onClick={(e) => {\n e.stopPropagation()\n onEventClick?.(event)\n }}\n aria-label={`${event.title}, ${format(event.start, 'h:mm a')} to ${format(event.end, 'h:mm a')}`}\n >\n {renderEvent ? (\n renderEvent(event)\n ) : (\n <span className=\"flex items-start gap-ds-02\">\n <span\n className={cn('mt-0.5 h-ds-03 w-ds-03 shrink-0 rounded-pill', eventDotMap[eventColor])}\n aria-hidden=\"true\"\n />\n <span className=\"line-clamp-2\">{event.title}</span>\n </span>\n )}\n </button>\n )\n })}\n\n {/* Live current-time indicator */}\n {nowIndicatorTop != null && (\n <div\n ref={nowLineRef}\n className=\"pointer-events-none absolute inset-x-0 z-raised h-ds-01 bg-error-9\"\n style={{ top: `${nowIndicatorTop}%` }}\n aria-hidden=\"true\"\n >\n <Dot color=\"error\" size=\"lg\" pulse className=\"absolute -translate-x-1/2 -translate-y-1/2\" />\n </div>\n )}\n </div>\n </div>\n )\n}\n\n/* ------------------------------------------------------------------ */\n/* ScheduleView */\n/* ------------------------------------------------------------------ */\n\nconst ScheduleView = React.forwardRef<HTMLDivElement, ScheduleViewProps>(\n (\n {\n view,\n date,\n events,\n onEventClick,\n onSlotClick,\n startHour = 8,\n endHour = 18,\n slotDuration = 30,\n selectedEventId,\n renderEvent,\n header,\n emptyState,\n height = 480,\n className,\n ...props\n },\n ref,\n ) => {\n const days = React.useMemo<Date[]>(() => {\n if (view === 'day') return [date]\n const weekStart = startOfWeek(date, { weekStartsOn: 1 })\n return Array.from({ length: 7 }, (_, i) => addDays(weekStart, i))\n }, [view, date])\n\n const interactive = !!onSlotClick\n const slotCount = ((endHour - startHour) * 60) / slotDuration\n\n // Live now-line: re-render each minute so the indicator actually ticks.\n const [now, setNow] = React.useState(() => new Date())\n React.useEffect(() => {\n const id = setInterval(() => setNow(new Date()), 60_000)\n return () => clearInterval(id)\n }, [])\n\n // Roving-tabindex slot navigation (only meaningful when interactive).\n const gridRef = React.useRef<HTMLDivElement>(null)\n const nowLineRef = React.useRef<HTMLDivElement>(null)\n const [active, setActive] = React.useState<{ day: number; slot: number }>({ day: 0, slot: 0 })\n\n // Scroll the now-line into view on mount.\n React.useEffect(() => {\n nowLineRef.current?.scrollIntoView({ block: 'center' })\n }, [])\n\n const handleSlotKeyDown = (e: React.KeyboardEvent<HTMLButtonElement>) => {\n if (!interactive) return\n const target = e.target as HTMLElement\n const dayAttr = target.getAttribute('data-day')\n const slotAttr = target.getAttribute('data-slot')\n if (dayAttr == null || slotAttr == null) return\n const day = Number(dayAttr)\n const slot = Number(slotAttr)\n const rtl = getComputedStyle(gridRef.current ?? target).direction === 'rtl'\n let nextDay = day\n let nextSlot = slot\n switch (e.key) {\n case 'ArrowDown': nextSlot = Math.min(slot + 1, slotCount - 1); break\n case 'ArrowUp': nextSlot = Math.max(slot - 1, 0); break\n case 'ArrowRight': nextDay = rtl ? Math.max(day - 1, 0) : Math.min(day + 1, days.length - 1); break\n case 'ArrowLeft': nextDay = rtl ? Math.min(day + 1, days.length - 1) : Math.max(day - 1, 0); break\n case 'Home': nextSlot = 0; break\n case 'End': nextSlot = slotCount - 1; break\n default: return\n }\n e.preventDefault()\n setActive({ day: nextDay, slot: nextSlot })\n // Focus the target cell now — focus() works regardless of the tabIndex\n // the re-render is about to move onto it.\n gridRef.current\n ?.querySelector<HTMLButtonElement>(`[data-day=\"${nextDay}\"][data-slot=\"${nextSlot}\"]`)\n ?.focus()\n }\n\n const isEmpty = events.length === 0\n\n return (\n <div ref={ref} className={cn('flex flex-col', className)} {...props}>\n {header}\n <div\n ref={gridRef}\n role=\"region\"\n aria-label={\n view === 'day'\n ? `Schedule for ${format(date, 'EEEE, MMMM d, yyyy')}`\n : `Week schedule starting ${format(days[0], 'MMMM d, yyyy')}`\n }\n className=\"flex overflow-hidden rounded-surface border border-surface-border-strong bg-surface-raised\"\n style={{ height: typeof height === 'number' ? `${height}px` : height }}\n >\n <TimeColumn startHour={startHour} endHour={endHour} />\n <div className=\"relative flex flex-1 divide-x divide-surface-border overflow-x-auto overflow-y-auto\">\n {days.map((day, i) => (\n <DayColumn\n key={day.toISOString()}\n dayIndex={i}\n date={day}\n events={events}\n startHour={startHour}\n endHour={endHour}\n slotDuration={slotDuration}\n now={now}\n active={active}\n interactive={interactive}\n onSlotKeyDown={handleSlotKeyDown}\n selectedEventId={selectedEventId}\n renderEvent={renderEvent}\n onEventClick={onEventClick}\n onSlotClick={onSlotClick}\n showHeader={view === 'week'}\n nowLineRef={i === 0 || isSameDay(day, now) ? nowLineRef : undefined}\n />\n ))}\n {isEmpty && (\n <div className=\"pointer-events-none absolute inset-0 flex items-center justify-center p-ds-05 text-center text-body-sm text-surface-fg-muted\">\n {emptyState ?? 'No events scheduled.'}\n </div>\n )}\n </div>\n </div>\n </div>\n )\n },\n)\nScheduleView.displayName = 'ScheduleView'\n\nexport { ScheduleView }\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"motion-provider.d.ts","sourceRoot":"","sources":["../../src/motion/motion-provider.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAE9B,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AAElD,KAAK,iBAAiB,GAAG,MAAM,GAAG,OAAO,CAAA;AAEzC,KAAK,kBAAkB,GAAG;IACxB,OAAO,EAAE,OAAO,OAAO,CAAA;IACvB,MAAM,EAAE,OAAO,MAAM,CAAA;IACrB,aAAa,EAAE,OAAO,CAAA;CACvB,CAAA;AAQD,KAAK,mBAAmB,GAAG;IACzB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;IACzB,wEAAwE;IACxE,aAAa,CAAC,EAAE,iBAAiB,CAAA;CAClC,CAAA;AAED,iBAAS,cAAc,CAAC,EAAE,QAAQ,EAAE,aAAsB,EAAE,EAAE,mBAAmB,qBAgBhF;AAED,iBAAS,SAAS,uBAEjB;AAED,OAAO,EAAE,cAAc,EAAE,KAAK,mBAAmB,EAAC,SAAS,EAAE,CAAA"}
1
+ {"version":3,"file":"motion-provider.d.ts","sourceRoot":"","sources":["../../src/motion/motion-provider.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAE9B,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AAElD,KAAK,iBAAiB,GAAG,MAAM,GAAG,OAAO,CAAA;AAEzC,KAAK,kBAAkB,GAAG;IACxB,OAAO,EAAE,OAAO,OAAO,CAAA;IACvB,MAAM,EAAE,OAAO,MAAM,CAAA;IACrB,aAAa,EAAE,OAAO,CAAA;CACvB,CAAA;AAOD,KAAK,mBAAmB,GAAG;IACzB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;IACzB,wEAAwE;IACxE,aAAa,CAAC,EAAE,iBAAiB,CAAA;CAClC,CAAA;AAED,iBAAS,cAAc,CAAC,EAAE,QAAQ,EAAE,aAAsB,EAAE,EAAE,mBAAmB,qBAgBhF;AAED,iBAAS,SAAS,IAAI,kBAAkB,CAMvC;AAED,OAAO,EAAE,cAAc,EAAE,KAAK,mBAAmB,EAAC,SAAS,EAAE,CAAA"}
@@ -1,29 +1,55 @@
1
1
  "use client";
2
2
  import { IconInput } from '../ui/lib/icon-input';
3
3
  import * as React from 'react';
4
+ export interface BottomNavbarUser {
5
+ name: string;
6
+ role?: string;
7
+ }
4
8
  export interface BottomNavItem {
5
9
  title: string;
6
10
  href: string;
7
11
  /** Icon for this nav item. Accepts any `IconInput`. */
8
12
  icon: IconInput;
13
+ /** Icon shown when the item is active — e.g. a filled variant. Falls back to `icon`. */
14
+ activeIcon?: IconInput;
9
15
  /** When true, the route matches only when the path is exactly equal */
10
16
  exact?: boolean;
11
17
  /** Notification badge count. 0 or undefined = hidden, 1–99 = shown, >99 = "99+" */
12
18
  badge?: number;
19
+ /**
20
+ * Roles allowed to see this item, matched against `user.role`. Omit to show
21
+ * it to everyone. For arbitrary logic, use `canView` instead.
22
+ */
23
+ roles?: string[];
24
+ /**
25
+ * Visibility predicate — full control over whether this item renders.
26
+ * Receives the current `user` (or `null`). Takes precedence over `roles`.
27
+ */
28
+ canView?: (user: BottomNavbarUser | null) => boolean;
13
29
  }
14
- export interface BottomNavbarUser {
15
- name: string;
16
- role?: string;
17
- }
30
+ /**
31
+ * Active-item indicator, animated between items on selection:
32
+ * - `pill` (default) — Material-3 tonal pill behind the icon
33
+ * - `underline` — top accent bar (Material-2 tab style)
34
+ * - `tint` — subtle background tint on the whole active cell
35
+ * - `none` — no shape; rely on the filled `activeIcon` + accent color (iOS)
36
+ */
37
+ export type BottomNavIndicator = 'pill' | 'underline' | 'tint' | 'none';
38
+ /** Show labels always (default) or only for the selected item (narrow viewports). */
39
+ export type BottomNavLabelVisibility = 'always' | 'selected';
18
40
  export interface BottomNavbarProps extends React.HTMLAttributes<HTMLElement> {
19
41
  /** Currently active pathname */
20
42
  currentPath?: string;
21
- /** User information (used to determine admin status, presence) */
43
+ /** Current user. Drives per-item role gating via `item.roles` / `item.canView`. */
22
44
  user?: BottomNavbarUser | null;
23
45
  /** Primary nav items shown directly in the bottom bar (max 4 recommended) */
24
46
  primaryItems?: BottomNavItem[];
25
- /** Additional items shown in the "More" overflow menu */
47
+ /** Additional items shown in the "More" overflow sheet */
26
48
  moreItems?: BottomNavItem[];
49
+ /** Active-indicator style. @default 'pill' */
50
+ indicator?: BottomNavIndicator;
51
+ /** Label visibility. @default 'always' */
52
+ labelVisibility?: BottomNavLabelVisibility;
27
53
  /** Additional className for the nav element */
28
54
  className?: string;
29
55
  }
@@ -1 +1 @@
1
- {"version":3,"file":"bottom-navbar.d.ts","sourceRoot":"","sources":["../../src/shell/bottom-navbar.tsx"],"names":[],"mappings":"AAUA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAK9B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAA;AAUrD,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,uDAAuD;IACvD,IAAI,EAAE,SAAS,CAAA;IACf,uEAAuE;IACvE,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,mFAAmF;IACnF,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAED,MAAM,WAAW,iBACf,SAAQ,KAAK,CAAC,cAAc,CAAC,WAAW,CAAC;IACzC,gCAAgC;IAChC,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,kEAAkE;IAClE,IAAI,CAAC,EAAE,gBAAgB,GAAG,IAAI,CAAA;IAC9B,6EAA6E;IAC7E,YAAY,CAAC,EAAE,aAAa,EAAE,CAAA;IAC9B,yDAAyD;IACzD,SAAS,CAAC,EAAE,aAAa,EAAE,CAAA;IAC3B,+CAA+C;IAC/C,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AA4ED,QAAA,MAAM,YAAY,uFA6JjB,CAAA;AAGD,OAAO,EAAE,YAAY,EAAE,CAAA"}
1
+ {"version":3,"file":"bottom-navbar.d.ts","sourceRoot":"","sources":["../../src/shell/bottom-navbar.tsx"],"names":[],"mappings":"AAWA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAM9B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAA;AAWrD,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,uDAAuD;IACvD,IAAI,EAAE,SAAS,CAAA;IACf,wFAAwF;IACxF,UAAU,CAAC,EAAE,SAAS,CAAA;IACtB,uEAAuE;IACvE,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,mFAAmF;IACnF,KAAK,CAAC,EAAE,MAAM,CAAA;IACd;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,EAAE,CAAA;IAChB;;;OAGG;IACH,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,gBAAgB,GAAG,IAAI,KAAK,OAAO,CAAA;CACrD;AAED;;;;;;GAMG;AACH,MAAM,MAAM,kBAAkB,GAAG,MAAM,GAAG,WAAW,GAAG,MAAM,GAAG,MAAM,CAAA;AACvE,qFAAqF;AACrF,MAAM,MAAM,wBAAwB,GAAG,QAAQ,GAAG,UAAU,CAAA;AAE5D,MAAM,WAAW,iBAAkB,SAAQ,KAAK,CAAC,cAAc,CAAC,WAAW,CAAC;IAC1E,gCAAgC;IAChC,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,mFAAmF;IACnF,IAAI,CAAC,EAAE,gBAAgB,GAAG,IAAI,CAAA;IAC9B,6EAA6E;IAC7E,YAAY,CAAC,EAAE,aAAa,EAAE,CAAA;IAC9B,0DAA0D;IAC1D,SAAS,CAAC,EAAE,aAAa,EAAE,CAAA;IAC3B,8CAA8C;IAC9C,SAAS,CAAC,EAAE,kBAAkB,CAAA;IAC9B,0CAA0C;IAC1C,eAAe,CAAC,EAAE,wBAAwB,CAAA;IAC1C,+CAA+C;IAC/C,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAwID,QAAA,MAAM,YAAY,uFA0HjB,CAAA;AAGD,OAAO,EAAE,YAAY,EAAE,CAAA"}