@alpic-ai/ui 0.0.0-dev.g24bc002 → 0.0.0-dev.g257d293

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 (58) hide show
  1. package/dist/components/area-chart.d.mts +62 -0
  2. package/dist/components/area-chart.mjs +269 -0
  3. package/dist/components/bar-chart.d.mts +48 -0
  4. package/dist/components/bar-chart.mjs +256 -0
  5. package/dist/components/bar-list.d.mts +28 -0
  6. package/dist/components/bar-list.mjs +99 -0
  7. package/dist/components/chart-card.d.mts +25 -0
  8. package/dist/components/chart-card.mjs +48 -0
  9. package/dist/components/chart-container.d.mts +20 -0
  10. package/dist/components/chart-container.mjs +37 -0
  11. package/dist/components/chart-legend.d.mts +16 -0
  12. package/dist/components/chart-legend.mjs +26 -0
  13. package/dist/components/chart-tooltip.d.mts +33 -0
  14. package/dist/components/chart-tooltip.mjs +52 -0
  15. package/dist/components/donut-chart.d.mts +46 -0
  16. package/dist/components/donut-chart.mjs +185 -0
  17. package/dist/components/heatmap-chart.d.mts +40 -0
  18. package/dist/components/heatmap-chart.mjs +198 -0
  19. package/dist/components/line-chart.d.mts +55 -0
  20. package/dist/components/line-chart.mjs +211 -0
  21. package/dist/components/stat.d.mts +30 -0
  22. package/dist/components/stat.mjs +107 -0
  23. package/dist/components/wizard.d.mts +1 -19
  24. package/dist/components/wizard.mjs +1 -19
  25. package/dist/hooks/use-chart-theme.d.mts +18 -0
  26. package/dist/hooks/use-chart-theme.mjs +57 -0
  27. package/dist/hooks/use-reduced-motion.d.mts +4 -0
  28. package/dist/hooks/use-reduced-motion.mjs +16 -0
  29. package/dist/lib/chart-palette.d.mts +4 -0
  30. package/dist/lib/chart-palette.mjs +95 -0
  31. package/dist/lib/chart.d.mts +14 -0
  32. package/dist/lib/chart.mjs +27 -0
  33. package/package.json +24 -23
  34. package/src/components/area-chart.tsx +339 -0
  35. package/src/components/bar-chart.tsx +309 -0
  36. package/src/components/bar-list.tsx +151 -0
  37. package/src/components/chart-card.tsx +63 -0
  38. package/src/components/chart-container.tsx +49 -0
  39. package/src/components/chart-legend.tsx +41 -0
  40. package/src/components/chart-tooltip.tsx +93 -0
  41. package/src/components/donut-chart.tsx +217 -0
  42. package/src/components/heatmap-chart.tsx +287 -0
  43. package/src/components/line-chart.tsx +264 -0
  44. package/src/components/stat.tsx +109 -0
  45. package/src/components/wizard.tsx +1 -35
  46. package/src/hooks/use-chart-theme.ts +75 -0
  47. package/src/hooks/use-reduced-motion.ts +17 -0
  48. package/src/lib/chart-palette.ts +110 -0
  49. package/src/lib/chart.ts +56 -0
  50. package/src/stories/area-chart.stories.tsx +198 -0
  51. package/src/stories/bar-chart.stories.tsx +167 -0
  52. package/src/stories/bar-list.stories.tsx +83 -0
  53. package/src/stories/donut-chart.stories.tsx +110 -0
  54. package/src/stories/heatmap-chart.stories.tsx +105 -0
  55. package/src/stories/line-chart.stories.tsx +144 -0
  56. package/src/stories/stat.stories.tsx +64 -0
  57. package/src/stories/wizard.stories.tsx +22 -4
  58. package/src/styles/tokens.css +18 -0
