@alpic-ai/ui 0.0.0-staging.gdee4b0b → 0.0.0-staging.gdfe3a14

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 (47) hide show
  1. package/dist/components/area-chart.d.mts +2 -0
  2. package/dist/components/area-chart.mjs +14 -12
  3. package/dist/components/bar-chart.d.mts +2 -0
  4. package/dist/components/bar-chart.mjs +15 -17
  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 +54 -27
  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 +14 -0
  16. package/dist/components/heatmap-chart.mjs +158 -97
  17. package/dist/components/line-chart.d.mts +2 -0
  18. package/dist/components/line-chart.mjs +15 -12
  19. package/dist/components/sidebar.mjs +1 -1
  20. package/dist/components/skeleton.mjs +1 -1
  21. package/dist/components/stat.d.mts +3 -1
  22. package/dist/components/stat.mjs +18 -8
  23. package/dist/components/textarea.mjs +1 -1
  24. package/dist/lib/chart-palette.mjs +20 -17
  25. package/dist/lib/chart.mjs +16 -1
  26. package/package.json +2 -2
  27. package/src/components/area-chart.tsx +23 -13
  28. package/src/components/bar-chart.tsx +24 -14
  29. package/src/components/bar-list.tsx +22 -15
  30. package/src/components/chart-card.tsx +8 -6
  31. package/src/components/chart-container.tsx +2 -0
  32. package/src/components/chart-legend.tsx +10 -2
  33. package/src/components/chart-tooltip.tsx +22 -6
  34. package/src/components/donut-chart.tsx +2 -5
  35. package/src/components/form.tsx +4 -2
  36. package/src/components/heatmap-chart.tsx +109 -21
  37. package/src/components/line-chart.tsx +29 -14
  38. package/src/components/skeleton.tsx +1 -1
  39. package/src/components/stat.tsx +14 -10
  40. package/src/components/textarea.tsx +1 -1
  41. package/src/lib/chart-palette.ts +20 -17
  42. package/src/lib/chart.ts +34 -0
  43. package/src/stories/textarea.stories.tsx +7 -0
  44. package/src/stories/wizard.stories.tsx +1 -1
  45. package/dist/hooks/use-reduced-motion.d.mts +0 -4
  46. package/dist/hooks/use-reduced-motion.mjs +0 -16
  47. 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,10 +1,10 @@
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";
7
+ import { Skeleton } from "./skeleton.mjs";
8
8
  import { jsx, jsxs } from "react/jsx-runtime";
9
9
  import * as React$1 from "react";
10
10
  import { Area, AreaChart as AreaChart$1, CartesianGrid, LabelList, ReferenceArea, ReferenceDot, ReferenceLine, ResponsiveContainer, Tooltip, XAxis, YAxis } from "recharts";
