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

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 (196) hide show
  1. package/DESIGN.md +30 -0
  2. package/README.md +82 -176
  3. package/THIRD_PARTY_NOTICES.md +11 -11
  4. package/agents/examples/accordion.tsx +39 -0
  5. package/agents/examples/alert.tsx +23 -0
  6. package/agents/examples/app-sidebar.tsx +38 -0
  7. package/agents/examples/avatar.tsx +14 -0
  8. package/agents/examples/badge.tsx +18 -0
  9. package/agents/examples/bar-list-labels.tsx +22 -0
  10. package/agents/examples/bar-list-sides.tsx +38 -0
  11. package/agents/examples/bar-list.tsx +24 -0
  12. package/agents/examples/breadcrumb.tsx +28 -0
  13. package/agents/examples/button-states.tsx +23 -0
  14. package/agents/examples/button.tsx +21 -0
  15. package/agents/examples/calendar.tsx +15 -0
  16. package/agents/examples/callout.tsx +33 -0
  17. package/agents/examples/card.tsx +34 -0
  18. package/agents/examples/change-bar.tsx +104 -0
  19. package/agents/examples/chart-area.tsx +39 -0
  20. package/agents/examples/chart-bar-flows.tsx +32 -0
  21. package/agents/examples/chart-bar-hourly.tsx +28 -0
  22. package/agents/examples/chart-bar-losses.tsx +29 -0
  23. package/agents/examples/chart-bar-sectors.tsx +72 -0
  24. package/agents/examples/chart-bar-years.tsx +40 -0
  25. package/agents/examples/chart-bar.tsx +56 -0
  26. package/agents/examples/chart-category-ticks.tsx +50 -0
  27. package/agents/examples/chart-line-events.tsx +47 -0
  28. package/agents/examples/chart-line-timeline.tsx +41 -0
  29. package/agents/examples/chart-line.tsx +57 -0
  30. package/agents/examples/code-block.tsx +38 -0
  31. package/agents/examples/collapsible.tsx +16 -0
  32. package/agents/examples/command.tsx +27 -0
  33. package/agents/examples/context-menu.tsx +15 -0
  34. package/agents/examples/copy-button.tsx +8 -0
  35. package/agents/examples/data-bar.tsx +121 -0
  36. package/agents/examples/data-table-compact.tsx +95 -0
  37. package/agents/examples/data-table-filters.tsx +103 -0
  38. package/agents/examples/data-table.tsx +98 -0
  39. package/agents/examples/delta.tsx +13 -0
  40. package/agents/examples/dialog.tsx +28 -0
  41. package/agents/examples/distribution-bar-budget.tsx +30 -0
  42. package/agents/examples/distribution-bar-tug.tsx +72 -0
  43. package/agents/examples/dropdown-menu.tsx +31 -0
  44. package/agents/examples/formatters.tsx +42 -0
  45. package/agents/examples/heatmap-calendar.tsx +60 -0
  46. package/agents/examples/heatmap-status.tsx +57 -0
  47. package/agents/examples/heatmap-weekday.tsx +75 -0
  48. package/agents/examples/input-states.tsx +33 -0
  49. package/agents/examples/input.tsx +17 -0
  50. package/agents/examples/local-time.tsx +14 -0
  51. package/agents/examples/metric-strip.tsx +46 -0
  52. package/agents/examples/overview-dashboard.tsx +18 -0
  53. package/agents/examples/page-hero.tsx +38 -0
  54. package/agents/examples/page-section.tsx +52 -0
  55. package/agents/examples/popover.tsx +23 -0
  56. package/agents/examples/price-chart.tsx +32 -0
  57. package/agents/examples/progress-tone.tsx +28 -0
  58. package/agents/examples/progress.tsx +10 -0
  59. package/agents/examples/range-position-table.tsx +101 -0
  60. package/agents/examples/range-position.tsx +53 -0
  61. package/agents/examples/scroll-area.tsx +20 -0
  62. package/agents/examples/section-cards-select.tsx +30 -0
  63. package/agents/examples/section-cards-trend.tsx +30 -0
  64. package/agents/examples/section-cards.tsx +48 -0
  65. package/agents/examples/select.tsx +27 -0
  66. package/agents/examples/separator.tsx +13 -0
  67. package/agents/examples/sheet.tsx +15 -0
  68. package/agents/examples/skeleton.tsx +11 -0
  69. package/agents/examples/slider.tsx +5 -0
  70. package/agents/examples/surfaces.tsx +116 -0
  71. package/agents/examples/table.tsx +50 -0
  72. package/agents/examples/tabs.tsx +16 -0
  73. package/agents/examples/target-rows.tsx +70 -0
  74. package/agents/examples/toggle-group.tsx +12 -0
  75. package/agents/examples/toggle.tsx +11 -0
  76. package/agents/examples/tooltip.tsx +13 -0
  77. package/agents/examples/treemap.tsx +88 -0
  78. package/agents/manifest.json +1081 -0
  79. package/agents/report-rules.json +7 -0
  80. package/dist/components/registry/charts/area-chart-card.js +25 -7
  81. package/dist/components/registry/charts/bar-chart-card.d.ts +0 -1
  82. package/dist/components/registry/charts/bar-chart-card.js +40 -26
  83. package/dist/components/registry/charts/line-chart-card.js +2 -2
  84. package/dist/components/registry/dashboard-01/section-cards.d.ts +2 -0
  85. package/dist/components/registry/dashboard-01/section-cards.js +42 -35
  86. package/dist/components/registry/dashboardcn/bar-list.js +18 -11
  87. package/dist/components/registry/dashboardcn/distribution-bar.js +1 -1
  88. package/dist/components/registry/dashboardcn/kpi-row-card.d.ts +29 -0
  89. package/dist/components/registry/dashboardcn/kpi-row-card.js +81 -0
  90. package/dist/components/registry/dashboardcn/sparkline.d.ts +22 -0
  91. package/dist/components/registry/dashboardcn/sparkline.js +117 -0
  92. package/dist/components/registry/heatmap/calendar-heatmap.d.ts +4 -2
  93. package/dist/components/registry/heatmap/calendar-heatmap.js +74 -33
  94. package/dist/components/registry/heatmap/status-heatmap.d.ts +5 -3
  95. package/dist/components/registry/heatmap/status-heatmap.js +37 -15
  96. package/dist/components/registry/heatmap/weekday-heatmap.d.ts +9 -3
  97. package/dist/components/registry/heatmap/weekday-heatmap.js +82 -47
  98. package/dist/components/registry/niko-table/core/data-table-context.d.ts +18 -1
  99. package/dist/components/registry/niko-table/core/data-table-context.js +7 -1
  100. package/dist/components/registry/niko-table/core/data-table-grouped-row.js +3 -1
  101. package/dist/components/registry/niko-table/core/data-table-root.js +39 -4
  102. package/dist/components/registry/niko-table/core/data-table-structure.js +17 -12
  103. package/dist/components/registry/niko-table/core/data-table.d.ts +2 -2
  104. package/dist/components/registry/niko-table/core/data-table.js +5 -1
  105. package/dist/components/registry/niko-table/filters/table-faceted-filter.js +4 -4
  106. package/dist/components/registry/niko-table/filters/table-search-filter.js +1 -1
  107. package/dist/components/registry/niko-table/lib/flex-columns.d.ts +1 -1
  108. package/dist/components/registry/niko-table/lib/flex-columns.js +3 -3
  109. package/dist/components/registry/niko-table/lib/styles.d.ts +1 -1
  110. package/dist/components/registry/niko-table/lib/styles.js +3 -3
  111. package/dist/components/registry/niko-table/types/index.d.ts +9 -0
  112. package/dist/components/registry/niko-table/ui/table.js +2 -2
  113. package/dist/components/registry/sidebar-07/app-sidebar.d.ts +1 -1
  114. package/dist/components/registry/sidebar-07/app-sidebar.js +3 -1
  115. package/dist/components/registry/sidebar-07/nav-main.d.ts +2 -1
  116. package/dist/components/registry/sidebar-07/nav-main.js +16 -3
  117. package/dist/components/siyahi/callout.d.ts +24 -0
  118. package/dist/components/siyahi/callout.js +40 -0
  119. package/dist/components/siyahi/change-bar.d.ts +21 -0
  120. package/dist/components/siyahi/change-bar.js +35 -0
  121. package/dist/components/siyahi/data-bar.d.ts +19 -0
  122. package/dist/components/siyahi/data-bar.js +29 -0
  123. package/dist/components/siyahi/data-table-compact-columns-toggle.d.ts +4 -0
  124. package/dist/components/siyahi/data-table-compact-columns-toggle.js +19 -0
  125. package/dist/components/siyahi/data-table-footnote.d.ts +12 -0
  126. package/dist/components/siyahi/data-table-footnote.js +35 -0
  127. package/dist/components/siyahi/default-sidebar-open.d.ts +8 -0
  128. package/dist/components/siyahi/default-sidebar-open.js +13 -0
  129. package/dist/components/siyahi/page-hero.d.ts +25 -0
  130. package/dist/components/siyahi/page-hero.js +41 -0
  131. package/dist/components/siyahi/page-section.d.ts +4 -1
  132. package/dist/components/siyahi/page-section.js +6 -3
  133. package/dist/components/siyahi/page-toc.d.ts +20 -0
  134. package/dist/components/siyahi/page-toc.js +76 -0
  135. package/dist/components/siyahi/panel-grid.d.ts +22 -0
  136. package/dist/components/siyahi/panel-grid.js +34 -0
  137. package/dist/components/siyahi/quote.d.ts +12 -0
  138. package/dist/components/siyahi/quote.js +23 -0
  139. package/dist/components/siyahi/range-position.d.ts +24 -0
  140. package/dist/components/siyahi/range-position.js +105 -0
  141. package/dist/components/siyahi/siyahi-provider.d.ts +13 -2
  142. package/dist/components/siyahi/siyahi-provider.js +17 -4
  143. package/dist/components/siyahi/target-rows.d.ts +34 -0
  144. package/dist/components/siyahi/target-rows.js +97 -0
  145. package/dist/components/siyahi/treemap.d.ts +4 -1
  146. package/dist/components/siyahi/treemap.js +78 -28
  147. package/dist/components/siyahi/warning-alert.d.ts +10 -0
  148. package/dist/components/siyahi/warning-alert.js +20 -0
  149. package/dist/components/ui/accordion.js +5 -5
  150. package/dist/components/ui/alert.js +2 -2
  151. package/dist/components/ui/badge.js +1 -1
  152. package/dist/components/ui/breadcrumb.js +2 -2
  153. package/dist/components/ui/button.js +10 -10
  154. package/dist/components/ui/calendar.js +1 -1
  155. package/dist/components/ui/card.js +4 -4
  156. package/dist/components/ui/chart.js +1 -1
  157. package/dist/components/ui/code-block.js +13 -12
  158. package/dist/components/ui/command.js +7 -6
  159. package/dist/components/ui/context-menu.js +7 -7
  160. package/dist/components/ui/dialog.js +5 -5
  161. package/dist/components/ui/dropdown-menu.js +8 -8
  162. package/dist/components/ui/input-group.js +11 -11
  163. package/dist/components/ui/input.js +1 -1
  164. package/dist/components/ui/popover.js +3 -3
  165. package/dist/components/ui/progress.js +1 -1
  166. package/dist/components/ui/select.js +6 -6
  167. package/dist/components/ui/sheet.js +5 -5
  168. package/dist/components/ui/sidebar.js +17 -18
  169. package/dist/components/ui/skeleton.js +1 -1
  170. package/dist/components/ui/slider.js +2 -2
  171. package/dist/components/ui/table.js +2 -2
  172. package/dist/components/ui/tabs.js +2 -2
  173. package/dist/components/ui/textarea.js +1 -1
  174. package/dist/components/ui/toggle-group.js +2 -2
  175. package/dist/components/ui/toggle.js +4 -4
  176. package/dist/components/ui/tooltip.js +2 -2
  177. package/dist/index.d.ts +21 -7
  178. package/dist/index.js +18 -4
  179. package/dist/lib/chart-frame.d.ts +25 -7
  180. package/dist/lib/chart-frame.js +251 -127
  181. package/dist/lib/chart-series.d.ts +12 -6
  182. package/dist/lib/compact-columns.d.ts +19 -0
  183. package/dist/lib/compact-columns.js +55 -0
  184. package/dist/lib/format.d.ts +7 -2
  185. package/dist/lib/format.js +54 -16
  186. package/dist/lib/heatmap-navigation.d.ts +8 -0
  187. package/dist/lib/heatmap-navigation.js +49 -0
  188. package/dist/lib/treemap.js +6 -1
  189. package/dist/lib/use-element-width.d.ts +5 -0
  190. package/dist/lib/use-element-width.js +15 -0
  191. package/package.json +4 -1
  192. package/registry.lock.json +16 -0
  193. package/report.css +33 -32
  194. package/styles/siyahi.css +221 -39
  195. package/dist/lib/recharts.d.ts +0 -2
  196. package/dist/lib/recharts.js +0 -1