@@ -0,0 +1,198 @@
1
+ "use client";
2
+ import { cn } from "../lib/cn.mjs";
3
+ import { HEAT_EMPTY, heatColor, heatRamp } from "../lib/chart-palette.mjs";
4
+ import { useChartContext } from "./chart-container.mjs";
5
+ import { jsx, jsxs } from "react/jsx-runtime";
6
+ import * as React$1 from "react";
7
+ import { createPortal } from "react-dom";
8
+ //#region src/components/heatmap-chart.tsx
9
+ const PLACEHOLDER_HEIGHT = 168;
10
+ const CELL = 22;
11
+ const GAP = 3;
12
+ const PAD = {
13
+ left: 36,
14
+ top: 18,
15
+ right: 6,
16
+ bottom: 6
17
+ };
18
+ const MAX_X_TICKS = 13;
19
+ const DOT_MIN = .42;
20
+ const DOT_RANGE = .52;
21
+ const TOOLTIP_OFFSET = 14;
22
+ const TOOLTIP_WIDTH = 150;
23
+ const TOOLTIP_HEIGHT = 64;
24
+ const pairKey = (rowLabel, colLabel) => JSON.stringify([rowLabel, colLabel]);
25
+ const uniqueInOrder = (values) => {
26
+ const seen = /* @__PURE__ */ new Set();
27
+ const ordered = [];
28
+ for (const value of values) if (!seen.has(value)) {
29
+ seen.add(value);
30
+ ordered.push(value);
31
+ }
32
+ return ordered;
33
+ };
34
+ function HeatmapChart({ data, xKey, yKey, dataKey = "value", variant = "square", palette, xLabels, yLabels, highlightPeak = true, loading = false, valueFormatter = (value) => value.toLocaleString("en-US"), xTickFormatter, yTickFormatter, ariaLabel = "Activity heatmap", className }) {
35
+ const { paletteName, theme } = useChartContext(palette);
36
+ const [hovered, setHovered] = React$1.useState(null);
37
+ const [mounted, setMounted] = React$1.useState(false);
38
+ React$1.useEffect(() => {
39
+ setMounted(true);
40
+ }, []);
41
+ const { columns, rows, cells, maxValue, peakKey } = React$1.useMemo(() => {
42
+ const columns = xLabels ? [...xLabels] : uniqueInOrder(data.map((row) => String(row[xKey] ?? "")));
43
+ const rows = yLabels ? [...yLabels] : uniqueInOrder(data.map((row) => String(row[yKey] ?? "")));
44
+ const valueAt = /* @__PURE__ */ new Map();
45
+ for (const row of data) valueAt.set(pairKey(String(row[yKey] ?? ""), String(row[xKey] ?? "")), Number(row[dataKey]) || 0);
46
+ let maxValue = 0;
47
+ let peakKey = "";
48
+ return {
49
+ columns,
50
+ rows,
51
+ cells: rows.flatMap((rowLabel, rowIndex) => columns.map((colLabel, colIndex) => {
52
+ const value = valueAt.get(pairKey(rowLabel, colLabel)) ?? 0;
53
+ if (value > maxValue) {
54
+ maxValue = value;
55
+ peakKey = pairKey(rowLabel, colLabel);
56
+ }
57
+ return {
58
+ rowLabel,
59
+ colLabel,
60
+ rowIndex,
61
+ colIndex,
62
+ value
63
+ };
64
+ })),
65
+ maxValue,
66
+ peakKey
67
+ };
68
+ }, [
69
+ data,
70
+ xKey,
71
+ yKey,
72
+ dataKey,
73
+ xLabels,
74
+ yLabels
75
+ ]);
76
+ const isEmpty = columns.length === 0 || rows.length === 0 || maxValue <= 0;
77
+ if (loading) return /* @__PURE__ */ jsxs("div", {
78
+ className: cn("flex items-center justify-center gap-2.5 font-mono text-quaternary-foreground text-text-xs", className),
79
+ style: { minHeight: PLACEHOLDER_HEIGHT },
80
+ children: [/* @__PURE__ */ jsx("span", { className: "size-4 animate-spin rounded-full border-2 border-border border-t-foreground" }), "loading…"]
81
+ });
82
+ if (isEmpty) return /* @__PURE__ */ jsx("div", {
83
+ className: cn("flex items-center justify-center rounded-lg border border-border border-dashed font-mono text-quaternary-foreground text-text-xs", className),
84
+ style: { minHeight: PLACEHOLDER_HEIGHT },
85
+ children: "no data in range"
86
+ });
87
+ const ramp = heatRamp(paletteName, theme.isDark ? HEAT_EMPTY.dark : HEAT_EMPTY.light);
88
+ const totalWidth = PAD.left + columns.length * CELL + (columns.length - 1) * GAP + PAD.right;
89
+ const totalHeight = PAD.top + rows.length * CELL + (rows.length - 1) * GAP + PAD.bottom;
90
+ const xStride = Math.ceil(columns.length / MAX_X_TICKS);
91
+ const cellX = (col) => PAD.left + col * 25;
92
+ const cellY = (row) => PAD.top + row * 25;
93
+ const formatX = (label) => xTickFormatter ? xTickFormatter(label) : label;
94
+ const formatY = (label) => yTickFormatter ? yTickFormatter(label) : label;
95
+ const tooltipLeft = hovered && hovered.x + TOOLTIP_OFFSET + TOOLTIP_WIDTH > window.innerWidth ? hovered.x - TOOLTIP_OFFSET - TOOLTIP_WIDTH : (hovered?.x ?? 0) + TOOLTIP_OFFSET;
96
+ const tooltipTop = hovered && hovered.y + TOOLTIP_OFFSET + TOOLTIP_HEIGHT > window.innerHeight ? hovered.y - TOOLTIP_OFFSET - TOOLTIP_HEIGHT : (hovered?.y ?? 0) + TOOLTIP_OFFSET;
97
+ return /* @__PURE__ */ jsxs("div", {
98
+ "data-slot": "heatmap-chart",
99
+ className: cn("w-full", className),
100
+ children: [/* @__PURE__ */ jsx("div", {
101
+ className: "overflow-x-auto",
102
+ children: /* @__PURE__ */ jsxs("svg", {
103
+ viewBox: `0 0 ${totalWidth} ${totalHeight}`,
104
+ className: "block min-w-[480px]",
105
+ style: {
106
+ width: "100%",
107
+ height: "auto"
108
+ },
109
+ role: "img",
110
+ "aria-label": ariaLabel,
111
+ onMouseLeave: () => setHovered(null),
112
+ children: [
113
+ columns.map((label, col) => col % xStride === 0 ? /* @__PURE__ */ jsx("text", {
114
+ x: cellX(col) + CELL / 2,
115
+ y: PAD.top - 7,
116
+ textAnchor: "middle",
117
+ className: "fill-quaternary-foreground font-mono text-[8.5px]",
118
+ children: formatX(label)
119
+ }, `x-${label}`) : null),
120
+ rows.map((label, row) => /* @__PURE__ */ jsx("text", {
121
+ x: PAD.left - 8,
122
+ y: cellY(row) + CELL / 2 + 3,
123
+ textAnchor: "end",
124
+ className: "fill-quaternary-foreground font-mono text-[8.5px]",
125
+ children: formatY(label)
126
+ }, `y-${label}`)),
127
+ cells.map((cell) => {
128
+ const fraction = cell.value / maxValue;
129
+ const fill = heatColor(ramp, fraction);
130
+ const key = pairKey(cell.rowLabel, cell.colLabel);
131
+ const isPeak = highlightPeak && key === peakKey;
132
+ if (variant === "dot") return /* @__PURE__ */ jsx("circle", {
133
+ cx: cellX(cell.colIndex) + CELL / 2,
134
+ cy: cellY(cell.rowIndex) + CELL / 2,
135
+ r: CELL / 2 * (DOT_MIN + DOT_RANGE * fraction),
136
+ fill,
137
+ stroke: isPeak ? theme.foreground : void 0,
138
+ strokeWidth: isPeak ? 1.4 : void 0
139
+ }, key);
140
+ return /* @__PURE__ */ jsx("rect", {
141
+ x: cellX(cell.colIndex),
142
+ y: cellY(cell.rowIndex),
143
+ width: CELL,
144
+ height: CELL,
145
+ rx: 2.5,
146
+ fill,
147
+ stroke: isPeak ? theme.foreground : void 0,
148
+ strokeWidth: isPeak ? 1.4 : void 0
149
+ }, key);
150
+ }),
151
+ cells.map((cell) => /* @__PURE__ */ jsx("rect", {
152
+ x: cellX(cell.colIndex),
153
+ y: cellY(cell.rowIndex),
154
+ width: 25,
155
+ height: 25,
156
+ fill: "transparent",
157
+ onMouseMove: (event) => setHovered({
158
+ x: event.clientX,
159
+ y: event.clientY,
160
+ rowLabel: cell.rowLabel,
161
+ colLabel: cell.colLabel,
162
+ value: cell.value
163
+ })
164
+ }, `hit-${pairKey(cell.rowLabel, cell.colLabel)}`))
165
+ ]
166
+ })
167
+ }), mounted && hovered && createPortal(/* @__PURE__ */ jsxs("div", {
168
+ className: "pointer-events-none fixed z-50 min-w-[130px] rounded-lg border border-border bg-popover px-3 py-2.5 text-popover-foreground shadow-lg",
169
+ style: {
170
+ left: tooltipLeft,
171
+ top: tooltipTop
172
+ },
173
+ children: [/* @__PURE__ */ jsxs("p", {
174
+ className: "mb-1.5 font-mono text-[10px] text-quaternary-foreground uppercase tracking-wider",
175
+ children: [
176
+ formatY(hovered.rowLabel),
177
+ " · ",
178
+ formatX(hovered.colLabel)
179
+ ]
180
+ }), /* @__PURE__ */ jsxs("div", {
181
+ className: "flex items-center justify-between gap-4 text-text-xs",
182
+ children: [/* @__PURE__ */ jsxs("span", {
183
+ className: "inline-flex items-center gap-2 text-muted-foreground",
184
+ children: [/* @__PURE__ */ jsx("span", {
185
+ "aria-hidden": true,
186
+ className: "size-2 shrink-0 rounded-[2px]",
187
+ style: { background: heatColor(ramp, hovered.value / maxValue) }
188
+ }), "activity"]
189
+ }), /* @__PURE__ */ jsx("span", {
190
+ className: "font-mono font-semibold tabular-nums text-foreground",
191
+ children: valueFormatter(hovered.value)
192
+ })]
193
+ })]
194
+ }), document.body)]
195
+ });
196
+ }
197
+ //#endregion
198
+ export { HeatmapChart };
@@ -0,0 +1,55 @@
1
+ import { ChartSeries } from "../lib/chart.mjs";
2
+ import { ChartPaletteName } from "../lib/chart-palette.mjs";
3
+ import { ChartMarker } from "./area-chart.mjs";
4
+ import * as React$1 from "react";
5
+
6
+ //#region src/components/line-chart.d.ts
7
+ declare const CURVE_TYPE: {
8
+ readonly monotone: "monotone";
9
+ readonly linear: "linear";
10
+ readonly step: "stepAfter";
11
+ };
12
+ interface LineChartProps {
13
+ data: ReadonlyArray<Record<string, string | number | null | undefined>>;
14
+ index: string;
15
+ series: ChartSeries[];
16
+ curve?: keyof typeof CURVE_TYPE;
17
+ legend?: boolean;
18
+ valueFlags?: boolean;
19
+ dots?: boolean;
20
+ height?: number;
21
+ yAxisWidth?: number;
22
+ palette?: ChartPaletteName;
23
+ referenceLine?: {
24
+ y: number;
25
+ label?: string;
26
+ band?: boolean;
27
+ };
28
+ markers?: ChartMarker[];
29
+ lastValueLabel?: boolean;
30
+ loading?: boolean;
31
+ valueFormatter?: (value: number) => string;
32
+ labelFormatter?: (label: string | number) => string;
33
+ className?: string;
34
+ }
35
+ declare function LineChart({
36
+ data,
37
+ index,
38
+ series,
39
+ curve,
40
+ legend,
41
+ valueFlags,
42
+ dots,
43
+ height,
44
+ yAxisWidth,
45
+ palette,
46
+ referenceLine,
47
+ markers,
48
+ lastValueLabel,
49
+ loading,
50
+ valueFormatter,
51
+ labelFormatter,
52
+ className
53
+ }: LineChartProps): React$1.JSX.Element;
54
+ //#endregion
55
+ export { LineChart, LineChartProps };
@@ -0,0 +1,211 @@
1
+ "use client";
2
+ import { cn } from "../lib/cn.mjs";
3
+ import { useReducedMotion } from "../hooks/use-reduced-motion.mjs";
4
+ import { resolveSeries } from "../lib/chart.mjs";
5
+ import { useChartContext } from "./chart-container.mjs";
6
+ import { ChartLegend } from "./chart-legend.mjs";
7
+ import { ChartTooltipContent } from "./chart-tooltip.mjs";
8
+ import { jsx, jsxs } from "react/jsx-runtime";
9
+ import * as React$1 from "react";
10
+ import { CartesianGrid, LabelList, Line, LineChart as LineChart$1, ReferenceArea, ReferenceDot, ReferenceLine, ResponsiveContainer, Tooltip, XAxis, YAxis } from "recharts";
11
+ //#region src/components/line-chart.tsx
12
+ const CURVE_TYPE = {
13
+ monotone: "monotone",
14
+ linear: "linear",
15
+ step: "stepAfter"
16
+ };
17
+ function LineChart({ data, index, series, curve = "monotone", legend = false, valueFlags = false, dots = false, height = 200, yAxisWidth = 48, palette, referenceLine, markers, lastValueLabel = false, loading = false, valueFormatter = (value) => value.toLocaleString("en-US"), labelFormatter, className }) {
18
+ const { palette: paletteColors, theme } = useChartContext(palette);
19
+ const animated = !useReducedMotion();
20
+ const resolved = resolveSeries(series, paletteColors, theme);
21
+ const numericMax = React$1.useMemo(() => {
22
+ let max = 0;
23
+ for (const row of data) for (const entry of series) {
24
+ const value = Number(row[entry.key]);
25
+ if (Number.isFinite(value) && value > max) max = value;
26
+ }
27
+ return max;
28
+ }, [data, series]);
29
+ const curveType = CURVE_TYPE[curve];
30
+ const margin = {
31
+ top: markers?.length ? 18 : 8,
32
+ right: lastValueLabel ? 56 : 8,
33
+ bottom: 2,
34
+ left: 0
35
+ };
36
+ const axis = {
37
+ stroke: theme.border,
38
+ tick: {
39
+ fill: theme.axisForeground,
40
+ fontSize: 10,
41
+ fontFamily: theme.fontMono
42
+ },
43
+ tickLine: false,
44
+ axisLine: {
45
+ stroke: theme.border,
46
+ strokeOpacity: .6
47
+ }
48
+ };
49
+ const legendItems = resolved.map((entry) => ({
50
+ name: entry.name,
51
+ color: entry.color,
52
+ dashed: entry.dashed
53
+ }));
54
+ const activeDotFor = (entry) => valueFlags ? (dotProps) => {
55
+ if (dotProps.cx == null || dotProps.cy == null) return /* @__PURE__ */ jsx("g", {});
56
+ return /* @__PURE__ */ jsxs("g", { children: [/* @__PURE__ */ jsx("circle", {
57
+ cx: dotProps.cx,
58
+ cy: dotProps.cy,
59
+ r: 3.5,
60
+ fill: entry.color,
61
+ stroke: theme.card,
62
+ strokeWidth: 2
63
+ }), /* @__PURE__ */ jsx("text", {
64
+ x: dotProps.cx,
65
+ y: dotProps.cy - 8,
66
+ textAnchor: "middle",
67
+ fill: entry.color,
68
+ fontFamily: theme.fontMono,
69
+ fontSize: 10,
70
+ style: { fontVariantNumeric: "tabular-nums" },
71
+ children: valueFormatter(Number(dotProps.value ?? 0))
72
+ })] });
73
+ } : {
74
+ r: 3.5,
75
+ fill: entry.color,
76
+ stroke: theme.card,
77
+ strokeWidth: 2
78
+ };
79
+ const renderLastLabel = (color) => (props) => {
80
+ if (props.index !== data.length - 1 || props.x == null || props.y == null) return null;
81
+ return /* @__PURE__ */ jsx("text", {
82
+ x: Number(props.x) + 6,
83
+ y: Number(props.y),
84
+ dy: 3,
85
+ fill: color,
86
+ fontFamily: theme.fontMono,
87
+ fontSize: 10,
88
+ textAnchor: "start",
89
+ style: { fontVariantNumeric: "tabular-nums" },
90
+ children: valueFormatter(Number(props.value ?? 0))
91
+ });
92
+ };
93
+ const isEmpty = data.length === 0 || resolved.length === 0;
94
+ return /* @__PURE__ */ jsxs("div", {
95
+ "data-slot": "line-chart",
96
+ className: cn("flex w-full flex-col gap-3", className),
97
+ children: [/* @__PURE__ */ jsx("div", {
98
+ className: "w-full",
99
+ style: { height },
100
+ children: loading ? /* @__PURE__ */ jsxs("div", {
101
+ className: "flex h-full items-center justify-center gap-2.5 font-mono text-quaternary-foreground text-text-xs",
102
+ children: [/* @__PURE__ */ jsx("span", { className: "size-4 animate-spin rounded-full border-2 border-border border-t-foreground" }), "loading…"]
103
+ }) : isEmpty ? /* @__PURE__ */ jsx("div", {
104
+ className: "flex h-full items-center justify-center rounded-lg border border-border border-dashed font-mono text-quaternary-foreground text-text-xs",
105
+ children: "no data in range"
106
+ }) : /* @__PURE__ */ jsx(ResponsiveContainer, {
107
+ width: "100%",
108
+ height: "100%",
109
+ children: /* @__PURE__ */ jsxs(LineChart$1, {
110
+ data,
111
+ margin,
112
+ children: [
113
+ /* @__PURE__ */ jsx(CartesianGrid, {
114
+ vertical: false,
115
+ stroke: theme.grid,
116
+ strokeDasharray: "2 4"
117
+ }),
118
+ /* @__PURE__ */ jsx(XAxis, {
119
+ dataKey: index,
120
+ ...axis,
121
+ interval: "preserveStartEnd",
122
+ minTickGap: 44
123
+ }),
124
+ /* @__PURE__ */ jsx(YAxis, {
125
+ ...axis,
126
+ width: yAxisWidth,
127
+ tickFormatter: (value) => valueFormatter(value)
128
+ }),
129
+ /* @__PURE__ */ jsx(Tooltip, {
130
+ offset: 12,
131
+ allowEscapeViewBox: {
132
+ x: false,
133
+ y: false
134
+ },
135
+ cursor: {
136
+ stroke: theme.axisForeground,
137
+ strokeWidth: 1,
138
+ strokeDasharray: "3 3"
139
+ },
140
+ content: /* @__PURE__ */ jsx(ChartTooltipContent, {
141
+ valueFormatter,
142
+ labelFormatter
143
+ })
144
+ }),
145
+ referenceLine?.band && /* @__PURE__ */ jsx(ReferenceArea, {
146
+ y1: referenceLine.y,
147
+ y2: numericMax,
148
+ fill: theme.warning,
149
+ fillOpacity: .06,
150
+ ifOverflow: "extendDomain"
151
+ }),
152
+ referenceLine && /* @__PURE__ */ jsx(ReferenceLine, {
153
+ y: referenceLine.y,
154
+ stroke: theme.warning,
155
+ strokeDasharray: "4 4",
156
+ strokeOpacity: .6,
157
+ label: referenceLine.label ? {
158
+ value: referenceLine.label,
159
+ fill: theme.warning,
160
+ fontSize: 9,
161
+ fontFamily: theme.fontMono,
162
+ position: "insideBottomRight"
163
+ } : void 0
164
+ }),
165
+ resolved.map((entry) => /* @__PURE__ */ jsx(Line, {
166
+ type: curveType,
167
+ dataKey: entry.key,
168
+ name: entry.name,
169
+ stroke: entry.color,
170
+ strokeWidth: entry.dashed ? 2 : 1.8,
171
+ strokeDasharray: entry.dashed ? "5 3" : void 0,
172
+ dot: dots ? {
173
+ r: 2.5,
174
+ fill: entry.color,
175
+ strokeWidth: 0
176
+ } : false,
177
+ activeDot: activeDotFor(entry),
178
+ isAnimationActive: animated,
179
+ animationDuration: 650,
180
+ animationEasing: "ease-out",
181
+ children: lastValueLabel && /* @__PURE__ */ jsx(LabelList, {
182
+ dataKey: entry.key,
183
+ content: renderLastLabel(entry.color)
184
+ })
185
+ }, entry.key)),
186
+ markers?.map((marker) => /* @__PURE__ */ jsx(ReferenceDot, {
187
+ x: marker.x,
188
+ y: marker.y,
189
+ r: 3.5,
190
+ fill: marker.color ?? theme.foreground,
191
+ stroke: theme.card,
192
+ strokeWidth: 2,
193
+ label: marker.label ? {
194
+ value: marker.label,
195
+ fill: marker.color ?? theme.foreground,
196
+ fontSize: 9,
197
+ fontFamily: theme.fontMono,
198
+ position: "top"
199
+ } : void 0
200
+ }, `${marker.x}-${marker.y}`))
201
+ ]
202
+ })
203
+ })
204
+ }), legend && !isEmpty && /* @__PURE__ */ jsx(ChartLegend, {
205
+ items: legendItems,
206
+ style: { paddingLeft: yAxisWidth }
207
+ })]
208
+ });
209
+ }
210
+ //#endregion
211
+ export { LineChart };
@@ -0,0 +1,30 @@
1
+ import * as React$1 from "react";
2
+
3
+ //#region src/components/stat.d.ts
4
+ declare const statDeltaVariants: (props?: ({
5
+ sentiment?: "positive" | "negative" | null | undefined;
6
+ } & import("class-variance-authority/types").ClassProp) | undefined) => string;
7
+ interface StatDelta {
8
+ value: number;
9
+ direction: "up" | "down";
10
+ invert?: boolean;
11
+ label?: React$1.ReactNode;
12
+ }
13
+ interface StatProps extends React$1.ComponentProps<"div"> {
14
+ value: React$1.ReactNode;
15
+ unit?: string;
16
+ delta?: StatDelta;
17
+ sparkline?: number[] | Array<{
18
+ value: number;
19
+ }>;
20
+ }
21
+ declare function Stat({
22
+ value,
23
+ unit,
24
+ delta,
25
+ sparkline,
26
+ className,
27
+ ...props
28
+ }: StatProps): React$1.JSX.Element;
29
+ //#endregion
30
+ export { Stat, StatDelta, StatProps, statDeltaVariants };
@@ -0,0 +1,107 @@
1
+ "use client";
2
+ import { cn } from "../lib/cn.mjs";
3
+ import { useChartContext } from "./chart-container.mjs";
4
+ import { ArrowDown, ArrowUp } from "lucide-react";
5
+ import { jsx, jsxs } from "react/jsx-runtime";
6
+ import { cva } from "class-variance-authority";
7
+ import * as React$1 from "react";
8
+ import { Area, AreaChart, ResponsiveContainer } from "recharts";
9
+ //#region src/components/stat.tsx
10
+ const statDeltaVariants = cva("inline-flex items-center gap-0.5 rounded-md px-1.5 py-0.5 font-mono text-[11px] font-medium leading-none", {
11
+ variants: { sentiment: {
12
+ positive: "text-success bg-success/12",
13
+ negative: "text-destructive bg-destructive/12"
14
+ } },
15
+ defaultVariants: { sentiment: "positive" }
16
+ });
17
+ const toSparkData = (sparkline) => (sparkline ?? []).map((point, index) => typeof point === "number" ? {
18
+ index,
19
+ value: point
20
+ } : {
21
+ index,
22
+ value: point.value
23
+ });
24
+ function Stat({ value, unit, delta, sparkline, className, ...props }) {
25
+ const { palette } = useChartContext();
26
+ const gradientId = React$1.useId().replace(/:/g, "");
27
+ const sparkData = React$1.useMemo(() => toSparkData(sparkline), [sparkline]);
28
+ const sparkColor = palette[0];
29
+ const sentiment = delta && (delta.invert ? delta.direction === "down" : delta.direction === "up") ? "positive" : "negative";
30
+ return /* @__PURE__ */ jsxs("div", {
31
+ "data-slot": "stat",
32
+ className: cn("flex flex-col gap-2.5", className),
33
+ ...props,
34
+ children: [/* @__PURE__ */ jsxs("div", {
35
+ className: "flex items-baseline gap-3",
36
+ children: [
37
+ /* @__PURE__ */ jsx("span", {
38
+ className: "type-display-sm font-bold leading-none tracking-tight tabular-nums text-foreground",
39
+ children: value
40
+ }),
41
+ unit && /* @__PURE__ */ jsx("span", {
42
+ className: "font-mono text-[11px] leading-none text-quaternary-foreground mb-0.5",
43
+ children: unit
44
+ }),
45
+ delta && /* @__PURE__ */ jsxs(DeltaPill, {
46
+ sentiment,
47
+ className: "mb-0.5",
48
+ children: [delta.direction === "up" ? /* @__PURE__ */ jsx(ArrowUp, { className: "size-3" }) : /* @__PURE__ */ jsx(ArrowDown, { className: "size-3" }), delta.label ?? `${delta.value}%`]
49
+ })
50
+ ]
51
+ }), sparkData.length > 0 && /* @__PURE__ */ jsx("div", {
52
+ className: "h-9 w-full",
53
+ children: /* @__PURE__ */ jsx(ResponsiveContainer, {
54
+ width: "100%",
55
+ height: "100%",
56
+ children: /* @__PURE__ */ jsxs(AreaChart, {
57
+ data: sparkData,
58
+ margin: {
59
+ top: 4,
60
+ right: 2,
61
+ bottom: 0,
62
+ left: 2
63
+ },
64
+ children: [/* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsxs("linearGradient", {
65
+ id: gradientId,
66
+ x1: "0",
67
+ y1: "0",
68
+ x2: "0",
69
+ y2: "1",
70
+ children: [/* @__PURE__ */ jsx("stop", {
71
+ offset: "0%",
72
+ stopColor: sparkColor,
73
+ stopOpacity: .4
74
+ }), /* @__PURE__ */ jsx("stop", {
75
+ offset: "100%",
76
+ stopColor: sparkColor,
77
+ stopOpacity: 0
78
+ })]
79
+ }) }), /* @__PURE__ */ jsx(Area, {
80
+ type: "monotone",
81
+ dataKey: "value",
82
+ stroke: sparkColor,
83
+ strokeWidth: 1.6,
84
+ fill: `url(#${gradientId})`,
85
+ isAnimationActive: false,
86
+ dot: ({ cx, cy, index }) => {
87
+ return index === sparkData.length - 1 && cx != null && cy != null ? /* @__PURE__ */ jsx("circle", {
88
+ cx,
89
+ cy,
90
+ r: 2.5,
91
+ fill: sparkColor
92
+ }, index) : /* @__PURE__ */ jsx("g", {}, index);
93
+ }
94
+ })]
95
+ })
96
+ })
97
+ })]
98
+ });
99
+ }
100
+ function DeltaPill({ sentiment, className, ...props }) {
101
+ return /* @__PURE__ */ jsx("span", {
102
+ className: cn(statDeltaVariants({ sentiment }), className),
103
+ ...props
104
+ });
105
+ }
106
+ //#endregion
107
+ export { Stat, statDeltaVariants };
@@ -1,24 +1,6 @@
1
1
  import * as React$1 from "react";
