@alpic-ai/ui 0.0.0-staging.g5cfa7c7 → 0.0.0-staging.g5fc2bcf

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 (51) hide show
  1. package/dist/components/area-chart.d.mts +2 -0
  2. package/dist/components/area-chart.mjs +10 -6
  3. package/dist/components/bar-chart.d.mts +2 -0
  4. package/dist/components/bar-chart.mjs +10 -6
  5. package/dist/components/bar-list.d.mts +3 -0
  6. package/dist/components/bar-list.mjs +21 -16
  7. package/dist/components/chart-card.d.mts +1 -1
  8. package/dist/components/chart-card.mjs +1 -1
  9. package/dist/components/chart-container.d.mts +1 -1
  10. package/dist/components/chart-legend.d.mts +5 -0
  11. package/dist/components/chart-legend.mjs +11 -2
  12. package/dist/components/chart-tooltip.mjs +5 -3
  13. package/dist/components/donut-chart.mjs +5 -3
  14. package/dist/components/form.mjs +3 -1
  15. package/dist/components/heatmap-chart.d.mts +8 -0
  16. package/dist/components/heatmap-chart.mjs +39 -8
  17. package/dist/components/line-chart.d.mts +2 -0
  18. package/dist/components/line-chart.mjs +11 -6
  19. package/dist/components/stat.d.mts +3 -1
  20. package/dist/components/stat.mjs +14 -4
  21. package/dist/components/textarea.mjs +1 -1
  22. package/dist/lib/chart-palette.mjs +16 -16
  23. package/dist/lib/chart.mjs +16 -1
  24. package/package.json +24 -24
  25. package/src/components/area-chart.tsx +13 -8
  26. package/src/components/bar-chart.tsx +13 -8
  27. package/src/components/bar-list.tsx +22 -15
  28. package/src/components/chart-card.tsx +8 -6
  29. package/src/components/chart-container.tsx +2 -0
  30. package/src/components/chart-legend.tsx +10 -2
  31. package/src/components/chart-tooltip.tsx +6 -6
  32. package/src/components/donut-chart.tsx +2 -5
  33. package/src/components/form.tsx +4 -2
  34. package/src/components/heatmap-chart.tsx +62 -18
  35. package/src/components/line-chart.tsx +19 -9
  36. package/src/components/stat.tsx +10 -6
  37. package/src/components/textarea.tsx +1 -1
  38. package/src/lib/chart-palette.ts +16 -16
  39. package/src/lib/chart.ts +34 -0
  40. package/src/stories/area-chart.stories.tsx +1 -1
  41. package/src/stories/bar-chart.stories.tsx +1 -1
  42. package/src/stories/bar-list.stories.tsx +1 -1
  43. package/src/stories/donut-chart.stories.tsx +1 -1
  44. package/src/stories/heatmap-chart.stories.tsx +1 -1
  45. package/src/stories/line-chart.stories.tsx +1 -1
  46. package/src/stories/textarea.stories.tsx +7 -0
  47. package/src/stories/wizard.stories.tsx +1 -1
  48. package/src/styles/tokens.css +0 -45
  49. package/dist/hooks/use-reduced-motion.d.mts +0 -4
  50. package/dist/hooks/use-reduced-motion.mjs +0 -16
  51. package/src/hooks/use-reduced-motion.ts +0 -17