@@ -0,0 +1,7 @@
1
+ [
2
+ "Never paint html or body beyond the base styles report.css sets.",
3
+ "Fluid width. No outer card and no banner title.",
4
+ "A panel with its own background gets 16px padding and var(--radius).",
5
+ "Charts get fixed pixel heights.",
6
+ "Never use 100vh."
7
+ ]
@@ -1,21 +1,39 @@
1
1
  "use client";
2
- import { ChartFrame } from "../../../lib/chart-frame.js";
3
- import { jsx } from "react/jsx-runtime";
2
+ import { ChartFrame, endDot } from "../../../lib/chart-frame.js";
3
+ import { jsx, jsxs } from "react/jsx-runtime";
4
+ import { useId } from "react";
4
5
  import { Area, AreaChart } from "recharts";
5
6
  //#region src/components/registry/charts/area-chart-card.tsx
6
7
  /** Values over an ordered axis as filled areas: one series, or several overlapping or stacked. */
7
8
  function AreaChartCard({ stacked = false, curve = "monotone", ...props }) {
8
- return /* @__PURE__ */ jsx(ChartFrame, {
9
+ const id = useId();
10
+ return /* @__PURE__ */ jsxs(ChartFrame, {
9
11
  chart: AreaChart,
10
12
  ...props,
11
- children: props.series.map(({ key, color }) => /* @__PURE__ */ jsx(Area, {
13
+ children: [/* @__PURE__ */ jsx("defs", { children: props.series.map(({ key, color }, index) => /* @__PURE__ */ jsxs("linearGradient", {
14
+ id: `${id}-${index}`,
15
+ x1: "0",
16
+ y1: "0",
17
+ x2: "0",
18
+ y2: "1",
19
+ children: [/* @__PURE__ */ jsx("stop", {
20
+ offset: "0%",
21
+ stopColor: color,
22
+ stopOpacity: stacked ? .5 : .32
23
+ }), /* @__PURE__ */ jsx("stop", {
24
+ offset: "100%",
25
+ stopColor: color,
26
+ stopOpacity: stacked ? .15 : .02
27
+ })]
28
+ }, key)) }), props.series.map(({ key, color }, index) => /* @__PURE__ */ jsx(Area, {
12
29
  dataKey: key,
13
30
  type: curve,
14
- fill: color,
15
- fillOpacity: stacked ? .4 : .2,
31
+ fill: stacked || index === 0 ? `url(#${id}-${index})` : "none",
16
32
  stroke: color,
33
+ strokeWidth: 2,
34
+ dot: endDot(color, props.data.length),
17
35
  stackId: stacked ? "stack" : void 0
18
- }, key))
36
+ }, key))]
19
37
  });
20
38
  }
21
39
  //#endregion
@@ -10,6 +10,5 @@ export interface BarChartCardProps<T extends object, K extends keyof T> extends
10
10
  /** Called with a clicked category's x value, or null when the picked one is clicked again. */
11
11
  onSelect?: (value: T[K] | null) => void;
12
12
  }
13
- /** Values over an ordered axis as bars: one series, or several grouped or stacked. */
14
13
  export declare function BarChartCard<T extends object, K extends keyof T>({ stacked, selected, onSelect, ...props }: BarChartCardProps<T, K>): import("react").JSX.Element;
15
14
  //#endregion
@@ -1,39 +1,53 @@
1
1
  "use client";
2
- import { ChartFrame } from "../../../lib/chart-frame.js";
2
+ import { ChartFrame, seriesFill } from "../../../lib/chart-frame.js";
3
3
  import { jsx } from "react/jsx-runtime";
4
4
  import { Bar, BarChart, Cell } from "recharts";
5
5
  //#region src/components/registry/charts/bar-chart-card.tsx
6
+ const DOWN_FROM_ZERO = ["auto", 0];
6
7
  /** Values over an ordered axis as bars: one series, or several grouped or stacked. */
8
+ const dimUnselected = (color, isSelected) => isSelected ? color : `color-mix(in oklab, ${color} 55%, var(--card))`;
9
+ const signedFill = (color, isSelected) => `color-mix(in oklab, ${color} ${isSelected ? 35 : 15}%, var(--card))`;
7
10
  function BarChartCard({ stacked = false, selected = null, onSelect, ...props }) {
8
11
  return /* @__PURE__ */ jsx(ChartFrame, {
9
12
  chart: BarChart,
10
13
  ...props,
14
+ yDomain: props.yDomain ?? (props.data.some((row) => props.series.some(({ key }) => Number(row[key]) > 0)) ? void 0 : DOWN_FROM_ZERO),
11
15
  stackOffset: stacked ? "sign" : void 0,
12
- children: props.series.map(({ key, color, negativeColor }, index) => /* @__PURE__ */ jsx(Bar, {
13
- dataKey: key,
14
- fill: color,
15
- stackId: stacked ? "stack" : void 0,
16
- radius: !stacked ? 2 : index === props.series.length - 1 ? props.horizontal ? [
17
- 0,
18
- 2,
19
- 2,
20
- 0
21
- ] : [
22
- 2,
23
- 2,
24
- 0,
25
- 0
26
- ] : 0,
27
- className: onSelect ? "cursor-pointer" : void 0,
28
- onClick: onSelect && ((bar) => {
29
- const value = bar.payload?.[props.xKey];
30
- if (value !== void 0) onSelect(value === selected ? null : value);
31
- }),
32
- children: (negativeColor || selected !== null) && props.data.map((row, i) => /* @__PURE__ */ jsx(Cell, {
33
- fill: negativeColor && Number(row[key]) < 0 ? negativeColor : color,
34
- fillOpacity: selected === null || row[props.xKey] === selected ? 1 : .35
35
- }, i))
36
- }, key))
16
+ onClick: onSelect ? ({ activeTooltipIndex, isTooltipActive }) => {
17
+ if (!isTooltipActive || activeTooltipIndex == null) return;
18
+ const row = props.data[Number(activeTooltipIndex)];
19
+ if (row) onSelect(row[props.xKey] === selected ? null : row[props.xKey]);
20
+ } : void 0,
21
+ children: props.series.map((item, index) => {
22
+ const { key, label, color, negativeColor } = item;
23
+ return /* @__PURE__ */ jsx(Bar, {
24
+ dataKey: key,
25
+ name: label,
26
+ fill: seriesFill(item, props.data),
27
+ maxBarSize: props.horizontal ? 24 : void 0,
28
+ stackId: stacked ? "stack" : void 0,
29
+ radius: !stacked ? 2 : index === props.series.length - 1 ? props.horizontal ? [
30
+ 0,
31
+ 2,
32
+ 2,
33
+ 0
34
+ ] : [
35
+ 2,
36
+ 2,
37
+ 0,
38
+ 0
39
+ ] : 0,
40
+ children: (negativeColor || selected !== null) && props.data.map((row, i) => {
41
+ const isSelected = selected === null || row[props.xKey] === selected;
42
+ const tone = negativeColor && Number(row[key]) < 0 ? negativeColor : color;
43
+ return negativeColor ? /* @__PURE__ */ jsx(Cell, {
44
+ fill: signedFill(tone, isSelected),
45
+ stroke: dimUnselected(tone, isSelected),
46
+ strokeWidth: 1.5
47
+ }, i) : /* @__PURE__ */ jsx(Cell, { fill: dimUnselected(tone, isSelected) }, i);
48
+ })
49
+ }, key);
50
+ })
37
51
  });
38
52
  }
39
53
  //#endregion
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import { ChartFrame } from "../../../lib/chart-frame.js";
2
+ import { ChartFrame, endDot } from "../../../lib/chart-frame.js";
3
3
  import { jsx } from "react/jsx-runtime";
4
4
  import { Line, LineChart } from "recharts";
5
5
  //#region src/components/registry/charts/line-chart-card.tsx
@@ -13,7 +13,7 @@ function LineChartCard({ curve = "monotone", ...props }) {
13
13
  type: curve,
14
14
  stroke: color,
15
15
  strokeWidth: 2,
16
- dot: false
16
+ dot: endDot(color, props.data.length)
17
17
  }, key))