2
2
 
3
3
  //#region src/components/wizard.d.ts
4
- interface WizardStep {
5
- id: string;
6
- label: string;
7
- }
8
- interface WizardStepsProps {
9
- steps: readonly WizardStep[];
10
- activeIdx: number;
11
- onSelect: (idx: number) => void;
12
- ariaLabel?: string;
13
- className?: string;
14
- }
15
- declare function WizardSteps({
16
- steps,
17
- activeIdx,
18
- onSelect,
19
- ariaLabel,
20
- className
21
- }: WizardStepsProps): React$1.JSX.Element;
22
4
  interface WizardProgressProps extends React$1.ComponentProps<"div"> {
23
5
  current: number;
24
6
  total: number;
@@ -30,4 +12,4 @@ declare function WizardProgress({
30
12
  ...props
31
13
  }: WizardProgressProps): React$1.JSX.Element;
32
14
  //#endregion
33
- export { WizardProgress, type WizardStep, WizardSteps };
15
+ export { WizardProgress };
@@ -1,25 +1,7 @@
1
1
  "use client";
2
2
  import { cn } from "../lib/cn.mjs";
3
- import { TabsNav, TabsNavList, TabsNavTrigger } from "./tabs.mjs";
4
3
  import { jsx, jsxs } from "react/jsx-runtime";
5
4
  //#region src/components/wizard.tsx
6
- function WizardSteps({ steps, activeIdx, onSelect, ariaLabel = "Wizard steps", className }) {
7
- return /* @__PURE__ */ jsx(TabsNav, {
8
- orientation: "vertical",
9
- "aria-label": ariaLabel,
10
- className,
11
- children: /* @__PURE__ */ jsx(TabsNavList, { children: steps.map((step, idx) => /* @__PURE__ */ jsx(TabsNavTrigger, {
12
- active: idx === activeIdx,
13
- asChild: true,
14
- children: /* @__PURE__ */ jsx("button", {
15
- type: "button",
16
- onClick: () => onSelect(idx),
17
- className: "w-full justify-start text-left",
18
- children: step.label
19
- })
20
- }, step.id)) })
21
- });
22
- }
23
5
  function WizardProgress({ current, total, className, ...props }) {
24
6
  const percent = total > 0 ? Math.round(current / total * 100) : 0;
25
7
  return /* @__PURE__ */ jsxs("div", {
@@ -43,4 +25,4 @@ function WizardProgress({ current, total, className, ...props }) {
43
25
  });
44
26
  }
45
27
  //#endregion
46
- export { WizardProgress, WizardSteps };
28
+ export { WizardProgress };
@@ -0,0 +1,18 @@
1
+ //#region src/hooks/use-chart-theme.d.ts
2
+ interface ChartTheme {
3
+ foreground: string;
4
+ mutedForeground: string;
5
+ axisForeground: string;
6
+ grid: string;
7
+ border: string;
8
+ card: string;
9
+ popover: string;
10
+ destructive: string;
11
+ warning: string;
12
+ success: string;
13
+ fontMono: string;
14
+ isDark: boolean;
15
+ }
16
+ declare function useChartTheme(): ChartTheme;
17
+ //#endregion
18
+ export { ChartTheme, useChartTheme };