@siyahi/ui 0.1.9-next.2 → 0.1.9-next.20

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 (149) hide show
  1. package/README.md +82 -176
  2. package/THIRD_PARTY_NOTICES.md +11 -11
  3. package/agents/examples/accordion.tsx +39 -0
  4. package/agents/examples/alert.tsx +25 -0
  5. package/agents/examples/app-sidebar.tsx +38 -0
  6. package/agents/examples/avatar.tsx +14 -0
  7. package/agents/examples/badge.tsx +18 -0
  8. package/agents/examples/bar-list-labels.tsx +22 -0
  9. package/agents/examples/bar-list-sides.tsx +30 -0
  10. package/agents/examples/bar-list.tsx +24 -0
  11. package/agents/examples/breadcrumb.tsx +28 -0
  12. package/agents/examples/button.tsx +21 -0
  13. package/agents/examples/calendar.tsx +15 -0
  14. package/agents/examples/card.tsx +34 -0
  15. package/agents/examples/chart-area.tsx +38 -0
  16. package/agents/examples/chart-bar-flows.tsx +31 -0
  17. package/agents/examples/chart-bar-hourly.tsx +28 -0
  18. package/agents/examples/chart-bar-losses.tsx +29 -0
  19. package/agents/examples/chart-bar-sectors.tsx +70 -0
  20. package/agents/examples/chart-bar-years.tsx +40 -0
  21. package/agents/examples/chart-bar.tsx +56 -0
  22. package/agents/examples/chart-category-ticks.tsx +50 -0
  23. package/agents/examples/chart-line-events.tsx +47 -0
  24. package/agents/examples/chart-line-timeline.tsx +41 -0
  25. package/agents/examples/chart-line.tsx +57 -0
  26. package/agents/examples/code-block.tsx +38 -0
  27. package/agents/examples/collapsible.tsx +16 -0
  28. package/agents/examples/command.tsx +27 -0
  29. package/agents/examples/context-menu.tsx +15 -0
  30. package/agents/examples/copy-button.tsx +8 -0
  31. package/agents/examples/data-bar.tsx +121 -0
  32. package/agents/examples/data-table-filters.tsx +103 -0
  33. package/agents/examples/data-table.tsx +98 -0
  34. package/agents/examples/delta.tsx +13 -0
  35. package/agents/examples/dialog.tsx +28 -0
  36. package/agents/examples/distribution-bar-budget.tsx +30 -0
  37. package/agents/examples/distribution-bar-tug.tsx +72 -0
  38. package/agents/examples/dropdown-menu.tsx +31 -0
  39. package/agents/examples/formatters.tsx +42 -0
  40. package/agents/examples/heatmap-calendar.tsx +50 -0
  41. package/agents/examples/heatmap-status.tsx +51 -0
  42. package/agents/examples/heatmap-weekday.tsx +53 -0
  43. package/agents/examples/input.tsx +17 -0
  44. package/agents/examples/local-time.tsx +14 -0
  45. package/agents/examples/metric-strip.tsx +40 -0
  46. package/agents/examples/page-section.tsx +52 -0
  47. package/agents/examples/popover.tsx +23 -0
  48. package/agents/examples/price-chart.tsx +32 -0
  49. package/agents/examples/progress-tone.tsx +28 -0
  50. package/agents/examples/progress.tsx +10 -0
  51. package/agents/examples/scroll-area.tsx +20 -0
  52. package/agents/examples/section-cards-select.tsx +27 -0
  53. package/agents/examples/section-cards-trend.tsx +30 -0
  54. package/agents/examples/section-cards.tsx +48 -0
  55. package/agents/examples/select.tsx +27 -0
  56. package/agents/examples/separator.tsx +13 -0
  57. package/agents/examples/sheet.tsx +15 -0
  58. package/agents/examples/skeleton.tsx +11 -0
  59. package/agents/examples/slider.tsx +5 -0
  60. package/agents/examples/surfaces.tsx +115 -0
  61. package/agents/examples/table.tsx +50 -0
  62. package/agents/examples/tabs.tsx +16 -0
  63. package/agents/examples/toggle-group.tsx +12 -0
  64. package/agents/examples/toggle.tsx +11 -0
  65. package/agents/examples/tooltip.tsx +13 -0
  66. package/agents/examples/treemap.tsx +87 -0
  67. package/agents/manifest.json +931 -0
  68. package/agents/report-rules.json +7 -0
  69. package/dist/components/registry/charts/bar-chart-card.js +33 -26
  70. package/dist/components/registry/dashboard-01/section-cards.d.ts +2 -0
  71. package/dist/components/registry/dashboard-01/section-cards.js +42 -35
  72. package/dist/components/registry/dashboardcn/bar-list.js +9 -8
  73. package/dist/components/registry/dashboardcn/distribution-bar.js +1 -1
  74. package/dist/components/registry/dashboardcn/kpi-row-card.d.ts +29 -0
  75. package/dist/components/registry/dashboardcn/kpi-row-card.js +81 -0
  76. package/dist/components/registry/dashboardcn/sparkline.d.ts +18 -0
  77. package/dist/components/registry/dashboardcn/sparkline.js +95 -0
  78. package/dist/components/registry/heatmap/calendar-heatmap.js +8 -1
  79. package/dist/components/registry/heatmap/status-heatmap.js +8 -1
  80. package/dist/components/registry/heatmap/weekday-heatmap.d.ts +6 -2
  81. package/dist/components/registry/heatmap/weekday-heatmap.js +56 -37
  82. package/dist/components/registry/niko-table/core/data-table-context.d.ts +2 -0
  83. package/dist/components/registry/niko-table/core/data-table-context.js +21 -0
  84. package/dist/components/registry/niko-table/core/data-table-grouped-row.js +3 -1
  85. package/dist/components/registry/niko-table/core/data-table-structure.js +15 -12
  86. package/dist/components/registry/niko-table/core/data-table.d.ts +2 -2
  87. package/dist/components/registry/niko-table/core/data-table.js +5 -1
  88. package/dist/components/registry/niko-table/filters/table-faceted-filter.js +4 -4
  89. package/dist/components/registry/niko-table/filters/table-search-filter.js +1 -1
  90. package/dist/components/registry/niko-table/lib/flex-columns.d.ts +1 -1
  91. package/dist/components/registry/niko-table/lib/flex-columns.js +3 -3
  92. package/dist/components/registry/niko-table/lib/styles.d.ts +1 -1
  93. package/dist/components/registry/niko-table/lib/styles.js +2 -2
  94. package/dist/components/registry/niko-table/ui/table.js +2 -2
  95. package/dist/components/registry/sidebar-07/app-sidebar.d.ts +1 -1
  96. package/dist/components/registry/sidebar-07/app-sidebar.js +3 -1
  97. package/dist/components/registry/sidebar-07/nav-main.d.ts +1 -1
  98. package/dist/components/registry/sidebar-07/nav-main.js +7 -1
  99. package/dist/components/siyahi/data-bar.d.ts +19 -0
  100. package/dist/components/siyahi/data-bar.js +29 -0
  101. package/dist/components/siyahi/page-section.d.ts +4 -1
  102. package/dist/components/siyahi/page-section.js +6 -3
  103. package/dist/components/siyahi/panel-grid.d.ts +22 -0
  104. package/dist/components/siyahi/panel-grid.js +34 -0
  105. package/dist/components/siyahi/siyahi-provider.d.ts +13 -2
  106. package/dist/components/siyahi/siyahi-provider.js +17 -4
  107. package/dist/components/siyahi/treemap.js +46 -4
  108. package/dist/components/ui/accordion.js +5 -5
  109. package/dist/components/ui/alert.js +2 -2
  110. package/dist/components/ui/badge.js +1 -1
  111. package/dist/components/ui/breadcrumb.js +2 -2
  112. package/dist/components/ui/button.js +10 -10
  113. package/dist/components/ui/calendar.js +1 -1
  114. package/dist/components/ui/card.js +4 -4
  115. package/dist/components/ui/chart.js +1 -1
  116. package/dist/components/ui/code-block.js +13 -12
  117. package/dist/components/ui/command.js +7 -6
  118. package/dist/components/ui/context-menu.js +7 -7
  119. package/dist/components/ui/dialog.js +5 -5
  120. package/dist/components/ui/dropdown-menu.js +8 -8
  121. package/dist/components/ui/input-group.js +11 -11
  122. package/dist/components/ui/input.js +1 -1
  123. package/dist/components/ui/popover.js +3 -3
  124. package/dist/components/ui/progress.js +1 -1
  125. package/dist/components/ui/select.js +6 -6
  126. package/dist/components/ui/sheet.js +5 -5
  127. package/dist/components/ui/sidebar.js +17 -18
  128. package/dist/components/ui/skeleton.js +1 -1
  129. package/dist/components/ui/slider.js +2 -2
  130. package/dist/components/ui/table.js +2 -2
  131. package/dist/components/ui/tabs.js +2 -2
  132. package/dist/components/ui/textarea.js +1 -1
  133. package/dist/components/ui/toggle-group.js +2 -2
  134. package/dist/components/ui/toggle.js +4 -4
  135. package/dist/components/ui/tooltip.js +2 -2
  136. package/dist/index.d.ts +7 -4
  137. package/dist/index.js +7 -4
  138. package/dist/lib/chart-frame.d.ts +13 -5
  139. package/dist/lib/chart-frame.js +207 -127
  140. package/dist/lib/chart-series.d.ts +7 -4
  141. package/dist/lib/format.js +32 -13
  142. package/dist/lib/heatmap-navigation.d.ts +8 -0
  143. package/dist/lib/heatmap-navigation.js +49 -0
  144. package/package.json +3 -1
  145. package/registry.lock.json +16 -0
  146. package/report.css +11 -11
  147. package/styles/siyahi.css +77 -11
  148. package/dist/lib/recharts.d.ts +0 -2
  149. package/dist/lib/recharts.js +0 -1
