@alpic-ai/ui 0.0.0-staging.gc8bbd5f → 0.0.0-staging.gca76f60

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.
@@ -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
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";
@@ -17,7 +17,6 @@ const CURVE_TYPE = {
17
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,14 +114,11 @@ 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, {
@@ -183,7 +179,7 @@ function AreaChart({ data, index, series, variant = "stacked", curve = "monotone
183
179
  ...axis,
184
180
  tick: makeXAxisTick(theme),
185
181
  interval: "preserveStartEnd",
186
- minTickGap: 44
182
+ minTickGap: 56
187
183
  }),
188
184
  /* @__PURE__ */ jsx(YAxis, {
189
185
  ...axis,
@@ -238,7 +234,7 @@ function AreaChart({ data, index, series, variant = "stacked", curve = "monotone
238
234
  fill: fillFor(entry, slot),
239
235
  dot: false,
240
236
  activeDot: activeDotFor(entry),
241
- isAnimationActive: animated,
237
+ isAnimationActive: false,
242
238
  animationDuration: 650,
243
239
  animationEasing: "ease-out",
244
240
  children: lastValueLabel && /* @__PURE__ */ jsx(LabelList, {
@@ -22,6 +22,7 @@ interface BarChartProps {
22
22
  };
23
23
  markers?: ChartMarker[];
24
24
  texture?: boolean;
25
+ partialLastBar?: boolean;
25
26
  loading?: boolean;
26
27
  valueFormatter?: (value: number) => string;
27
28
  labelFormatter?: (label: string | number) => string;
@@ -41,6 +42,7 @@ declare function BarChart({
41
42
  referenceLine,
42
43
  markers,
43
44
  texture,
45
+ partialLastBar,
44
46
  loading,
45
47
  valueFormatter,
46
48
  labelFormatter,
@@ -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
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
- import { Bar, BarChart as BarChart$1, CartesianGrid, LabelList, ReferenceArea, ReferenceDot, ReferenceLine, ResponsiveContainer, Tooltip, XAxis, YAxis } from "recharts";
10
+ import { Bar, BarChart as BarChart$1, CartesianGrid, Cell, 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, legendAlign = "left", 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, partialLastBar = 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;
@@ -39,6 +38,20 @@ function BarChart({ data, index, series, variant = "stacked", legend = false, le
39
38
  series,
40
39
  stacked
41
40
  ]);
41
+ const topSlotByRow = React$1.useMemo(() => {
42
+ if (!stacked) return [];
43
+ return data.map((row) => {
44
+ for (let slot = rendered.length - 1; slot >= 0; slot -= 1) {
45
+ const value = Number(row[rendered[slot]?.key ?? ""]);
46
+ if (Number.isFinite(value) && value > 0) return slot;
47
+ }
48
+ return -1;
49
+ });
50
+ }, [
51
+ data,
52
+ rendered,
53
+ stacked
54
+ ]);
42
55
  const margin = {
43
56
  top: markers?.length || valueLabels ? 18 : 8,
44
57
  right: 8,
@@ -98,14 +111,11 @@ function BarChart({ data, index, series, variant = "stacked", legend = false, le
98
111
  const isEmpty = data.length === 0 || rendered.length === 0;
99
112
  return /* @__PURE__ */ jsxs("div", {
100
113
  "data-slot": "bar-chart",
101
- className: cn("flex w-full flex-col gap-3", className),
114
+ className: cn("flex w-full flex-col gap-3", "[&_.recharts-surface]:outline-none [&_.recharts-wrapper]:outline-none", className),
102
115
  children: [/* @__PURE__ */ jsx("div", {
103
116
  className: "w-full",
104
117
  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", {
118
+ children: loading ? /* @__PURE__ */ jsx(Skeleton, { className: "h-full w-full rounded-lg" }) : isEmpty ? /* @__PURE__ */ jsx("div", {
109
119
  className: "flex h-full items-center justify-center rounded-lg border border-border border-dashed font-mono text-quaternary-foreground text-text-xs",
110
120
  children: "no data in range"
111
121
  }) : /* @__PURE__ */ jsx(ResponsiveContainer, {
@@ -167,7 +177,7 @@ function BarChart({ data, index, series, variant = "stacked", legend = false, le
167
177
  ...axis,
168
178
  tick: makeXAxisTick(theme),
169
179
  interval: "preserveStartEnd",
170
- minTickGap: 44
180
+ minTickGap: 56
171
181
  }),
172
182
  /* @__PURE__ */ jsx(YAxis, {
173
183
  ...axis,
@@ -211,7 +221,7 @@ function BarChart({ data, index, series, variant = "stacked", legend = false, le
211
221
  position: "insideBottomRight"
212
222
  } : void 0
213
223
  }),
214
- rendered.map((entry, slot) => /* @__PURE__ */ jsx(Bar, {
224
+ rendered.map((entry, slot) => /* @__PURE__ */ jsxs(Bar, {
215
225
  dataKey: entry.key,
216
226
  name: entry.name,
217
227
  stackId: stacked ? "stack" : void 0,
@@ -220,18 +230,34 @@ function BarChart({ data, index, series, variant = "stacked", legend = false, le
220
230
  strokeWidth: 0,
221
231
  radius: radiusFor(slot),
222
232
  maxBarSize: MAX_BAR_SIZE,
223
- activeBar: {
224
- fillOpacity: 1,
225
- stroke: theme.card,
226
- strokeWidth: 1
227
- },
228
- isAnimationActive: animated,
233
+ activeBar: false,
234
+ isAnimationActive: false,
229
235
  animationDuration: 650,
230
236
  animationEasing: "ease-out",
231
- children: valueLabels && (!stacked || rendered.length === 1) && /* @__PURE__ */ jsx(LabelList, {
237
+ children: [(stacked || partialLastBar) && data.map((row, rowIndex) => {
238
+ const roundedTop = stacked ? topSlotByRow[rowIndex] === slot : true;
239
+ const isPartial = partialLastBar && rowIndex === data.length - 1;
240
+ return /* @__PURE__ */ jsx(Cell, {
241
+ radius: roundedTop ? [
242
+ BAR_RADIUS,
243
+ BAR_RADIUS,
244
+ 0,
245
+ 0
246
+ ] : [
247
+ 0,
248
+ 0,
249
+ 0,
250
+ 0
251
+ ],
252
+ fillOpacity: isPartial ? .15 : 1,
253
+ stroke: isPartial ? entry.color : void 0,
254
+ strokeWidth: isPartial ? 1.25 : 0,
255
+ strokeDasharray: isPartial ? "3 2" : void 0
256
+ }, `${entry.key}-${row[index] ?? rowIndex}`);
257
+ }), valueLabels && (!stacked || rendered.length === 1) && /* @__PURE__ */ jsx(LabelList, {
232
258
  dataKey: entry.key,
233
259
  content: renderValueLabel(entry.color)
234
- })
260
+ })]
235
261
  }, entry.key)),
236
262
  markers?.map((marker) => /* @__PURE__ */ jsx(ReferenceDot, {
237
263
  x: marker.x,
@@ -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 { rampColor } from "../lib/chart-palette.mjs";
5
4
  import { formatShare } from "../lib/chart.mjs";
6
5
  import { useChartContext } from "./chart-container.mjs";
@@ -19,12 +18,7 @@ const IN_FILL_SHADOW = "0 1px 2px rgb(0 0 0 / 0.28)";
19
18
  function BarList({ data, index, dataKey = "value", maxItems, palette, semantic, loading = false, valueFormatter = (value) => value.toLocaleString("en-US"), labelFormatter, className }) {
20
19
  const { paletteName, theme } = useChartContext(palette);
21
20
  const accent = semantic ? theme[SEMANTIC_KEY[semantic]] : null;
22
- const reducedMotion = useReducedMotion();
23
- const [mounted, setMounted] = React$1.useState(false);
24
21
  const [active, setActive] = React$1.useState(null);
25
- React$1.useEffect(() => {
26
- setMounted(true);
27
- }, []);
28
22
  const total = React$1.useMemo(() => data.reduce((sum, row) => sum + (Number(row[dataKey]) || 0), 0), [data, dataKey]);
29
23
  const rows = React$1.useMemo(() => {
30
24
  const mapped = data.map((row) => ({
@@ -67,7 +61,7 @@ function BarList({ data, index, dataKey = "value", maxItems, palette, semantic,
67
61
  className: cn("flex w-full flex-col", className),
68
62
  children: rows.map((row, slot) => {
69
63
  const fraction = maxValue > 0 ? row.value / maxValue : 0;
70
- const fillWidth = !reducedMotion && !mounted ? "0%" : `${fraction * 100}%`;
64
+ const fillWidth = `${fraction * 100}%`;
71
65
  const dimmed = active !== null && active !== slot;
72
66
  const isActive = active === slot;
73
67
  return /* @__PURE__ */ jsxs("div", {
@@ -85,8 +79,7 @@ function BarList({ data, index, dataKey = "value", maxItems, palette, semantic,
85
79
  width: fillWidth,
86
80
  background: `linear-gradient(90deg, ${row.color}, color-mix(in oklab, ${row.color} 82%, transparent))`,
87
81
  boxShadow: `inset 0 0 0 1px ${row.color}`,
88
- opacity: dimmed ? .45 : 1,
89
- transition: reducedMotion ? void 0 : "width 700ms ease-out"
82
+ opacity: dimmed ? .45 : 1
90
83
  },
91
84
  children: /* @__PURE__ */ jsx("span", {
92
85
  className: "pointer-events-none absolute top-1/2 -translate-y-1/2 truncate type-text-xs font-medium text-white",
@@ -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(() => {
@@ -104,7 +102,7 @@ function DonutChart({ data, index, dataKey = "value", variant = "donut", legend
104
102
  cornerRadius: 2,
105
103
  stroke: theme.card,
106
104
  strokeWidth: 1.5,
107
- isAnimationActive: animated,
105
+ isAnimationActive: false,
108
106
  animationDuration: 650,
109
107
  animationEasing: "ease-out",
110
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
  })
@@ -22,6 +22,9 @@ interface HeatmapChartProps {
22
22
  label: string;
23
23
  format?: (value: number) => string;
24
24
  }>;
25
+ tooltipTitleKey?: string;
26
+ colorScale?: "linear" | "rank";
27
+ showLegend?: boolean;
25
28
  ariaLabel?: string;
26
29
  className?: string;
27
30
  }
@@ -41,6 +44,9 @@ declare function HeatmapChart({
41
44
  xTickFormatter,
42
45
  yTickFormatter,
43
46
  tooltipMetrics,
47
+ tooltipTitleKey,
48
+ colorScale,
49
+ showLegend,
44
50
  ariaLabel,
45
51
  className
46
52
  }: HeatmapChartProps): React$1.JSX.Element;