@@ -21,6 +21,7 @@ interface AreaChartProps {
21
21
  variant?: "stacked" | "grouped" | "expand";
22
22
  curve?: keyof typeof CURVE_TYPE;
23
23
  legend?: boolean;
24
+ legendAlign?: "left" | "center" | "right";
24
25
  valueFlags?: boolean;
25
26
  height?: number;
26
27
  yAxisWidth?: number;
@@ -45,6 +46,7 @@ declare function AreaChart({
45
46
  variant,
46
47
  curve,
47
48
  legend,
49
+ legendAlign,
48
50
  valueFlags,
49
51
  height,
50
52
  yAxisWidth,
@@ -1,7 +1,6 @@
1
1
  "use client";
2
2
  import { cn } from "../lib/cn.mjs";
3
- import { useReducedMotion } from "../hooks/use-reduced-motion.mjs";
4
- import { orderByLuminance, resolveSeries } from "../lib/chart.mjs";
3
+ import { makeXAxisTick, orderByLuminance, resolveSeries } from "../lib/chart.mjs";
5
4
  import { useChartContext } from "./chart-container.mjs";
6
5
  import { ChartLegend } from "./chart-legend.mjs";
7
6
  import { ChartTooltipContent } from "./chart-tooltip.mjs";
@@ -14,10 +13,9 @@ const CURVE_TYPE = {
14
13
  linear: "linear",
15
14
  step: "stepAfter"
16
15
  };
17
- function AreaChart({ data, index, series, variant = "stacked", curve = "monotone", legend = false, valueFlags = false, height = 200, yAxisWidth = 48, palette, referenceLine, markers, lastValueLabel = false, texture = false, loading = false, valueFormatter = (value) => value.toLocaleString("en-US"), labelFormatter, className }) {
16
+ function AreaChart({ data, index, series, variant = "stacked", curve = "monotone", legend = false, legendAlign = "left", valueFlags = false, height = 200, yAxisWidth = 48, palette, referenceLine, markers, lastValueLabel = false, texture = false, loading = false, valueFormatter = (value) => value.toLocaleString("en-US"), labelFormatter, className }) {
18
17
  const { palette: paletteColors, theme } = useChartContext(palette);
19
18
  const reactId = React$1.useId().replace(/:/g, "");
20
- const animated = !useReducedMotion();
21
19
  const resolved = resolveSeries(series, paletteColors, theme);
22
20
  const stacked = variant === "stacked" || variant === "expand";
23
21
  const rendered = stacked ? orderByLuminance(resolved) : resolved;
@@ -128,6 +126,10 @@ function AreaChart({ data, index, series, variant = "stacked", curve = "monotone
128
126
  }) : /* @__PURE__ */ jsx(ResponsiveContainer, {
129
127
  width: "100%",
130
128
  height: "100%",
129
+ initialDimension: {
130
+ width: 0,
131
+ height
132
+ },
131
133
  children: /* @__PURE__ */ jsxs(AreaChart$1, {
132
134
  data,
133
135
  stackOffset: variant === "expand" ? "expand" : "none",
@@ -177,6 +179,7 @@ function AreaChart({ data, index, series, variant = "stacked", curve = "monotone
177
179
  /* @__PURE__ */ jsx(XAxis, {
178
180
  dataKey: index,
179
181
  ...axis,
182
+ tick: makeXAxisTick(theme),
180
183
  interval: "preserveStartEnd",
181
184
  minTickGap: 44
182
185
  }),
@@ -233,7 +236,7 @@ function AreaChart({ data, index, series, variant = "stacked", curve = "monotone
233
236
  fill: fillFor(entry, slot),
234
237
  dot: false,
235
238
  activeDot: activeDotFor(entry),
236
- isAnimationActive: animated,
239
+ isAnimationActive: false,
237
240
  animationDuration: 650,
238
241
  animationEasing: "ease-out",
239
242
  children: lastValueLabel && /* @__PURE__ */ jsx(LabelList, {
@@ -261,7 +264,8 @@ function AreaChart({ data, index, series, variant = "stacked", curve = "monotone
261
264
  })
262
265
  }), legend && !isEmpty && /* @__PURE__ */ jsx(ChartLegend, {
263
266
  items: legendItems,
264
- style: { paddingLeft: yAxisWidth }
267
+ align: legendAlign,
268
+ insetLeft: yAxisWidth
265
269
  })]
266
270
  });
267
271
  }
@@ -10,6 +10,7 @@ interface BarChartProps {
10
10
  series: ChartSeries[];
11
11
  variant?: "stacked" | "grouped" | "expand";
12
12
  legend?: boolean;
13
+ legendAlign?: "left" | "center" | "right";
13
14
  valueLabels?: boolean;
14
15
  height?: number;
15
16
  yAxisWidth?: number;
@@ -32,6 +33,7 @@ declare function BarChart({
32
33
  series,
33
34
  variant,
34
35
  legend,
36
+ legendAlign,
35
37
  valueLabels,
36
38
  height,
37
39
  yAxisWidth,
@@ -1,7 +1,6 @@
1
1
  "use client";
2
2
  import { cn } from "../lib/cn.mjs";
3
- import { useReducedMotion } from "../hooks/use-reduced-motion.mjs";
4
- import { orderByLuminance, resolveSeries } from "../lib/chart.mjs";
3
+ import { makeXAxisTick, orderByLuminance, resolveSeries } from "../lib/chart.mjs";
5
4
  import { useChartContext } from "./chart-container.mjs";
6
5
  import { ChartLegend } from "./chart-legend.mjs";
7
6
  import { ChartTooltipContent } from "./chart-tooltip.mjs";
@@ -11,10 +10,9 @@ import { Bar, BarChart as BarChart$1, CartesianGrid, LabelList, ReferenceArea, R
11
10
  //#region src/components/bar-chart.tsx
12
11
  const BAR_RADIUS = 4;
13
12
  const MAX_BAR_SIZE = 48;
14
- function BarChart({ data, index, series, variant = "stacked", legend = false, valueLabels = false, height = 200, yAxisWidth = 48, palette, referenceLine, markers, texture = false, loading = false, valueFormatter = (value) => value.toLocaleString("en-US"), labelFormatter, className }) {
13
+ function BarChart({ data, index, series, variant = "stacked", legend = false, legendAlign = "left", valueLabels = false, height = 200, yAxisWidth = 48, palette, referenceLine, markers, texture = false, loading = false, valueFormatter = (value) => value.toLocaleString("en-US"), labelFormatter, className }) {
15
14
  const { palette: paletteColors, theme } = useChartContext(palette);
16
15
  const reactId = React$1.useId().replace(/:/g, "");
17
- const animated = !useReducedMotion();
18
16
  const resolved = resolveSeries(series, paletteColors, theme);
19
17
  const stacked = variant === "stacked" || variant === "expand";
20
18
  const rendered = stacked ? orderByLuminance(resolved) : resolved;
@@ -111,6 +109,10 @@ function BarChart({ data, index, series, variant = "stacked", legend = false, va
111
109
  }) : /* @__PURE__ */ jsx(ResponsiveContainer, {
112
110
  width: "100%",
113
111
  height: "100%",
112
+ initialDimension: {
113
+ width: 0,
114
+ height
115
+ },
114
116
  children: /* @__PURE__ */ jsxs(BarChart$1, {
115
117
  data,
116
118
  stackOffset: variant === "expand" ? "expand" : "none",
@@ -161,6 +163,7 @@ function BarChart({ data, index, series, variant = "stacked", legend = false, va
161
163
  /* @__PURE__ */ jsx(XAxis, {
162
164
  dataKey: index,
163
165
  ...axis,
166
+ tick: makeXAxisTick(theme),
164
167
  interval: "preserveStartEnd",
165
168
  minTickGap: 44
166
169
  }),
@@ -220,7 +223,7 @@ function BarChart({ data, index, series, variant = "stacked", legend = false, va
220
223
  stroke: theme.card,
221
224
  strokeWidth: 1
222
225
  },
223
- isAnimationActive: animated,
226
+ isAnimationActive: false,
224
227
  animationDuration: 650,
225
228
  animationEasing: "ease-out",
226
229
  children: valueLabels && (!stacked || rendered.length === 1) && /* @__PURE__ */ jsx(LabelList, {
@@ -248,7 +251,8 @@ function BarChart({ data, index, series, variant = "stacked", legend = false, va
248
251
  })
249
252
  }), legend && !isEmpty && /* @__PURE__ */ jsx(ChartLegend, {
250
253
  items: legendItems,
251
- style: { paddingLeft: yAxisWidth }
254
+ align: legendAlign,
255
+ insetLeft: yAxisWidth
252
256
  })]
253
257
  });
254
258
  }
@@ -8,6 +8,8 @@ interface BarListProps {
8
8
  dataKey?: string;
9
9
  maxItems?: number;
10
10
  palette?: ChartPaletteName;
11
+ /** Renders bars in a single semantic hue (e.g. red for errors) rather than the palette ramp. */
12
+ semantic?: "error" | "warning" | "success";
11
13
  loading?: boolean;
12
14
  valueFormatter?: (value: number) => string;
13
15
  labelFormatter?: (label: string | number) => string;
@@ -19,6 +21,7 @@ declare function BarList({
19
21
  dataKey,
20
22
  maxItems,
21
23
  palette,
24
+ semantic,
22
25
  loading,
23
26
  valueFormatter,
24
27
  labelFormatter,
@@ -1,23 +1,24 @@
1
1
  "use client";
2
2
  import { cn } from "../lib/cn.mjs";
3
- import { useReducedMotion } from "../hooks/use-reduced-motion.mjs";
4
3
  import { rampColor } from "../lib/chart-palette.mjs";
5
4
  import { formatShare } from "../lib/chart.mjs";
6
5
  import { useChartContext } from "./chart-container.mjs";
7
6
  import { jsx, jsxs } from "react/jsx-runtime";
8
7
  import * as React$1 from "react";
9
8
  //#region src/components/bar-list.tsx
9
+ const SEMANTIC_KEY = {
10
+ error: "destructive",
11
+ warning: "warning",
12
+ success: "success"
13
+ };
10
14
  const PLACEHOLDER_HEIGHT = 168;
11
15
  const RAMP_CEILING = .8;
16
+ const SEMANTIC_FLOOR = .62;
12
17
  const IN_FILL_SHADOW = "0 1px 2px rgb(0 0 0 / 0.28)";
13
- function BarList({ data, index, dataKey = "value", maxItems, palette, loading = false, valueFormatter = (value) => value.toLocaleString("en-US"), labelFormatter, className }) {
14
- const { paletteName } = useChartContext(palette);
15
- const reducedMotion = useReducedMotion();
16
- const [mounted, setMounted] = React$1.useState(false);
18
+ function BarList({ data, index, dataKey = "value", maxItems, palette, semantic, loading = false, valueFormatter = (value) => value.toLocaleString("en-US"), labelFormatter, className }) {
19
+ const { paletteName, theme } = useChartContext(palette);
20
+ const accent = semantic ? theme[SEMANTIC_KEY[semantic]] : null;
17
21
  const [active, setActive] = React$1.useState(null);
18
- React$1.useEffect(() => {
19
- setMounted(true);
20
- }, []);
21
22
  const total = React$1.useMemo(() => data.reduce((sum, row) => sum + (Number(row[dataKey]) || 0), 0), [data, dataKey]);
22
23
  const rows = React$1.useMemo(() => {
23
24
  const mapped = data.map((row) => ({
@@ -26,16 +27,21 @@ function BarList({ data, index, dataKey = "value", maxItems, palette, loading =
26
27
  }));
27
28
  mapped.sort((lower, upper) => upper.value - lower.value);
28
29
  const capped = maxItems ? mapped.slice(0, maxItems) : mapped;
29
- return capped.map((row, rank) => ({
30
- ...row,
31
- color: rampColor(paletteName, (capped.length > 1 ? rank / (capped.length - 1) : 0) * RAMP_CEILING)
32
- }));
30
+ return capped.map((row, rank) => {
31
+ const rankFraction = capped.length > 1 ? rank / (capped.length - 1) : 0;
32
+ const accentWeight = Math.round((1 - rankFraction * (1 - SEMANTIC_FLOOR)) * 100);
33
+ return {
34
+ ...row,
35
+ color: accent ? `color-mix(in oklab, ${accent} ${accentWeight}%, white)` : rampColor(paletteName, rankFraction * RAMP_CEILING)
36
+ };
37
+ });
33
38
  }, [
34
39
  data,
35
40
  index,
36
41
  dataKey,
37
42
  maxItems,
38
- paletteName
43
+ paletteName,
44
+ accent
39
45
  ]);
40
46
  const maxValue = rows.reduce((max, row) => row.value > max ? row.value : max, 0);
41
47
  const isEmpty = rows.length === 0;
@@ -55,7 +61,7 @@ function BarList({ data, index, dataKey = "value", maxItems, palette, loading =
55
61
  className: cn("flex w-full flex-col", className),
56
62
  children: rows.map((row, slot) => {
57
63
  const fraction = maxValue > 0 ? row.value / maxValue : 0;
58
- const fillWidth = !reducedMotion && !mounted ? "0%" : `${fraction * 100}%`;
64
+ const fillWidth = `${fraction * 100}%`;
59
65
  const dimmed = active !== null && active !== slot;
60
66
  const isActive = active === slot;
61
67
  return /* @__PURE__ */ jsxs("div", {
@@ -73,8 +79,7 @@ function BarList({ data, index, dataKey = "value", maxItems, palette, loading =
73
79
  width: fillWidth,
74
80
  background: `linear-gradient(90deg, ${row.color}, color-mix(in oklab, ${row.color} 82%, transparent))`,
75
81
  boxShadow: `inset 0 0 0 1px ${row.color}`,
76
- opacity: dimmed ? .45 : 1,
77
- transition: reducedMotion ? void 0 : "width 700ms ease-out"
82
+ opacity: dimmed ? .45 : 1
78
83
  },
79
84
  children: /* @__PURE__ */ jsx("span", {
80
85
  className: "pointer-events-none absolute top-1/2 -translate-y-1/2 truncate type-text-xs font-medium text-white",
@@ -8,7 +8,7 @@ interface ChartCardProps extends Omit<React$1.ComponentProps<"section">, "title"
8
8
  title?: React$1.ReactNode;
9
9
  description?: React$1.ReactNode;
10
10
  action?: React$1.ReactNode;
11
- accent?: "top" | "left";
11
+ accent?: "top" | "left" | "none";
12
12
  }
13
13
  declare function ChartCard({
14
14
  palette,
@@ -12,7 +12,7 @@ function ChartCard({ palette = "magenta", kicker, title, description, action, ac
12
12
  className: cn("chart-rise relative overflow-hidden rounded-xl border bg-card p-5 text-card-foreground shadow-shadow", isLeft && "pl-6", className),
13
13
  ...props,
14
14
  children: [
15
- /* @__PURE__ */ jsx("span", {
15
+ accent !== "none" && /* @__PURE__ */ jsx("span", {
16
16
  "aria-hidden": true,
17
17
  className: cn("absolute", isLeft ? "inset-y-0 left-0 w-[3px]" : "inset-x-0 top-0 h-[3px]"),
18
18
  style: { background: lead }
@@ -17,4 +17,4 @@ declare function ChartContainer({
17
17
  }): React$1.JSX.Element;
18
18
  declare function useChartContext(paletteOverride?: ChartPaletteName): ChartContextValue;
19
19
  //#endregion
20
- export { ChartContainer, type ChartContextValue, useChartContext };
20
+ export { ChartContainer, type ChartContextValue, type ChartPaletteName, useChartContext };
@@ -6,10 +6,15 @@ interface ChartLegendItem {
6
6
  }
7
7
  interface ChartLegendProps extends React.HTMLAttributes<HTMLDivElement> {
8
8
  items: ChartLegendItem[];
9
+ align?: "left" | "center" | "right";
10
+ insetLeft?: number;
9
11
  }
10
12
  declare function ChartLegend({
11
13
  items,
14
+ align,
15
+ insetLeft,
12
16
  className,
17
+ style,
13
18
  ...props
14
19
  }: ChartLegendProps): import("react").JSX.Element;
15
20
  //#endregion
@@ -2,6 +2,11 @@
2
2
  import { cn } from "../lib/cn.mjs";
3
3
  import { jsx, jsxs } from "react/jsx-runtime";
4
4
  //#region src/components/chart-legend.tsx
5
+ const ALIGN_CLASS = {
6
+ left: "justify-start",
7
+ center: "justify-center",
8
+ right: "justify-end"
9
+ };
5
10
  function Swatch({ color, dashed }) {
6
11
  return /* @__PURE__ */ jsx("span", {
7
12
  "aria-hidden": true,
@@ -9,9 +14,13 @@ function Swatch({ color, dashed }) {
9
14
  style: dashed ? { border: `1.5px solid ${color}` } : { background: color }
10
15
  });
11
16
  }
12
- function ChartLegend({ items, className, ...props }) {
17
+ function ChartLegend({ items, align = "left", insetLeft, className, style, ...props }) {
13
18
  return /* @__PURE__ */ jsx("div", {
14
- className: cn("flex flex-wrap gap-x-4 gap-y-1.5", className),
19
+ className: cn("flex flex-wrap gap-x-4 gap-y-1.5", ALIGN_CLASS[align], className),
20
+ style: {
21
+ paddingLeft: align === "left" ? insetLeft : void 0,
22
+ ...style
23
+ },
15
24
  ...props,
16
25
  children: items.map((item) => /* @__PURE__ */ jsxs("span", {
17
26
  className: "inline-flex items-center gap-1.5 font-mono text-[10px] text-muted-foreground uppercase tracking-[0.12em]",
@@ -6,7 +6,9 @@ import { jsx, jsxs } from "react/jsx-runtime";
6
6
  function ChartTooltipContent({ active, payload, label, valueFormatter = (value) => value.toLocaleString("en-US"), labelFormatter, hideLabel, showTotal, totalLabel = "Total", className }) {
7
7
  const { theme } = useChartContext();
8
8
  if (!active || !payload?.length) return null;
9
- const total = payload.reduce((sum, item) => sum + (typeof item.value === "number" ? item.value : Number(item.value ?? 0)), 0);
9
+ const toNumber = (value) => typeof value === "number" ? value : Number(value ?? 0);
10
+ const total = payload.reduce((sum, item) => sum + toNumber(item.value), 0);
11
+ const rows = [...payload].sort((lower, upper) => toNumber(upper.value) - toNumber(lower.value));
10
12
  return /* @__PURE__ */ jsxs("div", {
11
13
  className: cn("min-w-[130px] rounded-lg border px-3 py-2.5 shadow-lg", "border-border bg-popover text-popover-foreground", className),
12
14
  children: [!hideLabel && label !== void 0 && /* @__PURE__ */ jsx("p", {
@@ -14,13 +16,13 @@ function ChartTooltipContent({ active, payload, label, valueFormatter = (value)
14
16
  children: labelFormatter ? labelFormatter(label) : label
15
17
  }), /* @__PURE__ */ jsxs("div", {
16
18
  className: "flex flex-col gap-1",
17
- children: [payload.map((item, index) => {
19
+ children: [rows.map((item, index) => {
18
20
  const swatch = [
19
21
  item.color,
20
22
  item.stroke,
21
23
  item.fill
22
24
  ].find((candidate) => typeof candidate === "string" && candidate.length > 0 && !candidate.startsWith("url(")) ?? theme.mutedForeground;
23
- const numeric = typeof item.value === "number" ? item.value : Number(item.value ?? 0);
25
+ const numeric = toNumber(item.value);
24
26
  return /* @__PURE__ */ jsxs("div", {
25
27
  className: "flex items-center justify-between gap-4 text-text-xs",
26
28
  children: [/* @__PURE__ */ jsxs("span", {
@@ -1,6 +1,5 @@
1
1
  "use client";
2
2
  import { cn } from "../lib/cn.mjs";
3
- import { useReducedMotion } from "../hooks/use-reduced-motion.mjs";
4
3
  import { paletteColor } from "../lib/chart-palette.mjs";
5
4
  import { formatShare } from "../lib/chart.mjs";
6
5
  import { useChartContext } from "./chart-container.mjs";
@@ -21,7 +20,6 @@ const GEOMETRY = {
21
20
  function DonutChart({ data, index, dataKey = "value", variant = "donut", legend = false, paddingAngle = 1, height = 220, palette, centerLabel = "total", loading = false, valueFormatter = (value) => value.toLocaleString("en-US"), labelFormatter, className }) {
22
21
  const { palette: paletteColors, theme } = useChartContext(palette);
23
22
  const reactId = React$1.useId().replace(/:/g, "");
24
- const animated = !useReducedMotion();
25
23
  const [active, setActive] = React$1.useState(null);
26
24
  const rowRefs = React$1.useRef([]);
27
25
  const slices = React$1.useMemo(() => {
@@ -73,6 +71,10 @@ function DonutChart({ data, index, dataKey = "value", variant = "donut", legend
73
71
  children: [/* @__PURE__ */ jsx(ResponsiveContainer, {
74
72
  width: "100%",
75
73
  height: "100%",
74
+ initialDimension: {
75
+ width: 0,
76
+ height
77
+ },
76
78
  children: /* @__PURE__ */ jsxs(PieChart, { children: [/* @__PURE__ */ jsx("defs", { children: slices.map((slice, slot) => /* @__PURE__ */ jsxs("linearGradient", {
77
79
  id: `donut-${reactId}-${slot}`,
78
80
  x1: "0",
@@ -100,7 +102,7 @@ function DonutChart({ data, index, dataKey = "value", variant = "donut", legend
100
102
  cornerRadius: 2,
101
103
  stroke: theme.card,
102
104
  strokeWidth: 1.5,
103
- isAnimationActive: animated,
105
+ isAnimationActive: false,
104
106
  animationDuration: 650,
105
107
  animationEasing: "ease-out",
106
108
  onMouseEnter: (_entry, sliceIndex) => setActive(sliceIndex),
@@ -41,10 +41,12 @@ const useFormField = () => {
41
41
  const FormItemContext = React$1.createContext({});
42
42
  function FormItem({ className, ...props }) {
43
43
  const id = React$1.useId();
44
+ const { name } = React$1.useContext(FormFieldContext);
44
45
  return /* @__PURE__ */ jsx(FormItemContext.Provider, {
45
46
  value: { id },
46
47
  children: /* @__PURE__ */ jsx("div", {
47
48
  "data-slot": "form-item",
49
+ "data-field-name": name,
48
50
  className: cn("grid gap-2", className),
49
51
  ...props
50
52
  })
@@ -65,7 +67,7 @@ function FormLabel({ className, required, tooltip, children, ...props }) {
65
67
  }),
66
68
  required && /* @__PURE__ */ jsx("span", {
67
69
  "aria-hidden": true,
68
- className: "type-text-sm font-medium text-required",
70
+ className: "type-text-sm font-medium text-required leading-none",
69
71
  children: "*"
70
72
  }),
71
73
  tooltip && /* @__PURE__ */ jsxs(Tooltip, { children: [/* @__PURE__ */ jsx(TooltipTrigger, {
@@ -11,11 +11,17 @@ interface HeatmapChartProps {
11
11
  palette?: ChartPaletteName;
12
12
  xLabels?: readonly string[];
13
13
  yLabels?: readonly string[];
14
+ showAllXLabels?: boolean;
14
15
  highlightPeak?: boolean;
15
16
  loading?: boolean;
16
17
  valueFormatter?: (value: number) => string;
17
18
  xTickFormatter?: (label: string) => string;
18
19
  yTickFormatter?: (label: string) => string;
20
+ tooltipMetrics?: ReadonlyArray<{
21
+ key: string;
22
+ label: string;
23
+ format?: (value: number) => string;
24
+ }>;
19
25
  ariaLabel?: string;
20
26
  className?: string;
21
27
  }
@@ -28,11 +34,13 @@ declare function HeatmapChart({
28
34
  palette,
29
35
  xLabels,
30
36
  yLabels,
37
+ showAllXLabels,
31
38
  highlightPeak,
32
39
  loading,
33
40
  valueFormatter,
34
41
  xTickFormatter,
35
42
  yTickFormatter,
43
+ tooltipMetrics,
36
44
  ariaLabel,
37
45
  className
38
46
  }: HeatmapChartProps): React$1.JSX.Element;
@@ -31,7 +31,7 @@ const uniqueInOrder = (values) => {
31
31
  }
32
32
  return ordered;
33
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 }) {
34
+ function HeatmapChart({ data, xKey, yKey, dataKey = "value", variant = "square", palette, xLabels, yLabels, showAllXLabels = false, highlightPeak = true, loading = false, valueFormatter = (value) => value.toLocaleString("en-US"), xTickFormatter, yTickFormatter, tooltipMetrics, ariaLabel = "Activity heatmap", className }) {
35
35
  const { paletteName, theme } = useChartContext(palette);
36
36
  const [hovered, setHovered] = React$1.useState(null);
37
37
  const [mounted, setMounted] = React$1.useState(false);
@@ -42,24 +42,31 @@ function HeatmapChart({ data, xKey, yKey, dataKey = "value", variant = "square",
42
42
  const columns = xLabels ? [...xLabels] : uniqueInOrder(data.map((row) => String(row[xKey] ?? "")));
43
43
  const rows = yLabels ? [...yLabels] : uniqueInOrder(data.map((row) => String(row[yKey] ?? "")));
44
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);
45
+ const recordAt = /* @__PURE__ */ new Map();
46
+ for (const row of data) {
47
+ const key = pairKey(String(row[yKey] ?? ""), String(row[xKey] ?? ""));
48
+ valueAt.set(key, Number(row[dataKey]) || 0);
49
+ recordAt.set(key, row);
50
+ }
46
51
  let maxValue = 0;
47
52
  let peakKey = "";
48
53
  return {
49
54
  columns,
50
55
  rows,
51
56
  cells: rows.flatMap((rowLabel, rowIndex) => columns.map((colLabel, colIndex) => {
52
- const value = valueAt.get(pairKey(rowLabel, colLabel)) ?? 0;
57
+ const key = pairKey(rowLabel, colLabel);
58
+ const value = valueAt.get(key) ?? 0;
53
59
  if (value > maxValue) {
54
60
  maxValue = value;
55
- peakKey = pairKey(rowLabel, colLabel);
61
+ peakKey = key;
56
62
  }
57
63
  return {
58
64
  rowLabel,
59
65
  colLabel,
60
66
  rowIndex,
61
67
  colIndex,
62
- value
68
+ value,
69
+ record: recordAt.get(key) ?? null
63
70
  };
64
71
  })),
65
72
  maxValue,
@@ -87,7 +94,7 @@ function HeatmapChart({ data, xKey, yKey, dataKey = "value", variant = "square",
87
94
  const ramp = heatRamp(paletteName, theme.isDark ? HEAT_EMPTY.dark : HEAT_EMPTY.light);
88
95
  const totalWidth = PAD.left + columns.length * CELL + (columns.length - 1) * GAP + PAD.right;
89
96
  const totalHeight = PAD.top + rows.length * CELL + (rows.length - 1) * GAP + PAD.bottom;
90
- const xStride = Math.ceil(columns.length / MAX_X_TICKS);
97
+ const xStride = showAllXLabels ? 1 : Math.ceil(columns.length / MAX_X_TICKS);
91
98
  const cellX = (col) => PAD.left + col * 25;
92
99
  const cellY = (row) => PAD.top + row * 25;
93
100
  const formatX = (label) => xTickFormatter ? xTickFormatter(label) : label;
@@ -159,7 +166,8 @@ function HeatmapChart({ data, xKey, yKey, dataKey = "value", variant = "square",
159
166
  y: event.clientY,
160
167
  rowLabel: cell.rowLabel,
161
168
  colLabel: cell.colLabel,
162
- value: cell.value
169
+ value: cell.value,
170
+ record: cell.record
163
171
  })
164
172
  }, `hit-${pairKey(cell.rowLabel, cell.colLabel)}`))
165
173
  ]
@@ -177,7 +185,30 @@ function HeatmapChart({ data, xKey, yKey, dataKey = "value", variant = "square",
177
185
  " · ",
178
186
  formatX(hovered.colLabel)
179
187
  ]
180
- }), /* @__PURE__ */ jsxs("div", {
188
+ }), tooltipMetrics && tooltipMetrics.length > 0 ? /* @__PURE__ */ jsx("div", {
189
+ className: "flex flex-col gap-1",
190
+ children: tooltipMetrics.map((metric) => {
191
+ const raw = hovered.record ? Number(hovered.record[metric.key]) || 0 : 0;
192
+ const isActive = metric.key === dataKey;
193
+ return /* @__PURE__ */ jsxs("div", {
194
+ className: "flex items-center justify-between gap-4 text-text-xs",
195
+ children: [/* @__PURE__ */ jsxs("span", {
196
+ className: "inline-flex items-center gap-2 text-muted-foreground",
197
+ children: [/* @__PURE__ */ jsx("span", {
198
+ "aria-hidden": true,
199
+ className: "size-2 shrink-0 rounded-[2px]",
200
+ style: {
201
+ background: isActive ? heatColor(ramp, hovered.value / maxValue) : theme.mutedForeground,
202
+ opacity: isActive ? 1 : .35
203
+ }
204
+ }), metric.label]
205
+ }), /* @__PURE__ */ jsx("span", {
206
+ className: cn("font-mono tabular-nums", isActive ? "font-semibold text-foreground" : "text-muted-foreground"),
207
+ children: (metric.format ?? valueFormatter)(raw)
208
+ })]
209
+ }, metric.key);
210
+ })
211
+ }) : /* @__PURE__ */ jsxs("div", {
181
212
  className: "flex items-center justify-between gap-4 text-text-xs",
182
213
  children: [/* @__PURE__ */ jsxs("span", {
183
214
  className: "inline-flex items-center gap-2 text-muted-foreground",
@@ -15,6 +15,7 @@ interface LineChartProps {
15
15
  series: ChartSeries[];
16
16
  curve?: keyof typeof CURVE_TYPE;
17
17
  legend?: boolean;
18
+ legendAlign?: "left" | "center" | "right";
18
19
  valueFlags?: boolean;
19
20
  dots?: boolean;
20
21
  height?: number;
@@ -38,6 +39,7 @@ declare function LineChart({
38
39
  series,
39
40
  curve,
40
41
  legend,
42
+ legendAlign,
41
43
  valueFlags,
42
44
  dots,
43
45
  height,
@@ -1,7 +1,6 @@
1
1
  "use client";
2
2
  import { cn } from "../lib/cn.mjs";
3
- import { useReducedMotion } from "../hooks/use-reduced-motion.mjs";
4
- import { resolveSeries } from "../lib/chart.mjs";
3
+ import { makeXAxisTick, resolveSeries } from "../lib/chart.mjs";
5
4
  import { useChartContext } from "./chart-container.mjs";
6
5
  import { ChartLegend } from "./chart-legend.mjs";
7
6
  import { ChartTooltipContent } from "./chart-tooltip.mjs";
@@ -14,9 +13,8 @@ const CURVE_TYPE = {
14
13
  linear: "linear",
15
14
  step: "stepAfter"
16
15
  };
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 }) {
16
+ function LineChart({ data, index, series, curve = "monotone", legend = false, legendAlign = "left", valueFlags = false, dots = false, height = 200, yAxisWidth = 48, palette, referenceLine, markers, lastValueLabel = false, loading = false, valueFormatter = (value) => value.toLocaleString("en-US"), labelFormatter, className }) {
18
17
  const { palette: paletteColors, theme } = useChartContext(palette);
19
- const animated = !useReducedMotion();
20
18
  const resolved = resolveSeries(series, paletteColors, theme);
21
19
  const numericMax = React$1.useMemo(() => {
22
20
  let max = 0;
@@ -106,6 +104,10 @@ function LineChart({ data, index, series, curve = "monotone", legend = false, va
106
104
  }) : /* @__PURE__ */ jsx(ResponsiveContainer, {
107
105
  width: "100%",
108
106
  height: "100%",
107
+ initialDimension: {
108
+ width: 0,
109
+ height
110
+ },
109
111
  children: /* @__PURE__ */ jsxs(LineChart$1, {
110
112
  data,
111
113
  margin,
@@ -118,12 +120,14 @@ function LineChart({ data, index, series, curve = "monotone", legend = false, va
118
120
  /* @__PURE__ */ jsx(XAxis, {
119
121
  dataKey: index,
120
122
  ...axis,
123
+ tick: makeXAxisTick(theme),
121
124
  interval: "preserveStartEnd",
122
125
  minTickGap: 44
123
126
  }),
124
127
  /* @__PURE__ */ jsx(YAxis, {
125
128
  ...axis,
126
129
  width: yAxisWidth,
130
+ domain: ["auto", "auto"],
127
131
  tickFormatter: (value) => valueFormatter(value)
128
132
  }),
129
133
  /* @__PURE__ */ jsx(Tooltip, {
@@ -175,7 +179,7 @@ function LineChart({ data, index, series, curve = "monotone", legend = false, va
175
179
  strokeWidth: 0
176
180
  } : false,
177
181
  activeDot: activeDotFor(entry),
178
- isAnimationActive: animated,
182
+ isAnimationActive: false,
179
183
  animationDuration: 650,
180
184
  animationEasing: "ease-out",
181
185
  children: lastValueLabel && /* @__PURE__ */ jsx(LabelList, {
@@ -203,7 +207,8 @@ function LineChart({ data, index, series, curve = "monotone", legend = false, va
203
207
  })
204
208
  }), legend && !isEmpty && /* @__PURE__ */ jsx(ChartLegend, {
205
209
  items: legendItems,
206
- style: { paddingLeft: yAxisWidth }
210
+ align: legendAlign,
211
+ insetLeft: yAxisWidth
207
212
  })]
208
213
  });
209
214
  }
@@ -13,16 +13,18 @@ interface StatDelta {
13
13
  interface StatProps extends React$1.ComponentProps<"div"> {
14
14
  value: React$1.ReactNode;
15
15
  unit?: string;
16
- delta?: StatDelta;
16
+ delta?: StatDelta | null;
17
17
  sparkline?: number[] | Array<{
18
18
  value: number;
19
19
  }>;
20
+ semantic?: "error" | "warning" | "success";
20
21
  }
21
22
  declare function Stat({
22
23
  value,
23
24
  unit,
24
25
  delta,
25
26
  sparkline,
27
+ semantic,
26
28
  className,
27
29
  ...props
28
30
  }: StatProps): React$1.JSX.Element;