@@ -14,10 +14,9 @@ const CURVE_TYPE = {
14
14
  linear: "linear",
15
15
  step: "stepAfter"
16
16
  };
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 }) {
17
+ 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
18
  const { palette: paletteColors, theme } = useChartContext(palette);
19
19
  const reactId = React$1.useId().replace(/:/g, "");
20
- const animated = !useReducedMotion();
21
20
  const resolved = resolveSeries(series, paletteColors, theme);
22
21
  const stacked = variant === "stacked" || variant === "expand";
23
22
  const rendered = stacked ? orderByLuminance(resolved) : resolved;
@@ -115,19 +114,20 @@ function AreaChart({ data, index, series, variant = "stacked", curve = "monotone
115
114
  const isEmpty = data.length === 0 || rendered.length === 0;
116
115
  return /* @__PURE__ */ jsxs("div", {
117
116
  "data-slot": "area-chart",
118
- className: cn("flex w-full flex-col gap-3", className),
117
+ className: cn("flex w-full flex-col gap-3", "[&_.recharts-surface]:outline-none [&_.recharts-wrapper]:outline-none", className),
119
118
  children: [/* @__PURE__ */ jsx("div", {
120
119
  className: "w-full",
121
120
  style: { height },
122
- children: loading ? /* @__PURE__ */ jsxs("div", {
123
- className: "flex h-full items-center justify-center gap-2.5 font-mono text-quaternary-foreground text-text-xs",
124
- children: [/* @__PURE__ */ jsx("span", { className: "size-4 animate-spin rounded-full border-2 border-border border-t-foreground" }), "loading…"]
125
- }) : isEmpty ? /* @__PURE__ */ jsx("div", {
121
+ children: loading ? /* @__PURE__ */ jsx(Skeleton, { className: "h-full w-full rounded-lg" }) : isEmpty ? /* @__PURE__ */ jsx("div", {
126
122
  className: "flex h-full items-center justify-center rounded-lg border border-border border-dashed font-mono text-quaternary-foreground text-text-xs",
127
123
  children: "no data in range"
128
124
  }) : /* @__PURE__ */ jsx(ResponsiveContainer, {
129
125
  width: "100%",
130
126
  height: "100%",
127
+ initialDimension: {
128
+ width: 0,
129
+ height
130
+ },
131
131
  children: /* @__PURE__ */ jsxs(AreaChart$1, {
132
132
  data,
133
133
  stackOffset: variant === "expand" ? "expand" : "none",
@@ -177,8 +177,9 @@ function AreaChart({ data, index, series, variant = "stacked", curve = "monotone
177
177
  /* @__PURE__ */ jsx(XAxis, {
178
178
  dataKey: index,
179
179
  ...axis,
180
+ tick: makeXAxisTick(theme),
180
181
  interval: "preserveStartEnd",
181
- minTickGap: 44
182
+ minTickGap: 56
182
183
  }),
183
184
  /* @__PURE__ */ jsx(YAxis, {
184
185
  ...axis,
@@ -233,7 +234,7 @@ function AreaChart({ data, index, series, variant = "stacked", curve = "monotone
233
234
  fill: fillFor(entry, slot),
234
235
  dot: false,
235
236
  activeDot: activeDotFor(entry),
236
- isAnimationActive: animated,
237
+ isAnimationActive: false,
237
238
  animationDuration: 650,
238
239
  animationEasing: "ease-out",
239
240
  children: lastValueLabel && /* @__PURE__ */ jsx(LabelList, {
@@ -261,7 +262,8 @@ function AreaChart({ data, index, series, variant = "stacked", curve = "monotone
261
262
  })
262
263
  }), legend && !isEmpty && /* @__PURE__ */ jsx(ChartLegend, {
263
264
  items: legendItems,
264
- style: { paddingLeft: yAxisWidth }
265
+ align: legendAlign,
266
+ insetLeft: yAxisWidth
265
267
  })]
266
268
  });
267
269
  }
@@ -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,20 +1,19 @@
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";
7
+ import { Skeleton } from "./skeleton.mjs";
8
8
  import { jsx, jsxs } from "react/jsx-runtime";
9
9
  import * as React$1 from "react";
10
10
  import { Bar, BarChart as BarChart$1, CartesianGrid, LabelList, ReferenceArea, ReferenceDot, ReferenceLine, ResponsiveContainer, Tooltip, XAxis, YAxis } from "recharts";
11
11
  //#region src/components/bar-chart.tsx
12
12
  const BAR_RADIUS = 4;
13
13
  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 }) {
14
+ 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
15
  const { palette: paletteColors, theme } = useChartContext(palette);
16
16
  const reactId = React$1.useId().replace(/:/g, "");
17
- const animated = !useReducedMotion();
18
17
  const resolved = resolveSeries(series, paletteColors, theme);
19
18
  const stacked = variant === "stacked" || variant === "expand";
20
19
  const rendered = stacked ? orderByLuminance(resolved) : resolved;
@@ -98,19 +97,20 @@ function BarChart({ data, index, series, variant = "stacked", legend = false, va
98
97
  const isEmpty = data.length === 0 || rendered.length === 0;
99
98
  return /* @__PURE__ */ jsxs("div", {
100
99
  "data-slot": "bar-chart",
101
- className: cn("flex w-full flex-col gap-3", className),
100
+ className: cn("flex w-full flex-col gap-3", "[&_.recharts-surface]:outline-none [&_.recharts-wrapper]:outline-none", className),
102
101
  children: [/* @__PURE__ */ jsx("div", {
103
102
  className: "w-full",
104
103
  style: { height },
105
- children: loading ? /* @__PURE__ */ jsxs("div", {
106
- className: "flex h-full items-center justify-center gap-2.5 font-mono text-quaternary-foreground text-text-xs",
107
- children: [/* @__PURE__ */ jsx("span", { className: "size-4 animate-spin rounded-full border-2 border-border border-t-foreground" }), "loading…"]
108
- }) : isEmpty ? /* @__PURE__ */ jsx("div", {
104
+ children: loading ? /* @__PURE__ */ jsx(Skeleton, { className: "h-full w-full rounded-lg" }) : isEmpty ? /* @__PURE__ */ jsx("div", {
109
105
  className: "flex h-full items-center justify-center rounded-lg border border-border border-dashed font-mono text-quaternary-foreground text-text-xs",
110
106
  children: "no data in range"
111
107
  }) : /* @__PURE__ */ jsx(ResponsiveContainer, {
112
108
  width: "100%",
113
109
  height: "100%",
110
+ initialDimension: {
111
+ width: 0,
112
+ height
113
+ },
114
114
  children: /* @__PURE__ */ jsxs(BarChart$1, {
115
115
  data,
116
116
  stackOffset: variant === "expand" ? "expand" : "none",
@@ -161,8 +161,9 @@ function BarChart({ data, index, series, variant = "stacked", legend = false, va
161
161
  /* @__PURE__ */ jsx(XAxis, {
162
162
  dataKey: index,
163
163
  ...axis,
164
+ tick: makeXAxisTick(theme),
164
165
  interval: "preserveStartEnd",
165
- minTickGap: 44
166
+ minTickGap: 56
166
167
  }),
167
168
  /* @__PURE__ */ jsx(YAxis, {
168
169
  ...axis,
@@ -215,12 +216,8 @@ function BarChart({ data, index, series, variant = "stacked", legend = false, va
215
216
  strokeWidth: 0,
216
217
  radius: radiusFor(slot),
217
218
  maxBarSize: MAX_BAR_SIZE,
218
- activeBar: {
219
- fillOpacity: 1,
220
- stroke: theme.card,
221
- strokeWidth: 1
222
- },
223
- isAnimationActive: animated,
219
+ activeBar: false,
220
+ isAnimationActive: false,
224
221
  animationDuration: 650,
225
222
  animationEasing: "ease-out",
226
223
  children: valueLabels && (!stacked || rendered.length === 1) && /* @__PURE__ */ jsx(LabelList, {
@@ -248,7 +245,8 @@ function BarChart({ data, index, series, variant = "stacked", legend = false, va
248
245
  })
249
246
  }), legend && !isEmpty && /* @__PURE__ */ jsx(ChartLegend, {
250
247
  items: legendItems,
251
- style: { paddingLeft: yAxisWidth }
248
+ align: legendAlign,
249
+ insetLeft: yAxisWidth
252
250
  })]
253
251
  });
254
252
  }
@@ -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]",
@@ -3,10 +3,16 @@ import { cn } from "../lib/cn.mjs";
3
3
  import { useChartContext } from "./chart-container.mjs";
4
4
  import { jsx, jsxs } from "react/jsx-runtime";
5
5
  //#region src/components/chart-tooltip.tsx
6
+ const MAX_TOOLTIP_ROWS = 8;
6
7
  function ChartTooltipContent({ active, payload, label, valueFormatter = (value) => value.toLocaleString("en-US"), labelFormatter, hideLabel, showTotal, totalLabel = "Total", className }) {
7
8
  const { theme } = useChartContext();
8
9
  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);
10
+ const toNumber = (value) => typeof value === "number" ? value : Number(value ?? 0);
11
+ const total = payload.reduce((sum, item) => sum + toNumber(item.value), 0);
12
+ const sorted = [...payload].sort((lower, upper) => toNumber(upper.value) - toNumber(lower.value));
13
+ const rows = sorted.slice(0, MAX_TOOLTIP_ROWS);
14
+ const overflow = sorted.slice(MAX_TOOLTIP_ROWS);
15
+ const overflowTotal = overflow.reduce((sum, item) => sum + toNumber(item.value), 0);
10
16
  return /* @__PURE__ */ jsxs("div", {
11
17
  className: cn("min-w-[130px] rounded-lg border px-3 py-2.5 shadow-lg", "border-border bg-popover text-popover-foreground", className),
12
18
  children: [!hideLabel && label !== void 0 && /* @__PURE__ */ jsx("p", {
@@ -14,37 +20,58 @@ function ChartTooltipContent({ active, payload, label, valueFormatter = (value)
14
20
  children: labelFormatter ? labelFormatter(label) : label
15
21
  }), /* @__PURE__ */ jsxs("div", {
16
22
  className: "flex flex-col gap-1",
17
- children: [payload.map((item, index) => {
18
- const swatch = [
19
- item.color,
20
- item.stroke,
21
- item.fill
22
- ].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);
24
- return /* @__PURE__ */ jsxs("div", {
23
+ children: [
24
+ rows.map((item, index) => {
25
+ const swatch = [
26
+ item.color,
27
+ item.stroke,
28
+ item.fill
29
+ ].find((candidate) => typeof candidate === "string" && candidate.length > 0 && !candidate.startsWith("url(")) ?? theme.mutedForeground;
30
+ const numeric = toNumber(item.value);
31
+ return /* @__PURE__ */ jsxs("div", {
32
+ className: "flex items-center justify-between gap-4 text-text-xs",
33
+ children: [/* @__PURE__ */ jsxs("span", {
34
+ className: "inline-flex items-center gap-2 text-muted-foreground",
35
+ children: [/* @__PURE__ */ jsx("span", {
36
+ "aria-hidden": true,
37
+ className: "size-2 shrink-0 rounded-[2px]",
38
+ style: { background: swatch }
39
+ }), item.name]
40
+ }), /* @__PURE__ */ jsx("span", {
41
+ className: "font-mono font-semibold tabular-nums text-foreground",
42
+ children: valueFormatter(numeric)
43
+ })]
44
+ }, `${item.dataKey ?? index}`);
45
+ }),
46
+ overflow.length > 0 && /* @__PURE__ */ jsxs("div", {
25
47
  className: "flex items-center justify-between gap-4 text-text-xs",
26
48
  children: [/* @__PURE__ */ jsxs("span", {
27
- className: "inline-flex items-center gap-2 text-muted-foreground",
28
- children: [/* @__PURE__ */ jsx("span", {
29
- "aria-hidden": true,
30
- className: "size-2 shrink-0 rounded-[2px]",
31
- style: { background: swatch }
32
- }), item.name]
49
+ className: "inline-flex items-center gap-2 text-quaternary-foreground",
50
+ children: [
51
+ /* @__PURE__ */ jsx("span", {
52
+ "aria-hidden": true,
53
+ className: "size-2 shrink-0"
54
+ }),
55
+ "+",
56
+ overflow.length,
57
+ " more"
58
+ ]
59
+ }), /* @__PURE__ */ jsx("span", {
60
+ className: "font-mono font-semibold tabular-nums text-quaternary-foreground",
61
+ children: valueFormatter(overflowTotal)
62
+ })]
63
+ }),
64
+ showTotal && /* @__PURE__ */ jsxs("div", {
65
+ className: "mt-1 flex items-center justify-between gap-4 border-border border-t pt-1.5 text-text-xs",
66
+ children: [/* @__PURE__ */ jsx("span", {
67
+ className: "font-mono text-quaternary-foreground uppercase tracking-wider text-[10px]",
68
+ children: totalLabel
33
69
  }), /* @__PURE__ */ jsx("span", {
34
70
  className: "font-mono font-semibold tabular-nums text-foreground",
35
- children: valueFormatter(numeric)
71
+ children: valueFormatter(total)
36
72
  })]
37
- }, `${item.dataKey ?? index}`);
38
- }), showTotal && /* @__PURE__ */ jsxs("div", {
39
- className: "mt-1 flex items-center justify-between gap-4 border-border border-t pt-1.5 text-text-xs",
40
- children: [/* @__PURE__ */ jsx("span", {
41
- className: "font-mono text-quaternary-foreground uppercase tracking-wider text-[10px]",
42
- children: totalLabel
43
- }), /* @__PURE__ */ jsx("span", {
44
- className: "font-mono font-semibold tabular-nums text-foreground",
45
- children: valueFormatter(total)
46
- })]
47
- })]
73
+ })
74
+ ]
48
75
  })]
49
76
  });
50
77
  }
@@ -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,20 @@ 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
+ }>;
25
+ tooltipTitleKey?: string;
26
+ colorScale?: "linear" | "rank";
27
+ showLegend?: boolean;
19
28
  ariaLabel?: string;
20
29
  className?: string;
21
30
  }
@@ -28,11 +37,16 @@ declare function HeatmapChart({
28
37
  palette,
29
38
  xLabels,
30
39
  yLabels,
40
+ showAllXLabels,
31
41
  highlightPeak,
32
42
  loading,
33
43
  valueFormatter,
34
44
  xTickFormatter,
35
45
  yTickFormatter,
46
+ tooltipMetrics,
47
+ tooltipTitleKey,
48
+ colorScale,
49
+ showLegend,
36
50
  ariaLabel,
37
51
  className
38
52
  }: HeatmapChartProps): React$1.JSX.Element;