@alpic-ai/ui 0.0.0-staging.g59da2b5 → 0.0.0-staging.g5be6afb
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 +14 -12
- package/dist/components/bar-chart.d.mts +4 -0
- package/dist/components/bar-chart.mjs +53 -21
- package/dist/components/bar-list.d.mts +3 -0
- package/dist/components/bar-list.mjs +21 -16
- 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/chart-tooltip.mjs +54 -27
- package/dist/components/donut-chart.mjs +5 -3
- package/dist/components/form.mjs +3 -1
- package/dist/components/heatmap-chart.d.mts +14 -0
- package/dist/components/heatmap-chart.mjs +158 -97
- package/dist/components/line-chart.d.mts +2 -0
- package/dist/components/line-chart.mjs +15 -12
- package/dist/components/sidebar.mjs +1 -1
- package/dist/components/skeleton.mjs +1 -1
- package/dist/components/stat.d.mts +3 -1
- package/dist/components/stat.mjs +18 -8
- package/dist/components/textarea.mjs +1 -1
- package/dist/lib/chart-palette.mjs +20 -17
- package/dist/lib/chart.mjs +16 -1
- package/package.json +27 -27
- package/src/components/area-chart.tsx +23 -13
- package/src/components/bar-chart.tsx +60 -14
- package/src/components/bar-list.tsx +22 -15
- 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/chart-tooltip.tsx +22 -6
- package/src/components/donut-chart.tsx +2 -5
- package/src/components/form.tsx +4 -2
- package/src/components/heatmap-chart.tsx +109 -21
- package/src/components/line-chart.tsx +29 -14
- package/src/components/skeleton.tsx +1 -1
- package/src/components/stat.tsx +14 -10
- package/src/components/textarea.tsx +1 -1
- package/src/lib/chart-palette.ts +20 -17
- 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
- package/dist/hooks/use-reduced-motion.d.mts +0 -4
- package/dist/hooks/use-reduced-motion.mjs +0 -16
- 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 {
|
|
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__ */
|
|
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:
|
|
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:
|
|
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
|
-
|
|
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;
|
|
@@ -21,6 +22,7 @@ interface BarChartProps {
|
|
|
21
22
|
};
|
|
22
23
|
markers?: ChartMarker[];
|
|
23
24
|
texture?: boolean;
|
|
25
|
+
partialLastBar?: boolean;
|
|
24
26
|
loading?: boolean;
|
|
25
27
|
valueFormatter?: (value: number) => string;
|
|
26
28
|
labelFormatter?: (label: string | number) => string;
|
|
@@ -32,6 +34,7 @@ declare function BarChart({
|
|
|
32
34
|
series,
|
|
33
35
|
variant,
|
|
34
36
|
legend,
|
|
37
|
+
legendAlign,
|
|
35
38
|
valueLabels,
|
|
36
39
|
height,
|
|
37
40
|
yAxisWidth,
|
|
@@ -39,6 +42,7 @@ declare function BarChart({
|
|
|
39
42
|
referenceLine,
|
|
40
43
|
markers,
|
|
41
44
|
texture,
|
|
45
|
+
partialLastBar,
|
|
42
46
|
loading,
|
|
43
47
|
valueFormatter,
|
|
44
48
|
labelFormatter,
|
|
@@ -1,20 +1,19 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { cn } from "../lib/cn.mjs";
|
|
3
|
-
import {
|
|
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
|
-
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, 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, va
|
|
|
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,19 +111,20 @@ function BarChart({ data, index, series, variant = "stacked", legend = false, va
|
|
|
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__ */
|
|
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, {
|
|
112
122
|
width: "100%",
|
|
113
123
|
height: "100%",
|
|
124
|
+
initialDimension: {
|
|
125
|
+
width: 0,
|
|
126
|
+
height
|
|
127
|
+
},
|
|
114
128
|
children: /* @__PURE__ */ jsxs(BarChart$1, {
|
|
115
129
|
data,
|
|
116
130
|
stackOffset: variant === "expand" ? "expand" : "none",
|
|
@@ -161,8 +175,9 @@ function BarChart({ data, index, series, variant = "stacked", legend = false, va
|
|
|
161
175
|
/* @__PURE__ */ jsx(XAxis, {
|
|
162
176
|
dataKey: index,
|
|
163
177
|
...axis,
|
|
178
|
+
tick: makeXAxisTick(theme),
|
|
164
179
|
interval: "preserveStartEnd",
|
|
165
|
-
minTickGap:
|
|
180
|
+
minTickGap: 56
|
|
166
181
|
}),
|
|
167
182
|
/* @__PURE__ */ jsx(YAxis, {
|
|
168
183
|
...axis,
|
|
@@ -206,7 +221,7 @@ function BarChart({ data, index, series, variant = "stacked", legend = false, va
|
|
|
206
221
|
position: "insideBottomRight"
|
|
207
222
|
} : void 0
|
|
208
223
|
}),
|
|
209
|
-
rendered.map((entry, slot) => /* @__PURE__ */
|
|
224
|
+
rendered.map((entry, slot) => /* @__PURE__ */ jsxs(Bar, {
|
|
210
225
|
dataKey: entry.key,
|
|
211
226
|
name: entry.name,
|
|
212
227
|
stackId: stacked ? "stack" : void 0,
|
|
@@ -215,18 +230,34 @@ function BarChart({ data, index, series, variant = "stacked", legend = false, va
|
|
|
215
230
|
strokeWidth: 0,
|
|
216
231
|
radius: radiusFor(slot),
|
|
217
232
|
maxBarSize: MAX_BAR_SIZE,
|
|
218
|
-
activeBar:
|
|
219
|
-
|
|
220
|
-
stroke: theme.card,
|
|
221
|
-
strokeWidth: 1
|
|
222
|
-
},
|
|
223
|
-
isAnimationActive: animated,
|
|
233
|
+
activeBar: false,
|
|
234
|
+
isAnimationActive: false,
|
|
224
235
|
animationDuration: 650,
|
|
225
236
|
animationEasing: "ease-out",
|
|
226
|
-
children:
|
|
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, {
|
|
227
258
|
dataKey: entry.key,
|
|
228
259
|
content: renderValueLabel(entry.color)
|
|
229
|
-
})
|
|
260
|
+
})]
|
|
230
261
|
}, entry.key)),
|
|
231
262
|
markers?.map((marker) => /* @__PURE__ */ jsx(ReferenceDot, {
|
|
232
263
|
x: marker.x,
|
|
@@ -248,7 +279,8 @@ function BarChart({ data, index, series, variant = "stacked", legend = false, va
|
|
|
248
279
|
})
|
|
249
280
|
}), legend && !isEmpty && /* @__PURE__ */ jsx(ChartLegend, {
|
|
250
281
|
items: legendItems,
|
|
251
|
-
|
|
282
|
+
align: legendAlign,
|
|
283
|
+
insetLeft: yAxisWidth
|
|
252
284
|
})]
|
|
253
285
|
});
|
|
254
286
|
}
|
|
@@ -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
|
|
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
|
-
|
|
31
|
-
|
|
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 =
|
|
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
|
|
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: [
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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-
|
|
28
|
-
children: [
|
|
29
|
-
"
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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(
|
|
71
|
+
children: valueFormatter(total)
|
|
36
72
|
})]
|
|
37
|
-
}
|
|
38
|
-
|
|
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:
|
|
105
|
+
isAnimationActive: false,
|
|
104
106
|
animationDuration: 650,
|
|
105
107
|
animationEasing: "ease-out",
|
|
106
108
|
onMouseEnter: (_entry, sliceIndex) => setActive(sliceIndex),
|
package/dist/components/form.mjs
CHANGED
|
@@ -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;
|