@@ -1,4 +1,5 @@
1
1
  import { cn } from "../../../lib/utils.js";
2
+ import { useHeatmapNavigation } from "../../../lib/heatmap-navigation.js";
2
3
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
4
  import { Fragment as Fragment$1, createContext, use, useMemo } from "react";
4
5
  import { format } from "date-fns";
@@ -84,8 +85,9 @@ const useWeekdayHeatmap = () => {
84
85
  * @param extraRow - Appends an aggregated row below the grid. `compute` receives all activities and must return 24 values (one per hour). Rendered through `renderExtraRow` on the body, or falls back to `children`.
85
86
  * @param extraColumn - Appends an aggregated column to the right of the grid. `compute` receives all activities and must return 7 values indexed by weekday 0–6. Rendered through `renderExtraColumn` on the body, or falls back to `children`.
86
87
  */
87
- const WeekdayHeatmap = ({ data, weekStart = 0, use12Hour = false, blockSize = 24, blockMargin = 4, blockRadius = 2, blockAspectRatio = 1, levels: levelsProp = 5, isNormalized = false, colors, locale, labels: labelsProp, fontSize = 14, emptyState, totalCount: totalCountProp, extraRow, extraColumn, style = EMPTY_STYLE, className, children, ...props }) => {
88
+ const WeekdayHeatmap = ({ data, weekStart = 0, hours = HOURS, weekdays: displayedWeekdays, use12Hour = false, blockSize = 24, blockMargin = 4, blockRadius = 2, blockAspectRatio = 1, levels: levelsProp = 5, isNormalized = false, colors, locale, labels: labelsProp, fontSize = 14, emptyState, totalCount: totalCountProp, extraRow, extraColumn, style = EMPTY_STYLE, className, children, ...props }) => {
88
89
  const levels = Math.max(1, levelsProp);
90
+ const weekdays = useMemo(() => displayedWeekdays ?? Array.from({ length: 7 }, (_, i) => (weekStart + i) % 7), [displayedWeekdays, weekStart]);
89
91
  const dataWithLevels = useMemo(() => {
90
92
  if (data.length === 0) return [];
91
93
  const maxRegular = data.reduce((m, d) => Math.max(m, d.value), 1);
@@ -141,7 +143,7 @@ const WeekdayHeatmap = ({ data, weekStart = 0, use12Hour = false, blockSize = 24
141
143
  const weekdayLabels = locale ? generateWeekdayLabels(locale) : DEFAULT_WEEKDAY_LABELS;
142
144
  return {
143
145
  hours: use12Hour ? TWELVE_HOUR_LABELS : DEFAULT_HOUR_LABELS,
144
- endHour: use12Hour ? "12" : "00",
146
+ endHour: hours.at(-1) === 23 ? use12Hour ? "12" : "00" : (use12Hour ? TWELVE_HOUR_LABELS : DEFAULT_HOUR_LABELS)[(hours.at(-1) ?? -1) + 1],
145
147
  weekdays: weekdayLabels,
146
148
  cellLabel: "{{weekday}} {{hour}}: {{value}}",
147
149
  heatmapLabel: "Activity heatmap by weekday and hour",
@@ -152,7 +154,8 @@ const WeekdayHeatmap = ({ data, weekStart = 0, use12Hour = false, blockSize = 24
152
154
  }, [
153
155
  locale,
154
156
  use12Hour,
155
- labelsProp
157
+ labelsProp,
158
+ hours
156
159
  ]);
157
160
  const labelWidth = fontSize * 3.5 + LABEL_MARGIN;
158
161
  const totalCount = typeof totalCountProp === "number" ? totalCountProp : dataWithLevels.reduce((sum, a) => sum + a.value, 0);
@@ -160,8 +163,8 @@ const WeekdayHeatmap = ({ data, weekStart = 0, use12Hour = false, blockSize = 24
160
163
  const labelHeight = fontSize + LABEL_MARGIN;
161
164
  const hasExtraColumn = resolvedExtraColumn !== null;
162
165
  const hasExtraRow = resolvedExtraRow !== null;
163
- const width = 24 * (blockWidth + blockMargin) + (hasExtraColumn ? blockSize / 2 + (blockWidth + blockMargin) * 2 : 0) - blockMargin + labelWidth;
164
- const rowCount = hasExtraRow ? 8 : 7;
166
+ const width = hours.length * (blockWidth + blockMargin) + (hasExtraColumn ? blockSize / 2 + (blockWidth + blockMargin) * 2 : 0) - blockMargin + labelWidth;
167
+ const rowCount = weekdays.length + (hasExtraRow ? 1 : 0);
165
168
  const extraRowGap = hasExtraRow ? blockSize + blockMargin : 0;
166
169
  const height = rowCount * (blockSize + blockMargin) + extraRowGap - blockMargin + labelHeight;
167
170
  const contextValue = useMemo(() => ({
@@ -183,6 +186,8 @@ const WeekdayHeatmap = ({ data, weekStart = 0, use12Hour = false, blockSize = 24
183
186
  width,
184
187
  height,
185
188
  weekStart,
189
+ hours,
190
+ weekdays,
186
191
  colors
187
192
  }), [
188
193
  dataWithLevels,
@@ -203,6 +208,8 @@ const WeekdayHeatmap = ({ data, weekStart = 0, use12Hour = false, blockSize = 24
203
208
  width,
204
209
  height,
205
210
  weekStart,
211
+ hours,
212
+ weekdays,
206
213
  colors
207
214
  ]);
208
215
  if (data.length === 0) return emptyState ? emptyState : null;
@@ -210,7 +217,7 @@ const WeekdayHeatmap = ({ data, weekStart = 0, use12Hour = false, blockSize = 24
210
217
  value: contextValue,
211
218
  children: /* @__PURE__ */ jsx("div", {
212
219
  "data-slot": "weekday-heatmap",
213
- className: cn("flex w-max max-w-full flex-col gap-2 p-4", className),
220
+ className: cn("flex w-max max-w-full flex-col gap-2", className),
214
221
  style: {
215
222
  fontSize,
216
223
  ...style
@@ -221,15 +228,17 @@ const WeekdayHeatmap = ({ data, weekStart = 0, use12Hour = false, blockSize = 24
221
228
  });
222
229
  };
223
230
  const WeekdayHeatmapBlock = ({ ref, activity, extra, highlighted = false, onCellClick, onCellHover, onClick, onKeyDown, onMouseEnter, onMouseLeave, className, style: styleProp, ...props }) => {
224
- const { blockSize, blockWidth, blockMargin, blockRadius, labelWidth, labelHeight, levels, isNormalized, weekStart, labels, colors } = useWeekdayHeatmap();
231
+ const { blockSize, blockWidth, blockMargin, blockRadius, labelWidth, labelHeight, levels, isNormalized, hours, weekdays, labels, colors } = useWeekdayHeatmap();
225
232
  const level = Math.max(0, Math.min(colorStepCount(levels, isNormalized), activity.level));
226
233
  const isExtraRow = extra === "row";
227
234
  const isExtraColumn = extra === "column";
228
- const rowIndex = isExtraRow ? 7 : (activity.weekday - weekStart + 7) % 7;
235
+ const rowIndex = isExtraRow ? weekdays.length : weekdays.findIndex((day) => day === activity.weekday);
236
+ const hourIndex = isExtraColumn ? hours.length : hours.findIndex((hour) => hour === activity.hour);
237
+ if (rowIndex < 0 || hourIndex < 0) return null;
229
238
  const extraRowGap = isExtraRow ? blockSize + blockMargin : 0;
230
239
  const yPosition = labelHeight + (blockSize + blockMargin) * rowIndex + extraRowGap;
231
240
  const extraColumnGap = isExtraColumn ? blockWidth + blockMargin : 0;
232
- const xPosition = isExtraColumn ? labelWidth + 24 * (blockWidth + blockMargin) + blockSize / 2 + extraColumnGap : labelWidth + (blockWidth + blockMargin) * activity.hour;
241
+ const xPosition = isExtraColumn ? labelWidth + hours.length * (blockWidth + blockMargin) + blockSize / 2 + extraColumnGap : labelWidth + (blockWidth + blockMargin) * hourIndex;
233
242
  const weekdayToken = isExtraRow ? "extra" : labels.weekdays?.[activity.weekday] ?? String(activity.weekday);
234
243
  const hourToken = isExtraColumn ? "extra" : `${String(activity.hour).padStart(2, "0")}:00`;
235
244
  const ariaLabel = (labels.cellLabel ?? "{{weekday}} {{hour}}: {{value}}").replace("{{weekday}}", weekdayToken).replace("{{hour}}", hourToken).replace("{{value}}", String(activity.value));
@@ -279,7 +288,8 @@ const WeekdayHeatmapBlock = ({ ref, activity, extra, highlighted = false, onCell
279
288
  };
280
289
  WeekdayHeatmapBlock.displayName = "WeekdayHeatmapBlock";
281
290
  const WeekdayHeatmapBody = ({ hideHourLabels = false, hideWeekdayLabels = false, className, labelClassName, children, renderExtraRow, renderExtraColumn, ...props }) => {
282
- const { data, extraRow, extraColumn, blockSize, blockWidth, blockMargin, labels, labelWidth, labelHeight, fontSize, weekStart } = useWeekdayHeatmap();
291
+ const navigation = useHeatmapNavigation();
292
+ const { data, extraRow, extraColumn, blockSize, blockWidth, blockMargin, labels, labelWidth, labelHeight, fontSize, hours, weekdays } = useWeekdayHeatmap();
283
293
  const hasExtraRow = extraRow !== null;
284
294
  const hasExtraColumn = extraColumn !== null;
285
295
  const activityMap = useMemo(() => {
@@ -299,24 +309,24 @@ const WeekdayHeatmapBody = ({ hideHourLabels = false, hideWeekdayLabels = false,
299
309
  }, [extraColumn]);
300
310
  const regularActivities = useMemo(() => {
301
311
  const activities = [];
302
- for (let di = 0; di < 7; di++) {
303
- const weekday = (weekStart + di) % 7;
304
- for (let hour = 0; hour < 24; hour++) activities.push({
305
- activity: activityMap.get(`${weekday}-${hour}`) ?? {
306
- weekday,
307
- hour,
308
- value: 0,
309
- level: 0
310
- },
311
- weekdayIndex: di
312
- });
313
- }
312
+ for (const [di, weekday] of weekdays.entries()) for (const hour of hours) activities.push({
313
+ activity: activityMap.get(`${weekday}-${hour}`) ?? {
314
+ weekday,
315
+ hour,
316
+ value: 0,
317
+ level: 0
318
+ },
319
+ weekdayIndex: di
320
+ });
314
321
  return activities;
315
- }, [activityMap, weekStart]);
322
+ }, [
323
+ activityMap,
324
+ weekdays,
325
+ hours
326
+ ]);
316
327
  const extraColumnActivities = useMemo(() => {
317
328
  if (!extraColumnMap) return [];
318
- return Array.from({ length: 7 }, (_, di) => {
319
- const weekday = (weekStart + di) % 7;
329
+ return weekdays.map((weekday, di) => {
320
330
  const entry = extraColumnMap.get(weekday);
321
331
  return entry ? {
322
332
  activity: {
@@ -328,29 +338,38 @@ const WeekdayHeatmapBody = ({ hideHourLabels = false, hideWeekdayLabels = false,
328
338
  weekdayIndex: di
329
339
  } : null;
330
340
  }).filter((a) => a !== null);
331
- }, [extraColumnMap, weekStart]);
341
+ }, [extraColumnMap, weekdays]);
332
342
  const extraRowActivities = useMemo(() => {
333
343
  if (!extraRow) return [];
334
- return extraRow.values.map(({ hour, value, level }) => ({
344
+ return extraRow.values.filter(({ hour }) => hours.includes(hour)).map(({ hour, value, level }) => ({
335
345
  activity: {
336
346
  weekday: 0,
337
347
  hour,
338
348
  value,
339
349
  level
340
350
  },
341
- weekdayIndex: 7
351
+ weekdayIndex: weekdays.length
342
352
  }));
343
- }, [extraRow]);
344
- const rowCount = hasExtraRow ? 8 : 7;
353
+ }, [
354
+ extraRow,
355
+ hours,
356
+ weekdays.length
357
+ ]);
358
+ const rowCount = weekdays.length + (hasExtraRow ? 1 : 0);
345
359
  const extraRowGap = hasExtraRow ? blockSize + blockMargin : 0;
346
360
  const svgHeight = rowCount * (blockSize + blockMargin) + extraRowGap - blockMargin + labelHeight;
347
361
  const extraColumnGap = hasExtraColumn ? blockWidth + blockMargin : 0;
348
- const svgWidth = hasExtraColumn ? labelWidth + 24 * (blockWidth + blockMargin) + blockSize / 2 + (blockWidth + blockMargin) + extraColumnGap : labelWidth + 24 * (blockWidth + blockMargin) - blockMargin;
349
- const orderedWeekdayIndices = Array.from({ length: 7 }, (_, i) => (weekStart + i) % 7);
362
+ const svgWidth = hasExtraColumn ? labelWidth + hours.length * (blockWidth + blockMargin) + blockSize / 2 + (blockWidth + blockMargin) + extraColumnGap : labelWidth + hours.length * (blockWidth + blockMargin) - blockMargin;
363
+ const orderedWeekdayIndices = weekdays;
350
364
  return /* @__PURE__ */ jsx("div", {
351
365
  "data-slot": "weekday-heatmap-body",
366
+ ref: navigation.ref,
352
367
  className: cn("max-w-full overflow-x-auto overflow-y-hidden", className),
353
368
  ...props,
369
+ onKeyDown: (event) => {
370
+ props.onKeyDown?.(event);
371
+ if (!event.defaultPrevented) navigation.onKeyDown(event);
372
+ },
354
373
  children: /* @__PURE__ */ jsxs("svg", {
355
374
  role: "img",
356
375
  "aria-label": labels.heatmapLabel ?? "Activity heatmap by weekday and hour",
@@ -362,10 +381,10 @@ const WeekdayHeatmapBody = ({ hideHourLabels = false, hideWeekdayLabels = false,
362
381
  !hideHourLabels && /* @__PURE__ */ jsxs("g", {
363
382
  className: cn("fill-current font-mono", labelClassName),
364
383
  children: [
365
- HOURS.map((hour) => {
384
+ hours.map((hour, index) => {
366
385
  const label = labels.hours?.[hour] ?? "";
367
386
  return /* @__PURE__ */ jsx("text", {
368
- x: labelWidth + (blockWidth + blockMargin) * hour,
387
+ x: labelWidth + (blockWidth + blockMargin) * index,
369
388
  y: 0,
370
389
  textAnchor: "middle",
371
390
  dominantBaseline: "hanging",
@@ -374,7 +393,7 @@ const WeekdayHeatmapBody = ({ hideHourLabels = false, hideWeekdayLabels = false,
374
393
  }, `hour-${hour}`);
375
394
  }),
376
395
  labels.endHour != null && /* @__PURE__ */ jsx("text", {
377
- x: labelWidth + (blockWidth + blockMargin) * 24,
396
+ x: labelWidth + (blockWidth + blockMargin) * hours.length,
378
397
  y: 0,
379
398
  textAnchor: "middle",
380
399
  dominantBaseline: "hanging",
@@ -382,7 +401,7 @@ const WeekdayHeatmapBody = ({ hideHourLabels = false, hideWeekdayLabels = false,
382
401
  children: labels.endHour
383
402
  }, "hour-end"),
384
403
  hasExtraColumn && /* @__PURE__ */ jsx("text", {
385
- x: labelWidth + 24 * (blockWidth + blockMargin) + blockSize / 2 + (blockWidth + blockMargin) + blockWidth / 2,
404
+ x: labelWidth + hours.length * (blockWidth + blockMargin) + blockSize / 2 + (blockWidth + blockMargin) + blockWidth / 2,
386
405
  y: 0,
387
406
  textAnchor: "middle",
388
407
  dominantBaseline: "hanging",
@@ -406,7 +425,7 @@ const WeekdayHeatmapBody = ({ hideHourLabels = false, hideWeekdayLabels = false,
406
425
  }, `weekday-${weekday}`);
407
426
  }), hasExtraRow && /* @__PURE__ */ jsx("text", {
408
427
  x: labelWidth - 8,
409
- y: labelHeight + (blockSize + blockMargin) * 7 + (blockSize + blockMargin) + blockSize / 2,
428
+ y: labelHeight + (blockSize + blockMargin) * weekdays.length + (blockSize + blockMargin) + blockSize / 2,
410
429
  dominantBaseline: "middle",
411
430
  textAnchor: "end",
412
431
  style: { fontSize: `${fontSize * .75}px` },
@@ -91,6 +91,8 @@ type DataTableContextProps<TData extends RowData> = DataTableContextState & {
91
91
  * `null` until the container mounts (or when no `<DataTable>` is used).
92
92
  */
93
93
  scrollContainer: HTMLElement | null;
94
+ /** True below 40rem of table space; use for compact toolbar and record layouts. */
95
+ isCompact: boolean;
94
96
  /** Internal: `<DataTable>` registers its scroll container element here. */
95
97
  registerScrollContainer: (el: HTMLElement | null) => void;
96
98
  /**
@@ -127,6 +127,25 @@ function DataTableProvider({ children, table, columns, isLoading: externalIsLoad
127
127
  }, []);
128
128
  const [scrollContainer, setScrollContainer] = React.useState(null);
129
129
  const registerScrollContainer = useCallback((el) => setScrollContainer(el), []);
130
+ const [isCompact, setIsCompact] = React.useState(false);
131
+ React.useLayoutEffect(() => {
132
+ if (!scrollContainer) return;
133
+ const measure = () => {
134
+ const width = scrollContainer.clientWidth;
135
+ if (width > 0) {
136
+ const rem = Number.parseFloat(getComputedStyle(document.documentElement).fontSize);
137
+ setIsCompact(width < 40 * rem);
138
+ }
139
+ };
140
+ measure();
141
+ const observer = new ResizeObserver(measure);
142
+ observer.observe(scrollContainer);
143
+ window.addEventListener("resize", measure);
144
+ return () => {
145
+ observer.disconnect();
146
+ window.removeEventListener("resize", measure);
147
+ };
148
+ }, [scrollContainer]);
130
149
  const scrollRowIntoView = useCallback((index, opts) => {
131
150
  if (rowScrollerRef.current) {
132
151
  rowScrollerRef.current(index, opts);
@@ -269,6 +288,7 @@ function DataTableProvider({ children, table, columns, isLoading: externalIsLoad
269
288
  scrollRowIntoView,
270
289
  registerRowScroller,
271
290
  scrollContainer,
291
+ isCompact,
272
292
  registerScrollContainer,
273
293
  headerMinWidths,
274
294
  setColumnResizePreview: setManualResizePreview,
@@ -286,6 +306,7 @@ function DataTableProvider({ children, table, columns, isLoading: externalIsLoad
286
306
  scrollRowIntoView,
287
307
  registerRowScroller,
288
308
  scrollContainer,
309
+ isCompact,
289
310
  registerScrollContainer,
290
311
  headerMinWidths,
291
312
  setManualResizePreview,
@@ -1,5 +1,6 @@
1
1
  import { Button } from "../../../ui/button.js";
2
2
  import { cn } from "../../../../lib/utils.js";
3
+ import { useDataTable } from "./data-table-context.js";
3
4
  import { DataTableGroupRowProvider, useDataTableGroupRow } from "../components/data-table-grouped-rows.js";
4
5
  import { TableCell, TableRow } from "../ui/table.js";
5
6
  import { isInteractiveClickTarget } from "../lib/row-click.js";
@@ -18,12 +19,13 @@ import { flexRender } from "@tanstack/react-table";
18
19
  * groups you don't want to special-case.
19
20
  */
20
21
  function DataTableGroupedRowCells() {
22
+ const { isCompact } = useDataTable();
21
23
  const { row, groupLabel, count, isExpanded, toggle, columnWidths } = useDataTableGroupRow();
22
24
  const countLabel = `${count} ${count === 1 ? "row" : "rows"}`;
23
25
  return /* @__PURE__ */ jsx(Fragment, { children: row.getVisibleCells().map((cell) => {
24
26
  const cellStyle = {
25
27
  width: columnWidths.get(cell.column.id),
26
- ...getCommonPinningStyles(cell.column, false)
28
+ ...getCommonPinningStyles(cell.column, false, !isCompact)
27
29
  };
28
30
  let content = null;
29
31
  if (cell.getIsGrouped()) content = /* @__PURE__ */ jsxs("div", {
@@ -20,11 +20,11 @@ import React from "react";
20
20
  import { flexRender } from "@tanstack/react-table";
21
21
  //#region src/components/registry/niko-table/core/data-table-structure.tsx
22
22
  const DataTableHeader = React.memo(function DataTableHeader({ className, sticky = true }) {
23
- const { table, headerMinWidths, setColumnResizePreview } = useDataTable();
23
+ const { table, headerMinWidths, setColumnResizePreview, isCompact } = useDataTable();
24
24
  const resizing = table?.options.enableColumnResizing ?? false;
25
25
  const headerGroups = table?.getHeaderGroups() ?? [];
26
26
  if (headerGroups.length === 0) return null;
27
- const flexColumnIds = resolveFlexColumnIds(table);
27
+ const flexColumnIds = resolveFlexColumnIds(table, !isCompact);
28
28
  const columnSizing = table.state.columnSizing;
29
29
  return /* @__PURE__ */ jsx(TableHeader, {
30
30
  className: cn(sticky && "sticky top-0 z-30 bg-card", sticky && "after:absolute after:right-0 after:bottom-0 after:left-0 after:h-px after:bg-border", className),
@@ -37,12 +37,12 @@ const DataTableHeader = React.memo(function DataTableHeader({ className, sticky
37
37
  columnSizing,
38
38
  headerMinWidths
39
39
  }),
40
- ...getCommonPinningStyles(header.column, true)
40
+ ...getCommonPinningStyles(header.column, true, !isCompact)
41
41
  };
42
42
  return /* @__PURE__ */ jsxs(TableHead, {
43
43
  "data-col-id": header.column.id,
44
44
  style: headerStyle,
45
- className: cn(header.column.getIsPinned() && "bg-background", resizing && "relative overflow-hidden"),
45
+ className: cn(!isCompact && header.column.getIsPinned() && "bg-background", resizing && "relative overflow-hidden"),
46
46
  children: [header.isPlaceholder ? null : /* @__PURE__ */ jsx(DataTableColumnHeaderRoot, {
47
47
  column: header.column,
48
48
  children: resizing && typeof header.column.columnDef.header === "string" ? /* @__PURE__ */ jsx("span", {
@@ -61,7 +61,7 @@ const DataTableHeader = React.memo(function DataTableHeader({ className, sticky
61
61
  DataTableHeader.displayName = "DataTableHeader";
62
62
  /** Fade-pulse animation applied to a flashing cell (keyframe in the provider). */
63
63
  const FLASH_ANIMATION = "niko-row-flash 1.2s ease-out";
64
- const BodyRow = React.memo(function BodyRow({ row, displayIndex, expandColumnId, isClickable, isExpanded, isSelected, columnWidths, isRowFlashing, flashingCellKeys, renderRowContextMenu }) {
64
+ const BodyRow = React.memo(function BodyRow({ row, displayIndex, expandColumnId, isCompact, isClickable, isExpanded, isSelected, columnWidths, isRowFlashing, flashingCellKeys, renderRowContextMenu }) {
65
65
  const expandCell = isExpanded && expandColumnId ? row.getAllCells().find((c) => c.column.id === expandColumnId) : void 0;
66
66
  const visibleCells = row.getVisibleCells();
67
67
  const rowElement = /* @__PURE__ */ jsx(TableRow, {
@@ -76,14 +76,14 @@ const BodyRow = React.memo(function BodyRow({ row, displayIndex, expandColumnId,
76
76
  const flashing = isRowFlashing || flashingCellKeys.has(flashCellKey(row.id, cell.column.id));
77
77
  const cellStyle = {
78
78
  width: columnWidths.get(cell.column.id),
79
- ...getCommonPinningStyles(cell.column, false),
79
+ ...getCommonPinningStyles(cell.column, false, !isCompact),
80
80
  ...flashing ? { animation: FLASH_ANIMATION } : {}
81
81
  };
82
82
  return /* @__PURE__ */ jsx(TableCell, {
83
83
  "data-flash": flashing ? "true" : void 0,
84
84
  "data-col-id": cell.column.id,
85
85
  style: cellStyle,
86
- className: cn(!cell.getIsGrouped() && "truncate", cell.column.getIsPinned() && "bg-linear-to-r group-hover:from-muted/50 group-hover:to-muted/50 group-data-[context-menu-open]:from-muted/50 group-data-[context-menu-open]:to-muted/50 group-data-[state=selected]:from-muted group-data-[state=selected]:to-muted"),
86
+ className: cn(!cell.getIsGrouped() && "truncate", !isCompact && cell.column.getIsPinned() && "bg-linear-to-r group-hover:from-muted/50 group-hover:to-muted/50 group-data-[context-menu-open]:from-muted/50 group-data-[context-menu-open]:to-muted/50 group-data-[state=selected]:from-muted group-data-[state=selected]:to-muted"),
87
87
  children: renderCellContent(cell)
88
88
  }, cell.id);
89
89
  })
@@ -101,7 +101,7 @@ const BodyRow = React.memo(function BodyRow({ row, displayIndex, expandColumnId,
101
101
  });
102
102
  BodyRow.displayName = "BodyRow";
103
103
  function DataTableBody({ children, className, onScroll, onScrolledTop, onScrolledBottom, scrollThreshold = 50, onRowClick, getRowMemoKey, renderRowContextMenu }) {
104
- const { table, columns, isLoading, flashingRowIds, flashingCellKeys, registerRowScroller, headerMinWidths } = useDataTable();
104
+ const { table, columns, isLoading, flashingRowIds, flashingCellKeys, registerRowScroller, headerMinWidths, isCompact } = useDataTable();
105
105
  const { rows } = table.getRowModel();
106
106
  const containerRef = React.useRef(null);
107
107
  React.useEffect(() => {
@@ -139,13 +139,14 @@ function DataTableBody({ children, className, onScroll, onScrolledTop, onScrolle
139
139
  const expandColumnId = React.useMemo(() => table.getAllColumns().find((col) => col.columnDef.meta?.expandedContent)?.id, [table, columns]);
140
140
  const { columnVisibility, columnOrder, columnPinning, columnSizing } = table.state;
141
141
  const resizing = table.options.enableColumnResizing ?? false;
142
- const flexColumnIds = React.useMemo(() => resolveFlexColumnIds(table), [
142
+ const flexColumnIds = React.useMemo(() => resolveFlexColumnIds(table, !isCompact), [
143
143
  table,
144
144
  columnVisibility,
145
145
  columnOrder,
146
146
  columnPinning,
147
147
  columnSizing,
148
- resizing
148
+ resizing,
149
+ isCompact
149
150
  ]);
150
151
  const columnWidths = React.useMemo(() => {
151
152
  const widths = /* @__PURE__ */ new Map();
@@ -166,14 +167,15 @@ function DataTableBody({ children, className, onScroll, onScrolledTop, onScrolle
166
167
  resizing
167
168
  ]);
168
169
  const columnLayoutSignature = React.useMemo(() => table.getVisibleLeafColumns().map((c) => {
169
- const pinned = c.getIsPinned();
170
+ const pinned = !isCompact && c.getIsPinned();
170
171
  const base = pinned ? `${c.id}:${pinned}` : c.id;
171
172
  return resizing ? `${base}:${columnWidths.get(c.id) ?? "flex"}` : base;
172
173
  }).join(","), [
173
174
  table,
174
175
  columns,
175
176
  columnWidths,
176
- resizing
177
+ resizing,
178
+ isCompact
177
179
  ]);
178
180
  const isClickable = !!onRowClick;
179
181
  const resolvedRenderRowContextMenu = useResolvedRowContextMenuRenderer(renderRowContextMenu, children);
@@ -232,6 +234,7 @@ function DataTableBody({ children, className, onScroll, onScrolledTop, onScrolle
232
234
  row,
233
235
  displayIndex,
234
236
  expandColumnId,
237
+ isCompact,
235
238
  isClickable,
236
239
  isExpanded: row.getIsExpanded(),
237
240
  isSelected: row.getIsSelected(),
@@ -1,6 +1,6 @@
1
1
  import React from "react";
2
2
  //#region src/components/registry/niko-table/core/data-table.d.ts
3
- export interface DataTableContainerProps {
3
+ export interface DataTableContainerProps extends React.AriaAttributes {
4
4
  children: React.ReactNode;
5
5
  /**
6
6
  * Additional CSS classes for the container.
@@ -23,7 +23,7 @@ export interface DataTableContainerProps {
23
23
  */
24
24
  maxHeight?: number | string;
25
25
  }
26
- export declare function DataTable({ children, className, height, maxHeight }: DataTableContainerProps): React.JSX.Element;
26
+ export declare function DataTable({ children, className, height, maxHeight, "aria-label": label, "aria-labelledby": labelledBy }: DataTableContainerProps): React.JSX.Element;
27
27
  export declare namespace DataTable {
28
28
  var displayName: string;
29
29
  }
@@ -105,7 +105,7 @@ function ColumnResizePreviewLine() {
105
105
  }
106
106
  });
107
107
  }
108
- function DataTable({ children, className, height, maxHeight }) {
108
+ function DataTable({ children, className, height, maxHeight, "aria-label": label = "Data table", "aria-labelledby": labelledBy }) {
109
109
  const parsed = React.useMemo(() => parseHeightFromClassName(className), [className]);
110
110
  const finalHeight = height ?? parsed.height;
111
111
  const finalMaxHeight = maxHeight ?? parsed.maxHeight ?? finalHeight;
@@ -113,6 +113,10 @@ function DataTable({ children, className, height, maxHeight }) {
113
113
  return /* @__PURE__ */ jsxs("div", {
114
114
  ref: registerScrollContainer,
115
115
  "data-slot": "table-container",
116
+ role: "region",
117
+ "aria-label": labelledBy ? void 0 : label,
118
+ "aria-labelledby": labelledBy,
119
+ tabIndex: 0,
116
120
  className: cn("relative isolate w-full overflow-auto rounded-lg border", "[&::-webkit-scrollbar]:h-2.5 [&::-webkit-scrollbar]:w-2.5", "[&::-webkit-scrollbar-track]:bg-transparent", "[&::-webkit-scrollbar-thumb]:rounded-full [&::-webkit-scrollbar-thumb]:bg-border/40", "hover:[&::-webkit-scrollbar-thumb]:bg-border", "[&::-webkit-scrollbar-thumb:hover]:bg-border/80!", "scrollbar-thin scrollbar-thumb-border/40 scrollbar-track-transparent", "hover:scrollbar-thumb-border", parsed.safeClassName),
117
121
  style: {
118
122
  height: finalHeight,
@@ -149,7 +149,7 @@ function TableFacetedFilter({ column, title, options = [], multiple, onValueChan
149
149
  ] })
150
150
  ]
151
151
  }) }), /* @__PURE__ */ jsx(PopoverContent, {
152
- className: "w-52 p-0",
152
+ className: "w-max min-w-52 max-w-(--available-width) p-0",
153
153
  align: "start",
154
154
  children: /* @__PURE__ */ jsx(TableFacetedFilterContent, {
155
155
  title,
@@ -177,16 +177,16 @@ function TableFacetedFilterContent({ title, options, selectedValues, onItemSelec
177
177
  onSelect: () => onItemSelect(option, isSelected),
178
178
  children: [
179
179
  /* @__PURE__ */ jsx("div", {
180
- className: cn("mr-2 flex size-4 items-center justify-center rounded-sm border border-primary", isSelected ? "bg-primary text-primary-foreground" : "opacity-50 [&_svg]:invisible"),
180
+ className: cn("mr-2 flex size-4 shrink-0 items-center justify-center rounded-sm border border-primary", isSelected ? "bg-primary text-primary-foreground" : "opacity-50 [&_svg]:invisible"),
181
181
  children: /* @__PURE__ */ jsx(Check, { className: "size-4" })
182
182
  }),
183
183
  option.icon && /* @__PURE__ */ jsx(option.icon, { className: "mr-2 size-4" }),
184
184
  /* @__PURE__ */ jsx("span", {
185
- className: "truncate",
185
+ className: "min-w-0 flex-1 whitespace-normal wrap-anywhere",
186
186
  children: option.label
187
187
  }),
188
188
  option.count !== void 0 && /* @__PURE__ */ jsx("span", {
189
- className: "ml-auto font-mono text-xs",
189
+ className: "ml-auto shrink-0 font-mono text-xs",
190
190
  children: option.count
191
191
  })
192
192
  ]
@@ -62,7 +62,7 @@ function TableSearchFilter({ table, className, placeholder = "Search...", label
62
62
  ]);
63
63
  const hasValue = currentValue.length > 0;
64
64
  return /* @__PURE__ */ jsxs("div", {
65
- className: cn("relative flex flex-1 items-center", className),
65
+ className: cn("relative flex min-w-0 max-w-full flex-1 items-center", className),
66
66
  role: "search",
67
67
  children: [
68
68
  /* @__PURE__ */ jsx(Search, {
@@ -26,7 +26,7 @@ import { ColumnSizingState, RowData } from "@tanstack/react-table";
26
26
  * Returns the set of column ids to render widthless (empty when fill is off or
27
27
  * no eligible column exists — the table then sizes to its columns as before).
28
28
  */
29
- export declare function resolveFlexColumnIds<TData extends RowData>(table: DataTableInstance<TData>): ReadonlySet<string>;
29
+ export declare function resolveFlexColumnIds<TData extends RowData>(table: DataTableInstance<TData>, pinningEnabled?: boolean): ReadonlySet<string>;
30
30
  /**
31
31
  * The width to render for a single column. Centralizes the flex + header-fit
32
32
  * rules so the header, body cells, and the min-width lock all agree.
@@ -24,19 +24,19 @@
24
24
  * Returns the set of column ids to render widthless (empty when fill is off or
25
25
  * no eligible column exists — the table then sizes to its columns as before).
26
26
  */
27
- function resolveFlexColumnIds(table) {
27
+ function resolveFlexColumnIds(table, pinningEnabled = true) {
28
28
  const ids = /* @__PURE__ */ new Set();
29
29
  if (!table.options.enableColumnResizing) return ids;
30
30
  if (table.options.meta?.disableFlexFill) return ids;
31
31
  const leafColumns = table.getVisibleLeafColumns();
32
32
  const columnSizing = table.state.columnSizing;
33
33
  let hasExplicit = false;
34
- for (const column of leafColumns) if (column.columnDef.meta?.flex === true && !column.getIsPinned() && columnSizing[column.id] == null) {
34
+ for (const column of leafColumns) if (column.columnDef.meta?.flex === true && !(pinningEnabled && column.getIsPinned()) && columnSizing[column.id] == null) {
35
35
  ids.add(column.id);
36
36
  hasExplicit = true;
37
37
  }
38
38
  if (hasExplicit) return ids;
39
- const auto = leafColumns.find((column) => column.getCanResize() && !column.getIsPinned() && column.columnDef.meta?.flex !== false && columnSizing[column.id] == null);
39
+ const auto = leafColumns.find((column) => column.getCanResize() && !(pinningEnabled && column.getIsPinned()) && column.columnDef.meta?.flex !== false && columnSizing[column.id] == null);
40
40
  if (auto) ids.add(auto.id);
41
41
  return ids;
42
42
  }
@@ -2,5 +2,5 @@ import { DataTableFeatures } from "./data-table-features.js";
2
2
  import React from "react";
3
3
  import { Column, RowData } from "@tanstack/react-table";
4
4
  //#region src/components/registry/niko-table/lib/styles.d.ts
5
- export declare const getCommonPinningStyles: <TData extends RowData>(column: Column<DataTableFeatures, TData>, isHeader?: boolean) => React.CSSProperties;
5
+ export declare const getCommonPinningStyles: <TData extends RowData>(column: Column<DataTableFeatures, TData>, isHeader?: boolean, pinningEnabled?: boolean) => React.CSSProperties;
6
6
  //#endregion
@@ -1,8 +1,8 @@
1
1
  import "@tanstack/react-table";
2
2
  //#region src/components/registry/niko-table/lib/styles.ts
3
- const getCommonPinningStyles = (column, isHeader = false) => {
3
+ const getCommonPinningStyles = (column, isHeader = false, pinningEnabled = true) => {
4
4
  const isPinned = column.getIsPinned();
5
- if (!isPinned) return {};
5
+ if (!isPinned || !pinningEnabled) return {};
6
6
  const isLeft = isPinned === "start";
7
7
  const columnSize = column.getSize();
8
8
  return {
@@ -52,14 +52,14 @@ function TableRow({ className, ...props }) {
52
52
  function TableHead({ className, ...props }) {
53
53
  return /* @__PURE__ */ jsx("th", {
54
54
  "data-slot": "table-head",
55
- className: cn("h-12 px-3 text-left align-middle font-medium whitespace-nowrap text-foreground [&:has([role=checkbox])]:pr-0 *:[[role=checkbox]]:translate-y-[2px]", className),
55
+ className: cn("h-10 px-2 text-left align-middle font-medium whitespace-nowrap text-foreground [&:has([role=checkbox])]:pr-0 *:[[role=checkbox]]:translate-y-[2px]", className),
56
56
  ...props
57
57
  });
58
58
  }
59
59
  function TableCell({ className, ...props }) {
60
60
  return /* @__PURE__ */ jsx("td", {
61
61
  "data-slot": "table-cell",
62
- className: cn("p-3 align-middle whitespace-nowrap [&:has([role=checkbox])]:pr-0 *:[[role=checkbox]]:translate-y-[2px]", className),
62
+ className: cn("p-2 align-middle whitespace-nowrap [&:has([role=checkbox])]:pr-0 *:[[role=checkbox]]:translate-y-[2px]", className),
63
63
  ...props
64
64
  });
65
65
  }
@@ -14,5 +14,5 @@ export interface AppSidebarProps extends React$1.ComponentProps<typeof Sidebar>
14
14
  projects?: React$1.ComponentProps<typeof NavProjects>["projects"];
15
15
  user?: React$1.ComponentProps<typeof NavUser>["user"];
16
16
  }
17
- export declare function AppSidebar({ navMain, navLabel, header, teams, projects, user, collapsible, ...props }: AppSidebarProps): React$1.JSX.Element;
17
+ export declare function AppSidebar({ navMain, navLabel, header, teams, projects, user, collapsible, className, ...props }: AppSidebarProps): React$1.JSX.Element;
18
18
  //#endregion
@@ -1,5 +1,6 @@
1
1
  "use client";
2
2
  import { Sidebar, SidebarContent, SidebarFooter, SidebarHeader, SidebarRail } from "../../ui/sidebar.js";
3
+ import { cn } from "../../../lib/utils.js";
3
4
  import { NavMain } from "./nav-main.js";
4
5
  import { NavProjects } from "./nav-projects.js";
5
6
  import { NavUser } from "./nav-user.js";
@@ -7,9 +8,10 @@ import { TeamSwitcher } from "./team-switcher.js";
7
8
  import { jsx, jsxs } from "react/jsx-runtime";
8
9
  import "react";
9
10
  //#region src/components/registry/sidebar-07/app-sidebar.tsx
10
- function AppSidebar({ navMain, navLabel, header, teams, projects, user, collapsible = "icon", ...props }) {
11
+ function AppSidebar({ navMain, navLabel, header, teams, projects, user, collapsible = "icon", className, ...props }) {
11
12
  return /* @__PURE__ */ jsxs(Sidebar, {
12
13
  collapsible,
14
+ className: cn(collapsible !== "none" && "h-auto", className),
13
15
  ...props,
14
16
  children: [
15
17
  (header || teams) && /* @__PURE__ */ jsx(SidebarHeader, { children: header ?? (teams && /* @__PURE__ */ jsx(TeamSwitcher, { teams })) }),
@@ -4,7 +4,7 @@ export declare function NavMain({ items, label }: {
4
4
  items: {
5
5
  title: string;
6
6
  url: string;
7
- icon?: React.ReactNode;
7
+ icon: React.ReactNode;
8
8
  isActive?: boolean;
9
9
  linkProps?: Omit<React.ComponentProps<"a">, "href">;
10
10
  items?: {
@@ -2,9 +2,11 @@ import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "../../ui/co
2
2
  import { SidebarGroup, SidebarGroupLabel, SidebarMenu, SidebarMenuButton, SidebarMenuItem, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, useSidebar } from "../../ui/sidebar.js";
3
3
  import { ChevronRightIcon } from "lucide-react";
4
4
  import { jsx, jsxs } from "react/jsx-runtime";
5
+ import { useState } from "react";
5
6
  //#region src/components/registry/sidebar-07/nav-main.tsx
6
7
  function NavMain({ items, label = "Navigation" }) {
7
8
  const { isMobile, setOpenMobile } = useSidebar();
9
+ const [expanded, setExpanded] = useState({});
8
10
  const closeDrawer = () => {
9
11
  if (isMobile) setOpenMobile(false);
10
12
  };
@@ -19,7 +21,11 @@ function NavMain({ items, label = "Navigation" }) {
19
21
  onClick: closeDrawer,
20
22
  children: [item.icon, /* @__PURE__ */ jsx("span", { children: item.title })]
21
23
  }) }, item.title) : /* @__PURE__ */ jsxs(Collapsible, {
22
- defaultOpen: item.isActive,
24
+ open: expanded[item.title] ?? !!item.isActive,
25
+ onOpenChange: (open) => setExpanded((previous) => ({
26
+ ...previous,
27
+ [item.title]: open
28
+ })),
23
29
  className: "group/collapsible",
24
30
  render: /* @__PURE__ */ jsx(SidebarMenuItem, {}),
25
31
  children: [/* @__PURE__ */ jsxs(CollapsibleTrigger, {