18
18
  });
19
19
  }
@@ -6,6 +6,8 @@ export interface SectionCard {
6
6
  value: React$1.ReactNode;
7
7
  /** Outline badge, top right; usually a <Delta />. */
8
8
  badge?: React$1.ReactNode;
9
+ /** A small chart under the value, usually a <Sparkline /> of the recent run. */
10
+ trend?: React$1.ReactNode;
9
11
  /** Bold first footer line. */
10
12
  headline?: React$1.ReactNode;
11
13
  /** Muted second footer line. */
@@ -1,7 +1,8 @@
1
1
  "use client";
2
2
  import { Badge } from "../../ui/badge.js";
3
- import { Card, CardAction, CardDescription, CardFooter, CardHeader, CardTitle } from "../../ui/card.js";
3
+ import { Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "../../ui/card.js";
4
4
  import { cn } from "../../../lib/utils.js";
5
+ import { useSurface } from "../../siyahi/siyahi-provider.js";
5
6
  import { jsx, jsxs } from "react/jsx-runtime";
6
7
  import "react";
7
8
  //#region src/components/registry/dashboard-01/section-cards.tsx
@@ -13,43 +14,49 @@ const COLUMNS = {
13
14
  6: "@3xl/main:grid-cols-3"
14
15
  };
15
16
  function SectionCards({ items, selected, onSelect }) {
17
+ const ruled = useSurface() === "ruled";
16
18
  return /* @__PURE__ */ jsx("div", {
17
- className: "@container/main",
19
+ className: cn("@container/main", ruled && "overflow-hidden border-y"),
18
20
  children: /* @__PURE__ */ jsx("div", {
19
- className: cn("grid grid-cols-1 gap-4", COLUMNS[items.length] ?? COLUMNS[4]),
20
- children: items.map((item) => /* @__PURE__ */ jsxs(Card, {
21
- className: cn("@container/card", onSelect && "relative", item.label === selected && "ring-2 ring-primary!", onSelect && item.disabled && "opacity-60"),
22
- children: [
23
- onSelect && /* @__PURE__ */ jsx("button", {
24
- type: "button",
25
- "aria-label": item.label,
26
- "aria-pressed": item.label === selected,
27
- disabled: item.disabled,
28
- onClick: () => onSelect(item.label),
29
- className: "absolute inset-0 z-10 rounded-4xl outline-none focus-visible:ring-3 focus-visible:ring-ring/50 focus-visible:ring-inset"
30
- }),
31
- /* @__PURE__ */ jsxs(CardHeader, { children: [
32
- /* @__PURE__ */ jsx(CardDescription, { children: item.label }),
33
- /* @__PURE__ */ jsx(CardTitle, {
34
- className: "text-2xl font-semibold tabular-nums @[250px]/card:text-3xl",
35
- children: item.value
21
+ className: cn("grid grid-cols-1", ruled ? "-mt-px -ml-px" : "gap-4", COLUMNS[items.length] ?? COLUMNS[4]),
22
+ children: items.map((item) => /* @__PURE__ */ jsx("div", {
23
+ "data-slot": "section-card",
24
+ className: cn("min-w-0", ruled && "border-t border-l"),
25
+ children: /* @__PURE__ */ jsxs(Card, {
26
+ className: cn("@container/card h-full [--card-spacing:--spacing(3)]", onSelect && "relative", item.label === selected && "ring-2 ring-primary!", item.label === selected && ruled && "ring-inset", onSelect && item.disabled && "opacity-60"),
27
+ children: [
28
+ onSelect && /* @__PURE__ */ jsx("button", {
29
+ type: "button",
30
+ "aria-label": item.label,
31
+ "aria-pressed": item.label === selected,
32
+ disabled: item.disabled,
33
+ onClick: () => onSelect(item.label),
34
+ className: "absolute inset-0 z-10 rounded-[inherit] outline-none focus-visible:ring-3 focus-visible:ring-ring/50 focus-visible:ring-inset"
36
35
  }),
37
- item.badge && /* @__PURE__ */ jsx(CardAction, { children: /* @__PURE__ */ jsx(Badge, {
38
- variant: "outline",
39
- children: item.badge
40
- }) })
41
- ] }),
42
- (item.headline || item.detail) && /* @__PURE__ */ jsxs(CardFooter, {
43
- className: "flex-col items-start gap-1.5 text-sm",
44
- children: [item.headline && /* @__PURE__ */ jsx("div", {
45
- className: "line-clamp-1 flex gap-2 font-medium",
46
- children: item.headline
47
- }), item.detail && /* @__PURE__ */ jsx("div", {
48
- className: "text-muted-foreground",
49
- children: item.detail
50
- })]
51
- })
52
- ]
36
+ /* @__PURE__ */ jsxs(CardHeader, { children: [
37
+ /* @__PURE__ */ jsx(CardDescription, { children: item.label }),
38
+ /* @__PURE__ */ jsx(CardTitle, {
39
+ className: "text-2xl font-semibold tabular-nums",
40
+ children: item.value
41
+ }),
42
+ item.badge && /* @__PURE__ */ jsx(CardAction, { children: /* @__PURE__ */ jsx(Badge, {
43
+ variant: "outline",
44
+ children: item.badge
45
+ }) })
46
+ ] }),
47
+ item.trend && /* @__PURE__ */ jsx(CardContent, { children: item.trend }),
48
+ (item.headline || item.detail) && /* @__PURE__ */ jsxs(CardFooter, {
49
+ className: "flex-col items-start gap-1 text-sm",
50
+ children: [item.headline && /* @__PURE__ */ jsx("div", {
51
+ className: "flex flex-wrap gap-2 font-medium",
52
+ children: item.headline
53
+ }), item.detail && /* @__PURE__ */ jsx("div", {
54
+ className: "text-muted-foreground",
55
+ children: item.detail
56
+ })]
57
+ })
58
+ ]
59
+ })
53
60
  }, item.label))
54
61
  })
55
62
  });
@@ -1,15 +1,16 @@
1
- import { useLocale } from "../../siyahi/siyahi-provider.js";
2
1
  import { cn } from "../../../lib/utils.js";
2
+ import { useLocale } from "../../siyahi/siyahi-provider.js";
3
3
  import { pct } from "../../../lib/format.js";
4
4
  import { jsx, jsxs } from "react/jsx-runtime";
5
5
  import * as React$1 from "react";
6
6
  //#region src/components/registry/dashboardcn/bar-list.tsx
7
7
  function BarList({ data, valueFormatter, sortOrder = "descending", color = "var(--chart-1)", showPercentage = false, max: scale, onItemClick, className, ...props }) {
8
8
  const locale = useLocale();
9
- const format = valueFormatter ?? ((value) => new Intl.NumberFormat(locale.numberLocale, {
9
+ const defaultFormat = React$1.useMemo(() => new Intl.NumberFormat(locale.numberLocale, {
10
10
  notation: "compact",
11
11
  maximumFractionDigits: 1
12
- }).format(value));
12
+ }).format, [locale.numberLocale]);
13
+ const format = valueFormatter ?? defaultFormat;
13
14
  const items = React$1.useMemo(() => {
14
15
  if (sortOrder === "none") return data;
15
16
  const sorted = [...data].sort((a, b) => a.value - b.value);
@@ -19,7 +20,7 @@ function BarList({ data, valueFormatter, sortOrder = "descending", color = "var(
19
20
  const total = items.reduce((sum, item) => sum + item.value, 0);
20
21
  return /* @__PURE__ */ jsx("div", {
21
22
  "data-slot": "bar-list",
22
- className: cn("flex flex-col gap-1.5", className),
23
+ className: cn("@container/bar-list grid grid-cols-[minmax(0,1fr)_auto] gap-x-3 gap-y-1.5", className),
23
24
  ...props,
24
25
  children: items.map((item) => {
25
26
  const width = max > 0 ? item.value / max * 100 : 0;
@@ -28,16 +29,22 @@ function BarList({ data, valueFormatter, sortOrder = "descending", color = "var(
28
29
  const Label = item.href ? "a" : onItemClick ? "button" : "div";
29
30
  return /* @__PURE__ */ jsxs("div", {
30
31
  "data-slot": "bar-list-row",
31
- className: "flex items-center gap-3 text-sm",
32
+ className: "col-span-2 grid grid-cols-subgrid items-center text-sm @max-sm/bar-list:items-start",
32
33
  children: [/* @__PURE__ */ jsxs("div", {
33
- className: "relative flex h-8 min-w-0 flex-1 items-center",
34
+ className: "relative flex h-8 min-w-0 flex-1 items-center @max-sm/bar-list:h-auto @max-sm/bar-list:min-h-8 @max-sm/bar-list:items-start @max-sm/bar-list:pb-3",
34
35
  children: [
35
36
  /* @__PURE__ */ jsx("div", {
37
+ "data-slot": "bar-list-track",
38
+ "aria-hidden": "true",
39
+ className: "bg-muted/60 absolute inset-0 rounded-md @max-sm/bar-list:inset-y-auto @max-sm/bar-list:bottom-0 @max-sm/bar-list:h-1.5"
40
+ }),
41
+ /* @__PURE__ */ jsx("div", {
42
+ "data-slot": "bar-list-bar",
36
43
  "aria-hidden": "true",
37
- className: "absolute inset-y-0 left-0 rounded-md transition-[width] duration-300",
44
+ className: "absolute inset-y-0 left-0 rounded-md transition-[width] duration-300 @max-sm/bar-list:inset-y-auto @max-sm/bar-list:bottom-0 @max-sm/bar-list:h-1.5",
38
45
  style: {
39
46
  width: `${width}%`,
40
- backgroundColor: `color-mix(in oklab, ${color} 20%, transparent)`
47
+ backgroundColor: item.detail ? "color-mix(in oklab, var(--muted-foreground) 20%, transparent)" : `color-mix(in oklab, ${color} 32%, transparent)`
41
48
  }
42
49
  }),
43
50
  /* @__PURE__ */ jsxs(Label, {
@@ -46,12 +53,12 @@ function BarList({ data, valueFormatter, sortOrder = "descending", color = "var(
46
53
  type: "button",
47
54
  onClick: () => onItemClick(item)
48
55
  } : {},
49
- className: cn("relative flex min-w-0 items-center gap-2 px-2 text-left", interactive && "hover:underline"),
56
+ className: cn("text-foreground relative flex min-w-0 items-center gap-2 px-2 text-left", interactive && "hover:underline"),
50
57
  children: [item.icon ? /* @__PURE__ */ jsx("span", {
51
58
  className: "text-muted-foreground shrink-0 [&>svg]:size-4",
52
59
  children: item.icon
53
60
  }) : null, /* @__PURE__ */ jsx("span", {
54
- className: "truncate",
61
+ className: "truncate @max-sm/bar-list:whitespace-normal",
55
62
  children: item.name
56
63
  })]
57
64
  }),
@@ -62,7 +69,7 @@ function BarList({ data, valueFormatter, sortOrder = "descending", color = "var(
62
69
  }) : null
63
70
  ]
64
71
  }), /* @__PURE__ */ jsxs("div", {
65
- className: "flex shrink-0 items-center gap-2 tabular-nums",
72
+ className: "flex items-center justify-end gap-2 tabular-nums",
66
73
  children: [/* @__PURE__ */ jsx("span", {
67
74
  className: "font-medium",
68
75
  children: format(item.value)
@@ -1,5 +1,5 @@
1
- import { useLocale } from "../../siyahi/siyahi-provider.js";
2
1
  import { cn } from "../../../lib/utils.js";
2
+ import { useLocale } from "../../siyahi/siyahi-provider.js";
3
3
  import { pct } from "../../../lib/format.js";
4
4
  import { jsx, jsxs } from "react/jsx-runtime";
5
5
  import "react";
@@ -0,0 +1,29 @@
1
+ import { Card } from "../../ui/card.js";
2
+ import * as React$1 from "react";
3
+ //#region src/components/registry/dashboardcn/kpi-row-card.d.ts
4
+ export interface MetricStripItem {
5
+ /** Muted label above the figure. */
6
+ label: string;
7
+ /** The figure, formatted by the page: money(4512300). */
8
+ value: React$1.ReactNode;
9
+ /** The signed change, drawn as a Delta under the figure; its sign colours the trend. For "pct", a fraction. */
10
+ change?: number;
11
+ changeFormat?: "pct" | "money";
12
+ /** Muted words after the change, such as "vs last month". */
13
+ note?: React$1.ReactNode;
14
+ /** Recent values, oldest first, drawn as a sparkline beside the figure. */
15
+ trend?: number[];
16
+ }
17
+ export interface MetricStripProps extends Omit<React$1.ComponentProps<typeof Card>, "title"> {
18
+ title?: React$1.ReactNode;
19
+ description?: React$1.ReactNode;
20
+ /** A range toggle, top right, that changes every figure. */
21
+ action?: React$1.ReactNode;
22
+ items: MetricStripItem[];
23
+ /** Lines under the strip, such as what the figures cover. */
24
+ footer?: React$1.ReactNode;
25
+ }
26
+ /** A card holding a row of small figures, each with its change and a sparkline: more numbers in less room than stat tiles. */
27
+ declare function MetricStrip({ title, description, action, items, footer, className, ...props }: MetricStripProps): React$1.JSX.Element;
28
+ //#endregion
29
+ export { MetricStrip };
@@ -0,0 +1,81 @@
1
+ import { Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "../../ui/card.js";
2
+ import { cn } from "../../../lib/utils.js";
3
+ import { Delta } from "../../siyahi/delta.js";
4
+ import { Sparkline } from "./sparkline.js";
5
+ import { jsx, jsxs } from "react/jsx-runtime";
6
+ import "react";
7
+ //#region src/components/registry/dashboardcn/kpi-row-card.tsx
8
+ const COLUMNS = {
9
+ 1: "basis-full",
10
+ 2: "basis-full @2xs/strip:basis-1/2",
11
+ 3: "basis-full @lg/strip:basis-1/3",
12
+ 4: "basis-full @2xs/strip:basis-1/2 @3xl/strip:basis-1/4",
13
+ 5: "basis-full @2xs/strip:basis-1/2 @3xl/strip:basis-1/5",
14
+ 6: "basis-full @2xs/strip:basis-1/2 @xl/strip:basis-1/3 @5xl/strip:basis-1/6",
15
+ 8: "basis-full @2xs/strip:basis-1/2 @3xl/strip:basis-1/4 @7xl/strip:basis-1/8"
16
+ };
17
+ function trendColor(change) {
18
+ if (change === void 0) return "var(--chart-1)";
19
+ if (change > 0) return "var(--positive)";
20
+ if (change < 0) return "var(--negative)";
21
+ return "var(--muted-foreground)";
22
+ }
23
+ /** A card holding a row of small figures, each with its change and a sparkline: more numbers in less room than stat tiles. */
24
+ function MetricStrip({ title, description, action, items, footer, className, ...props }) {
25
+ const columns = COLUMNS[Math.min(items.length, 8)] ?? COLUMNS[8];
26
+ return /* @__PURE__ */ jsxs(Card, {
27
+ className: cn("gap-4", className),
28
+ ...props,
29
+ children: [
30
+ (title || description || action) && /* @__PURE__ */ jsxs(CardHeader, { children: [
31
+ title && /* @__PURE__ */ jsx(CardTitle, { children: title }),
32
+ description && /* @__PURE__ */ jsx(CardDescription, { children: description }),
33
+ action && /* @__PURE__ */ jsx(CardAction, { children: action })
34
+ ] }),
35
+ /* @__PURE__ */ jsx(CardContent, { children: /* @__PURE__ */ jsx("div", {
36
+ "data-slot": "metric-strip",
37
+ className: "@container/strip -mx-4 -my-3 overflow-hidden",
38
+ children: /* @__PURE__ */ jsx("div", {
39
+ className: "-mt-px -ml-px flex flex-wrap",
40
+ children: items.map((item) => /* @__PURE__ */ jsxs("div", {
41
+ "data-slot": "metric-strip-item",
42
+ className: cn("flex min-w-0 flex-col gap-1.5 border-t border-l px-4 py-4", columns),
43
+ children: [/* @__PURE__ */ jsx("div", {
44
+ className: "truncate text-xs text-muted-foreground",
45
+ children: item.label
46
+ }), /* @__PURE__ */ jsxs("div", {
47
+ className: "flex flex-wrap items-end justify-between gap-x-3 gap-y-1",
48
+ children: [/* @__PURE__ */ jsxs("div", {
49
+ className: "flex min-w-0 flex-col gap-0.5",
50
+ children: [/* @__PURE__ */ jsx("div", {
51
+ className: "text-xl font-semibold tracking-tight tabular-nums",
52
+ children: item.value
53
+ }), (item.change !== void 0 || item.note) && /* @__PURE__ */ jsxs("div", {
54
+ className: "flex flex-wrap items-center gap-x-1.5 text-xs",
55
+ children: [item.change !== void 0 && /* @__PURE__ */ jsx(Delta, {
56
+ value: item.change,
57
+ format: item.changeFormat
58
+ }), item.note && /* @__PURE__ */ jsx("span", {
59
+ className: "text-muted-foreground",
60
+ children: item.note
61
+ })]
62
+ })]
63
+ }), item.trend && item.trend.length > 1 && /* @__PURE__ */ jsx(Sparkline, {
64
+ data: item.trend,
65
+ variant: "line",
66
+ color: trendColor(item.change),
67
+ className: "h-9 w-20 shrink-0"
68
+ })]
69
+ })]
70
+ }, item.label))
71
+ })
72
+ }) }),
73
+ footer && /* @__PURE__ */ jsx(CardFooter, {
74
+ className: "flex-col items-start gap-2 text-sm",
75
+ children: footer
76
+ })
77
+ ]
78
+ });
79
+ }
80
+ //#endregion
81
+ export { MetricStrip };
@@ -0,0 +1,22 @@
1
+ import { ChartColor } from "../../../lib/chart-series.js";
2
+ import * as React$1 from "react";
3
+ //#region src/components/registry/dashboardcn/sparkline.d.ts
4
+ export interface SparklineProps extends React$1.ComponentProps<"div"> {
5
+ /** Series to plot, oldest first. */
6
+ data: number[];
7
+ variant?: "area" | "line";
8
+ /**
9
+ * The last step and the dot on the latest value: a chart slot, or var(--positive) or var(--negative) for a gain or
10
+ * a loss. Defaults to var(--chart-1). The run before it is muted.
11
+ */
12
+ color?: ChartColor;
13
+ curve?: "monotone" | "linear" | "step";
14
+ /** The run's line; the last step is 2px. */
15
+ strokeWidth?: number;
16
+ /** Animate after hydration and when data changes. Respects reduced motion. */
17
+ animate?: boolean;
18
+ animationDuration?: number;
19
+ }
20
+ declare function Sparkline({ data, variant, color, curve, strokeWidth, animate, animationDuration, className, ...props }: SparklineProps): React$1.JSX.Element;
21
+ //#endregion
22
+ export { Sparkline };
@@ -0,0 +1,117 @@
1
+ import { cn } from "../../../lib/utils.js";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import * as React$1 from "react";
4
+ import { Area, AreaChart, Line, LineChart, ResponsiveContainer, YAxis } from "recharts";
5
+ //#region src/components/registry/dashboardcn/sparkline.tsx
6
+ const reducedMotionQuery = "(prefers-reduced-motion: reduce)";
7
+ function subscribeToReducedMotion(onChange) {
8
+ const query = window.matchMedia(reducedMotionQuery);
9
+ query.addEventListener("change", onChange);
10
+ return () => query.removeEventListener("change", onChange);
11
+ }
12
+ function getReducedMotionSnapshot() {
13
+ return window.matchMedia(reducedMotionQuery).matches;
14
+ }
15
+ function getReducedMotionServerSnapshot() {
16
+ return true;
17
+ }
18
+ function Sparkline({ data, variant = "area", color = "var(--chart-1)", curve = "linear", strokeWidth = 1.25, animate = true, animationDuration = 500, className, ...props }) {
19
+ const id = React$1.useId();
20
+ const reducedMotion = React$1.useSyncExternalStore(subscribeToReducedMotion, getReducedMotionSnapshot, getReducedMotionServerSnapshot);
21
+ const animationActive = animate && !reducedMotion;
22
+ const points = React$1.useMemo(() => data.map((y, i) => ({
23
+ i,
24
+ y,
25
+ now: i >= data.length - 2 ? y : void 0
26
+ })), [data]);
27
+ const margin = {
28
+ top: 3,
29
+ right: 3,
30
+ bottom: 3,
31
+ left: 0
32
+ };
33
+ const axis = /* @__PURE__ */ jsx(YAxis, {
34
+ hide: true,
35
+ domain: ["dataMin", "dataMax"]
36
+ });
37
+ const now = /* @__PURE__ */ jsx(Line, {
38
+ className: "sparkline-now",
39
+ type: curve,
40
+ dataKey: "now",
41
+ stroke: color,
42
+ strokeWidth: 2,
43
+ dot: (dot) => dot.index === data.length - 1 ? /* @__PURE__ */ jsx("circle", {
44
+ cx: dot.cx,
45
+ cy: dot.cy,
46
+ r: 3,
47
+ fill: color
48
+ }, "now") : /* @__PURE__ */ jsx("g", {}, dot.index),
49
+ isAnimationActive: animationActive,
50
+ animationBegin: animationDuration,
51
+ animationDuration: 150
52
+ });
53
+ return /* @__PURE__ */ jsx("div", {
54
+ "data-slot": "sparkline",
55
+ "aria-hidden": "true",
56
+ className: cn("h-10 w-full", className),
57
+ ...props,
58
+ children: /* @__PURE__ */ jsx(ResponsiveContainer, {
59
+ width: "100%",
60
+ height: "100%",
61
+ children: variant === "line" ? /* @__PURE__ */ jsxs(LineChart, {
62
+ data: points,
63
+ margin,
64
+ children: [
65
+ axis,
66
+ /* @__PURE__ */ jsx(Line, {
67
+ type: curve,
68
+ dataKey: "y",
69
+ stroke: "var(--muted-foreground)",
70
+ strokeWidth,
71
+ dot: false,
72
+ isAnimationActive: animationActive,
73
+ animationDuration,
74
+ animationEasing: "ease-out"
75
+ }),
76
+ now
77
+ ]
78
+ }) : /* @__PURE__ */ jsxs(AreaChart, {
79
+ data: points,
80
+ margin,
81
+ children: [
82
+ axis,
83
+ /* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsxs("linearGradient", {
84
+ id,
85
+ x1: "0",
86
+ y1: "0",
87
+ x2: "0",
88
+ y2: "1",
89
+ children: [/* @__PURE__ */ jsx("stop", {
90
+ offset: "0%",
91
+ stopColor: color,
92
+ stopOpacity: .18
93
+ }), /* @__PURE__ */ jsx("stop", {
94
+ offset: "100%",
95
+ stopColor: color,
96
+ stopOpacity: 0
97
+ })]
98
+ }) }),
99
+ /* @__PURE__ */ jsx(Area, {
100
+ type: curve,
101
+ dataKey: "y",
102
+ stroke: "var(--muted-foreground)",
103
+ strokeWidth,
104
+ fill: `url(#${id})`,
105
+ dot: false,
106
+ isAnimationActive: animationActive,
107
+ animationDuration,
108
+ animationEasing: "ease-out"
109
+ }),
110
+ now
111
+ ]
112
+ })
113
+ })
114
+ });
115
+ }
116
+ //#endregion
117
+ export { Sparkline };
@@ -75,8 +75,10 @@ export type CalendarHeatmapProps = HTMLAttributes<HTMLDivElement> & {
75
75
  * @param levels - Total number of legend cells (including empty when not normalized). Default: 5
76
76
  * @param isNormalized - When true, uses min-max normalization across the dataset (suitable for signed values). When false (default), treats 0 as empty and scales from 0 to max.
77
77
  */
78
- export declare const CalendarHeatmap: ({ data, weekStart, continuousMonths, hasEmptyColumn, blockSize, blockMargin, blockRadius, blockAspectRatio, levels: levelsProp, isNormalized, colors, locale, labels: labelsProp, fontSize, emptyState, totalCount: totalCountProp, style, className, children, ...props }: CalendarHeatmapProps) => string | number | bigint | true | import("react").JSX.Element | Iterable<ReactNode> | Promise<string | number | bigint | boolean | Iterable<ReactNode> | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | import("react").ReactPortal | null | undefined> | null;
78
+ export declare const CalendarHeatmap: ({ data, weekStart, continuousMonths, hasEmptyColumn, blockSize: blockSizeProp, blockMargin, blockRadius, blockAspectRatio, levels: levelsProp, isNormalized, colors, locale, labels: labelsProp, fontSize, emptyState, totalCount: totalCountProp, style, className, children, ...props }: CalendarHeatmapProps) => string | number | bigint | true | import("react").JSX.Element | Iterable<ReactNode> | Promise<string | number | bigint | boolean | Iterable<ReactNode> | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | import("react").ReactPortal | null | undefined> | null;
79
79
  export type CalendarHeatmapBlockProps = HTMLAttributes<SVGRectElement> & {
80
+ /** The cell the page has picked: drawn with a ring and announced as pressed. */
81
+ selected?: boolean;
80
82
  activity: ActivityWithLevel;
81
83
  dayIndex: number;
82
84
  weekIndex: number;
@@ -84,7 +86,7 @@ export type CalendarHeatmapBlockProps = HTMLAttributes<SVGRectElement> & {
84
86
  onCellClick?: (activity: ActivityWithLevel) => void;
85
87
  onCellHover?: (activity: ActivityWithLevel | null) => void;
86
88
  };
87
- export declare function CalendarHeatmapBlock({ ref, activity, dayIndex, weekIndex, highlighted, onCellClick, onCellHover, onClick, onKeyDown, onMouseEnter, onMouseLeave, className, style: styleProp, ...props }: CalendarHeatmapBlockProps & {
89
+ export declare function CalendarHeatmapBlock({ ref, activity, dayIndex, weekIndex, highlighted, selected, onCellClick, onCellHover, onClick, onKeyDown, onMouseEnter, onMouseLeave, className, style: styleProp, ...props }: CalendarHeatmapBlockProps & {
88
90
  ref?: React.RefObject<SVGRectElement | null>;
89
91
  }): import("react").JSX.Element;
90
92
  export declare namespace CalendarHeatmapBlock {