@alpic-ai/ui 0.0.0-dev.g19fc228 → 0.0.0-dev.g1a5d5ed
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.
- package/dist/components/area-chart.d.mts +2 -0
- package/dist/components/area-chart.mjs +9 -3
- package/dist/components/bar-chart.d.mts +2 -0
- package/dist/components/bar-chart.mjs +9 -3
- package/dist/components/bar-list.d.mts +3 -0
- package/dist/components/bar-list.mjs +19 -7
- package/dist/components/chart-card.d.mts +1 -1
- package/dist/components/chart-card.mjs +1 -1
- package/dist/components/chart-container.d.mts +1 -1
- package/dist/components/chart-legend.d.mts +5 -0
- package/dist/components/chart-legend.mjs +11 -2
- package/dist/components/donut-chart.mjs +4 -0
- package/dist/components/heatmap-chart.d.mts +8 -0
- package/dist/components/heatmap-chart.mjs +39 -8
- package/dist/components/line-chart.d.mts +2 -0
- package/dist/components/line-chart.mjs +10 -3
- package/dist/components/stat.d.mts +3 -1
- package/dist/components/stat.mjs +14 -4
- package/dist/components/textarea.mjs +1 -1
- package/dist/lib/chart.mjs +16 -1
- package/package.json +23 -23
- package/src/components/area-chart.tsx +12 -4
- package/src/components/bar-chart.tsx +12 -4
- package/src/components/bar-list.tsx +21 -6
- package/src/components/chart-card.tsx +8 -6
- package/src/components/chart-container.tsx +2 -0
- package/src/components/chart-legend.tsx +10 -2
- package/src/components/donut-chart.tsx +1 -1
- package/src/components/heatmap-chart.tsx +62 -18
- package/src/components/line-chart.tsx +18 -5
- package/src/components/stat.tsx +10 -6
- package/src/components/textarea.tsx +1 -1
- package/src/lib/chart.ts +34 -0
- package/src/stories/area-chart.stories.tsx +1 -1
- package/src/stories/bar-chart.stories.tsx +1 -1
- package/src/stories/bar-list.stories.tsx +1 -1
- package/src/stories/donut-chart.stories.tsx +1 -1
- package/src/stories/heatmap-chart.stories.tsx +1 -1
- package/src/stories/line-chart.stories.tsx +1 -1
- package/src/stories/textarea.stories.tsx +7 -0
- package/src/stories/wizard.stories.tsx +1 -1
- package/src/styles/tokens.css +0 -45
|
@@ -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,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { cn } from "../lib/cn.mjs";
|
|
3
3
|
import { useReducedMotion } from "../hooks/use-reduced-motion.mjs";
|
|
4
|
-
import { orderByLuminance, resolveSeries } from "../lib/chart.mjs";
|
|
4
|
+
import { makeXAxisTick, orderByLuminance, resolveSeries } from "../lib/chart.mjs";
|
|
5
5
|
import { useChartContext } from "./chart-container.mjs";
|
|
6
6
|
import { ChartLegend } from "./chart-legend.mjs";
|
|
7
7
|
import { ChartTooltipContent } from "./chart-tooltip.mjs";
|
|
@@ -14,7 +14,7 @@ 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
20
|
const animated = !useReducedMotion();
|
|
@@ -128,6 +128,10 @@ function AreaChart({ data, index, series, variant = "stacked", curve = "monotone
|
|
|
128
128
|
}) : /* @__PURE__ */ jsx(ResponsiveContainer, {
|
|
129
129
|
width: "100%",
|
|
130
130
|
height: "100%",
|
|
131
|
+
initialDimension: {
|
|
132
|
+
width: 0,
|
|
133
|
+
height
|
|
134
|
+
},
|
|
131
135
|
children: /* @__PURE__ */ jsxs(AreaChart$1, {
|
|
132
136
|
data,
|
|
133
137
|
stackOffset: variant === "expand" ? "expand" : "none",
|
|
@@ -177,6 +181,7 @@ function AreaChart({ data, index, series, variant = "stacked", curve = "monotone
|
|
|
177
181
|
/* @__PURE__ */ jsx(XAxis, {
|
|
178
182
|
dataKey: index,
|
|
179
183
|
...axis,
|
|
184
|
+
tick: makeXAxisTick(theme),
|
|
180
185
|
interval: "preserveStartEnd",
|
|
181
186
|
minTickGap: 44
|
|
182
187
|
}),
|
|
@@ -261,7 +266,8 @@ function AreaChart({ data, index, series, variant = "stacked", curve = "monotone
|
|
|
261
266
|
})
|
|
262
267
|
}), legend && !isEmpty && /* @__PURE__ */ jsx(ChartLegend, {
|
|
263
268
|
items: legendItems,
|
|
264
|
-
|
|
269
|
+
align: legendAlign,
|
|
270
|
+
insetLeft: yAxisWidth
|
|
265
271
|
})]
|
|
266
272
|
});
|
|
267
273
|
}
|
|
@@ -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,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { cn } from "../lib/cn.mjs";
|
|
3
3
|
import { useReducedMotion } from "../hooks/use-reduced-motion.mjs";
|
|
4
|
-
import { orderByLuminance, resolveSeries } from "../lib/chart.mjs";
|
|
4
|
+
import { makeXAxisTick, orderByLuminance, resolveSeries } from "../lib/chart.mjs";
|
|
5
5
|
import { useChartContext } from "./chart-container.mjs";
|
|
6
6
|
import { ChartLegend } from "./chart-legend.mjs";
|
|
7
7
|
import { ChartTooltipContent } from "./chart-tooltip.mjs";
|
|
@@ -11,7 +11,7 @@ import { Bar, BarChart as BarChart$1, CartesianGrid, LabelList, ReferenceArea, R
|
|
|
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
17
|
const animated = !useReducedMotion();
|
|
@@ -111,6 +111,10 @@ function BarChart({ data, index, series, variant = "stacked", legend = false, va
|
|
|
111
111
|
}) : /* @__PURE__ */ jsx(ResponsiveContainer, {
|
|
112
112
|
width: "100%",
|
|
113
113
|
height: "100%",
|
|
114
|
+
initialDimension: {
|
|
115
|
+
width: 0,
|
|
116
|
+
height
|
|
117
|
+
},
|
|
114
118
|
children: /* @__PURE__ */ jsxs(BarChart$1, {
|
|
115
119
|
data,
|
|
116
120
|
stackOffset: variant === "expand" ? "expand" : "none",
|
|
@@ -161,6 +165,7 @@ function BarChart({ data, index, series, variant = "stacked", legend = false, va
|
|
|
161
165
|
/* @__PURE__ */ jsx(XAxis, {
|
|
162
166
|
dataKey: index,
|
|
163
167
|
...axis,
|
|
168
|
+
tick: makeXAxisTick(theme),
|
|
164
169
|
interval: "preserveStartEnd",
|
|
165
170
|
minTickGap: 44
|
|
166
171
|
}),
|
|
@@ -248,7 +253,8 @@ function BarChart({ data, index, series, variant = "stacked", legend = false, va
|
|
|
248
253
|
})
|
|
249
254
|
}), legend && !isEmpty && /* @__PURE__ */ jsx(ChartLegend, {
|
|
250
255
|
items: legendItems,
|
|
251
|
-
|
|
256
|
+
align: legendAlign,
|
|
257
|
+
insetLeft: yAxisWidth
|
|
252
258
|
})]
|
|
253
259
|
});
|
|
254
260
|
}
|
|
@@ -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,
|
|
@@ -7,11 +7,18 @@ import { useChartContext } from "./chart-container.mjs";
|
|
|
7
7
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
8
8
|
import * as React$1 from "react";
|
|
9
9
|
//#region src/components/bar-list.tsx
|
|
10
|
+
const SEMANTIC_KEY = {
|
|
11
|
+
error: "destructive",
|
|
12
|
+
warning: "warning",
|
|
13
|
+
success: "success"
|
|
14
|
+
};
|
|
10
15
|
const PLACEHOLDER_HEIGHT = 168;
|
|
11
16
|
const RAMP_CEILING = .8;
|
|
17
|
+
const SEMANTIC_FLOOR = .62;
|
|
12
18
|
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);
|
|
19
|
+
function BarList({ data, index, dataKey = "value", maxItems, palette, semantic, loading = false, valueFormatter = (value) => value.toLocaleString("en-US"), labelFormatter, className }) {
|
|
20
|
+
const { paletteName, theme } = useChartContext(palette);
|
|
21
|
+
const accent = semantic ? theme[SEMANTIC_KEY[semantic]] : null;
|
|
15
22
|
const reducedMotion = useReducedMotion();
|
|
16
23
|
const [mounted, setMounted] = React$1.useState(false);
|
|
17
24
|
const [active, setActive] = React$1.useState(null);
|
|
@@ -26,16 +33,21 @@ function BarList({ data, index, dataKey = "value", maxItems, palette, loading =
|
|
|
26
33
|
}));
|
|
27
34
|
mapped.sort((lower, upper) => upper.value - lower.value);
|
|
28
35
|
const capped = maxItems ? mapped.slice(0, maxItems) : mapped;
|
|
29
|
-
return capped.map((row, rank) =>
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
36
|
+
return capped.map((row, rank) => {
|
|
37
|
+
const rankFraction = capped.length > 1 ? rank / (capped.length - 1) : 0;
|
|
38
|
+
const accentWeight = Math.round((1 - rankFraction * (1 - SEMANTIC_FLOOR)) * 100);
|
|
39
|
+
return {
|
|
40
|
+
...row,
|
|
41
|
+
color: accent ? `color-mix(in oklab, ${accent} ${accentWeight}%, white)` : rampColor(paletteName, rankFraction * RAMP_CEILING)
|
|
42
|
+
};
|
|
43
|
+
});
|
|
33
44
|
}, [
|
|
34
45
|
data,
|
|
35
46
|
index,
|
|
36
47
|
dataKey,
|
|
37
48
|
maxItems,
|
|
38
|
-
paletteName
|
|
49
|
+
paletteName,
|
|
50
|
+
accent
|
|
39
51
|
]);
|
|
40
52
|
const maxValue = rows.reduce((max, row) => row.value > max ? row.value : max, 0);
|
|
41
53
|
const isEmpty = rows.length === 0;
|
|
@@ -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]",
|
|
@@ -73,6 +73,10 @@ function DonutChart({ data, index, dataKey = "value", variant = "donut", legend
|
|
|
73
73
|
children: [/* @__PURE__ */ jsx(ResponsiveContainer, {
|
|
74
74
|
width: "100%",
|
|
75
75
|
height: "100%",
|
|
76
|
+
initialDimension: {
|
|
77
|
+
width: 0,
|
|
78
|
+
height
|
|
79
|
+
},
|
|
76
80
|
children: /* @__PURE__ */ jsxs(PieChart, { children: [/* @__PURE__ */ jsx("defs", { children: slices.map((slice, slot) => /* @__PURE__ */ jsxs("linearGradient", {
|
|
77
81
|
id: `donut-${reactId}-${slot}`,
|
|
78
82
|
x1: "0",
|
|
@@ -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
|
-
|
|
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
|
|
57
|
+
const key = pairKey(rowLabel, colLabel);
|
|
58
|
+
const value = valueAt.get(key) ?? 0;
|
|
53
59
|
if (value > maxValue) {
|
|
54
60
|
maxValue = value;
|
|
55
|
-
peakKey =
|
|
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__ */
|
|
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,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { cn } from "../lib/cn.mjs";
|
|
3
3
|
import { useReducedMotion } from "../hooks/use-reduced-motion.mjs";
|
|
4
|
-
import { resolveSeries } from "../lib/chart.mjs";
|
|
4
|
+
import { makeXAxisTick, resolveSeries } from "../lib/chart.mjs";
|
|
5
5
|
import { useChartContext } from "./chart-container.mjs";
|
|
6
6
|
import { ChartLegend } from "./chart-legend.mjs";
|
|
7
7
|
import { ChartTooltipContent } from "./chart-tooltip.mjs";
|
|
@@ -14,7 +14,7 @@ const CURVE_TYPE = {
|
|
|
14
14
|
linear: "linear",
|
|
15
15
|
step: "stepAfter"
|
|
16
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 }) {
|
|
17
|
+
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
18
|
const { palette: paletteColors, theme } = useChartContext(palette);
|
|
19
19
|
const animated = !useReducedMotion();
|
|
20
20
|
const resolved = resolveSeries(series, paletteColors, theme);
|
|
@@ -106,6 +106,10 @@ function LineChart({ data, index, series, curve = "monotone", legend = false, va
|
|
|
106
106
|
}) : /* @__PURE__ */ jsx(ResponsiveContainer, {
|
|
107
107
|
width: "100%",
|
|
108
108
|
height: "100%",
|
|
109
|
+
initialDimension: {
|
|
110
|
+
width: 0,
|
|
111
|
+
height
|
|
112
|
+
},
|
|
109
113
|
children: /* @__PURE__ */ jsxs(LineChart$1, {
|
|
110
114
|
data,
|
|
111
115
|
margin,
|
|
@@ -118,12 +122,14 @@ function LineChart({ data, index, series, curve = "monotone", legend = false, va
|
|
|
118
122
|
/* @__PURE__ */ jsx(XAxis, {
|
|
119
123
|
dataKey: index,
|
|
120
124
|
...axis,
|
|
125
|
+
tick: makeXAxisTick(theme),
|
|
121
126
|
interval: "preserveStartEnd",
|
|
122
127
|
minTickGap: 44
|
|
123
128
|
}),
|
|
124
129
|
/* @__PURE__ */ jsx(YAxis, {
|
|
125
130
|
...axis,
|
|
126
131
|
width: yAxisWidth,
|
|
132
|
+
domain: ["auto", "auto"],
|
|
127
133
|
tickFormatter: (value) => valueFormatter(value)
|
|
128
134
|
}),
|
|
129
135
|
/* @__PURE__ */ jsx(Tooltip, {
|
|
@@ -203,7 +209,8 @@ function LineChart({ data, index, series, curve = "monotone", legend = false, va
|
|
|
203
209
|
})
|
|
204
210
|
}), legend && !isEmpty && /* @__PURE__ */ jsx(ChartLegend, {
|
|
205
211
|
items: legendItems,
|
|
206
|
-
|
|
212
|
+
align: legendAlign,
|
|
213
|
+
insetLeft: yAxisWidth
|
|
207
214
|
})]
|
|
208
215
|
});
|
|
209
216
|
}
|
|
@@ -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;
|
package/dist/components/stat.mjs
CHANGED
|
@@ -21,11 +21,17 @@ const toSparkData = (sparkline) => (sparkline ?? []).map((point, index) => typeo
|
|
|
21
21
|
index,
|
|
22
22
|
value: point.value
|
|
23
23
|
});
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
const SEMANTIC_KEY = {
|
|
25
|
+
error: "destructive",
|
|
26
|
+
warning: "warning",
|
|
27
|
+
success: "success"
|
|
28
|
+
};
|
|
29
|
+
function Stat({ value, unit, delta, sparkline, semantic, className, ...props }) {
|
|
30
|
+
const { palette, theme } = useChartContext();
|
|
26
31
|
const gradientId = React$1.useId().replace(/:/g, "");
|
|
27
32
|
const sparkData = React$1.useMemo(() => toSparkData(sparkline), [sparkline]);
|
|
28
|
-
const
|
|
33
|
+
const hasSpark = sparkData.some((point) => point.value > 0);
|
|
34
|
+
const sparkColor = semantic ? theme[SEMANTIC_KEY[semantic]] : palette[0];
|
|
29
35
|
const sentiment = delta && (delta.invert ? delta.direction === "down" : delta.direction === "up") ? "positive" : "negative";
|
|
30
36
|
return /* @__PURE__ */ jsxs("div", {
|
|
31
37
|
"data-slot": "stat",
|
|
@@ -48,11 +54,15 @@ function Stat({ value, unit, delta, sparkline, className, ...props }) {
|
|
|
48
54
|
children: [delta.direction === "up" ? /* @__PURE__ */ jsx(ArrowUp, { className: "size-3" }) : /* @__PURE__ */ jsx(ArrowDown, { className: "size-3" }), delta.label ?? `${delta.value}%`]
|
|
49
55
|
})
|
|
50
56
|
]
|
|
51
|
-
}),
|
|
57
|
+
}), hasSpark && /* @__PURE__ */ jsx("div", {
|
|
52
58
|
className: "h-9 w-full",
|
|
53
59
|
children: /* @__PURE__ */ jsx(ResponsiveContainer, {
|
|
54
60
|
width: "100%",
|
|
55
61
|
height: "100%",
|
|
62
|
+
initialDimension: {
|
|
63
|
+
width: 0,
|
|
64
|
+
height: 36
|
|
65
|
+
},
|
|
56
66
|
children: /* @__PURE__ */ jsxs(AreaChart, {
|
|
57
67
|
data: sparkData,
|
|
58
68
|
margin: {
|
|
@@ -12,7 +12,7 @@ function Textarea({ className, id, label, required, hint, error, tooltip, ...pro
|
|
|
12
12
|
const textarea = /* @__PURE__ */ jsx("textarea", {
|
|
13
13
|
id: fieldId,
|
|
14
14
|
"data-slot": "textarea",
|
|
15
|
-
className: cn("block w-full min-h-[120px] resize-
|
|
15
|
+
className: cn("block w-full min-h-[120px] max-h-[480px] resize-y [field-sizing:content]", "px-3.5 py-3", "type-text-md text-foreground placeholder:text-placeholder", "bg-background border border-border rounded-md", "transition-colors", "outline-none focus-visible:border-ring focus-visible:border-2", "disabled:bg-disabled disabled:text-disabled-foreground disabled:cursor-not-allowed", "aria-invalid:border-border-error [@media(hover:hover)]:aria-invalid:hover:border-border-error", error && "border-border-error [@media(hover:hover)]:hover:border-border-error", className),
|
|
16
16
|
required,
|
|
17
17
|
"aria-invalid": error ? true : void 0,
|
|
18
18
|
"aria-describedby": fieldId && (hint || error) ? `${fieldId}-description` : void 0,
|
package/dist/lib/chart.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { luminance, paletteColor } from "./chart-palette.mjs";
|
|
2
|
+
import * as React$1 from "react";
|
|
2
3
|
//#region src/lib/chart.ts
|
|
3
4
|
const formatShare = (fraction) => `${(fraction * 100).toFixed(fraction >= .1 ? 0 : 1)}%`;
|
|
4
5
|
const semanticColor = (theme, semantic) => {
|
|
@@ -23,5 +24,19 @@ const resolveSeries = (series, palette, theme) => series.map((entry, index) => (
|
|
|
23
24
|
* stack order in Recharts — first entry sits at the bottom.
|
|
24
25
|
*/
|
|
25
26
|
const orderByLuminance = (series) => [...series].sort((lower, upper) => luminance(lower.color) - luminance(upper.color));
|
|
27
|
+
const makeXAxisTick = (theme) => ({ x, y, payload, index, visibleTicksCount }) => {
|
|
28
|
+
const isFirst = index === 0;
|
|
29
|
+
const isLast = visibleTicksCount != null && index === visibleTicksCount - 1;
|
|
30
|
+
const anchor = isFirst ? "start" : isLast ? "end" : "middle";
|
|
31
|
+
return React$1.createElement("text", {
|
|
32
|
+
x: Number(x ?? 0),
|
|
33
|
+
y: Number(y ?? 0),
|
|
34
|
+
dy: 12,
|
|
35
|
+
textAnchor: anchor,
|
|
36
|
+
fill: theme.axisForeground,
|
|
37
|
+
fontFamily: theme.fontMono,
|
|
38
|
+
fontSize: 10
|
|
39
|
+
}, String(payload?.value ?? ""));
|
|
40
|
+
};
|
|
26
41
|
//#endregion
|
|
27
|
-
export { formatShare, orderByLuminance, resolveSeries };
|
|
42
|
+
export { formatShare, makeXAxisTick, orderByLuminance, resolveSeries };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alpic-ai/ui",
|
|
3
|
-
"version": "0.0.0-dev.
|
|
3
|
+
"version": "0.0.0-dev.g1a5d5ed",
|
|
4
4
|
"description": "Alpic design system — shared UI components",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -23,32 +23,32 @@
|
|
|
23
23
|
"src"
|
|
24
24
|
],
|
|
25
25
|
"peerDependencies": {
|
|
26
|
-
"lucide-react": "^1.
|
|
26
|
+
"lucide-react": "^1.21.0",
|
|
27
27
|
"react": "^19.2.7",
|
|
28
28
|
"react-dom": "^19.2.7",
|
|
29
|
-
"react-hook-form": "^7.
|
|
29
|
+
"react-hook-form": "^7.80.0",
|
|
30
30
|
"sonner": "^2.0.7",
|
|
31
31
|
"tailwindcss": "^4.3.1",
|
|
32
32
|
"tw-animate-css": "^1.4.0"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@radix-ui/react-accordion": "^1.2.
|
|
36
|
-
"@radix-ui/react-avatar": "^1.
|
|
37
|
-
"@radix-ui/react-checkbox": "^1.3.
|
|
38
|
-
"@radix-ui/react-collapsible": "^1.1.
|
|
39
|
-
"@radix-ui/react-dialog": "^1.1.
|
|
40
|
-
"@radix-ui/react-dropdown-menu": "^2.1.
|
|
41
|
-
"@radix-ui/react-label": "^2.1.
|
|
42
|
-
"@radix-ui/react-popover": "^1.1.
|
|
43
|
-
"@radix-ui/react-radio-group": "^1.4.
|
|
44
|
-
"@radix-ui/react-scroll-area": "^1.2.
|
|
45
|
-
"@radix-ui/react-select": "^2.3.
|
|
46
|
-
"@radix-ui/react-separator": "^1.1.
|
|
47
|
-
"@radix-ui/react-slot": "^1.
|
|
48
|
-
"@radix-ui/react-switch": "^1.3.
|
|
49
|
-
"@radix-ui/react-tabs": "^1.1.
|
|
50
|
-
"@radix-ui/react-toggle-group": "^1.1.
|
|
51
|
-
"@radix-ui/react-tooltip": "^1.2.
|
|
35
|
+
"@radix-ui/react-accordion": "^1.2.14",
|
|
36
|
+
"@radix-ui/react-avatar": "^1.2.0",
|
|
37
|
+
"@radix-ui/react-checkbox": "^1.3.5",
|
|
38
|
+
"@radix-ui/react-collapsible": "^1.1.14",
|
|
39
|
+
"@radix-ui/react-dialog": "^1.1.17",
|
|
40
|
+
"@radix-ui/react-dropdown-menu": "^2.1.18",
|
|
41
|
+
"@radix-ui/react-label": "^2.1.10",
|
|
42
|
+
"@radix-ui/react-popover": "^1.1.17",
|
|
43
|
+
"@radix-ui/react-radio-group": "^1.4.1",
|
|
44
|
+
"@radix-ui/react-scroll-area": "^1.2.12",
|
|
45
|
+
"@radix-ui/react-select": "^2.3.1",
|
|
46
|
+
"@radix-ui/react-separator": "^1.1.10",
|
|
47
|
+
"@radix-ui/react-slot": "^1.3.0",
|
|
48
|
+
"@radix-ui/react-switch": "^1.3.1",
|
|
49
|
+
"@radix-ui/react-tabs": "^1.1.15",
|
|
50
|
+
"@radix-ui/react-toggle-group": "^1.1.13",
|
|
51
|
+
"@radix-ui/react-tooltip": "^1.2.10",
|
|
52
52
|
"class-variance-authority": "^0.7.1",
|
|
53
53
|
"clsx": "^2.1.1",
|
|
54
54
|
"cmdk": "^1.1.1",
|
|
@@ -60,12 +60,12 @@
|
|
|
60
60
|
"@tailwindcss/postcss": "^4.3.1",
|
|
61
61
|
"@types/react": "19.2.17",
|
|
62
62
|
"@types/react-dom": "19.2.3",
|
|
63
|
-
"lucide-react": "^1.
|
|
64
|
-
"react-hook-form": "^7.
|
|
63
|
+
"lucide-react": "^1.21.0",
|
|
64
|
+
"react-hook-form": "^7.80.0",
|
|
65
65
|
"shx": "^0.4.0",
|
|
66
66
|
"sonner": "^2.0.7",
|
|
67
67
|
"tailwindcss": "^4.3.1",
|
|
68
|
-
"tsdown": "^0.22.
|
|
68
|
+
"tsdown": "^0.22.3",
|
|
69
69
|
"tw-animate-css": "^1.4.0",
|
|
70
70
|
"typescript": "^6.0.3"
|
|
71
71
|
},
|