@techsio/ui-kit 0.38.0 → 0.39.0
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/molecules/chart.js +393 -0
- package/dist/src/molecules/chart.d.ts +64 -0
- package/dist/src/molecules/chart.d.ts.map +1 -0
- package/dist/stories/changelog/changelog.stories.d.ts.map +1 -1
- package/dist/stories/molecules/chart.stories.d.ts +24 -0
- package/dist/stories/molecules/chart.stories.d.ts.map +1 -0
- package/package.json +8 -1
- package/src/tokens/components/components.css +1 -0
- package/src/tokens/components/molecules/_chart.css +98 -0
|
@@ -0,0 +1,393 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import { areaY, barX, barY, colorLegend, d3Curve, defineChart, dot, group, lineY, stack } from "@tanstack/charts";
|
|
3
|
+
import { polar, radialArc } from "@tanstack/charts/polar";
|
|
4
|
+
import { tooltip as tooltip_tooltip } from "@tanstack/charts/tooltip";
|
|
5
|
+
import { scaleBand } from "@tanstack/charts-scales/band";
|
|
6
|
+
import { scaleLinear } from "@tanstack/charts-scales/linear";
|
|
7
|
+
import { scalePoint } from "@tanstack/charts-scales/point";
|
|
8
|
+
import { Chart } from "@tanstack/react-charts";
|
|
9
|
+
import { scaleUtc } from "d3-scale";
|
|
10
|
+
import { curveMonotoneX, pie } from "d3-shape";
|
|
11
|
+
import { useMemo } from "react";
|
|
12
|
+
import { tv } from "../utils.js";
|
|
13
|
+
const chartVariants = tv({
|
|
14
|
+
slots: {
|
|
15
|
+
root: "chart-base block w-full text-chart-fg",
|
|
16
|
+
tooltip: "chart-tooltip"
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
const SERIES_1_PAINT = "var(--color-chart-series-1)";
|
|
20
|
+
const SERIES_STROKE_FROM_COLOR = null;
|
|
21
|
+
const DONUT_INNER_RADIUS_RATIO = 0.6;
|
|
22
|
+
const POLAR_INSET = 8;
|
|
23
|
+
function toAccessor(accessor) {
|
|
24
|
+
if ("function" == typeof accessor) return accessor;
|
|
25
|
+
return (datum)=>datum[accessor];
|
|
26
|
+
}
|
|
27
|
+
const defineChartSpec = defineChart;
|
|
28
|
+
const MAX_SERIES = 6;
|
|
29
|
+
function resolveXAxis(data, getX, xLabel) {
|
|
30
|
+
const values = data.map(getX);
|
|
31
|
+
if (values.length > 0 && values.every((value)=>value instanceof Date)) return {
|
|
32
|
+
scale: ()=>scaleUtc(),
|
|
33
|
+
nice: true,
|
|
34
|
+
axis: {
|
|
35
|
+
label: xLabel
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
if (values.length > 0 && values.every((value)=>"number" == typeof value)) return {
|
|
39
|
+
scale: scaleLinear,
|
|
40
|
+
nice: true,
|
|
41
|
+
axis: {
|
|
42
|
+
label: xLabel
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
return {
|
|
46
|
+
scale: ()=>scalePoint().padding(0.1),
|
|
47
|
+
axis: {
|
|
48
|
+
label: xLabel
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
function resolveBuild(config) {
|
|
53
|
+
const getX = toAccessor(config.x);
|
|
54
|
+
const getY = toAccessor(config.y);
|
|
55
|
+
const getSeries = null == config.series ? void 0 : toAccessor(config.series);
|
|
56
|
+
const isPolar = "pie" === config.type || "donut" === config.type;
|
|
57
|
+
const showLegend = config.legend ?? (null != config.series || isPolar);
|
|
58
|
+
const { formatValue } = config;
|
|
59
|
+
const valueTicks = formatValue ? {
|
|
60
|
+
format: formatValue
|
|
61
|
+
} : void 0;
|
|
62
|
+
warnOnSeriesOverflow(config.data, isPolar ? getX : getSeries);
|
|
63
|
+
const tooltipBase = {
|
|
64
|
+
use: tooltip_tooltip,
|
|
65
|
+
className: config.tooltipClassName
|
|
66
|
+
};
|
|
67
|
+
const channelName = (accessor, label)=>label ?? ("string" == typeof accessor ? accessor : void 0);
|
|
68
|
+
const xName = channelName(config.x, config.xLabel);
|
|
69
|
+
const yName = channelName(config.y, config.yLabel);
|
|
70
|
+
const formatChannel = (point, channel)=>{
|
|
71
|
+
const value = "x" === channel ? point.xValue : point.yValue;
|
|
72
|
+
if ("number" == typeof value && formatValue) return formatValue(value);
|
|
73
|
+
return String(value ?? "");
|
|
74
|
+
};
|
|
75
|
+
const tooltipFor = (valueChannel, swapped = false)=>{
|
|
76
|
+
if (!config.showTooltip) return false;
|
|
77
|
+
const dataValueChannel = swapped ? "y" : valueChannel;
|
|
78
|
+
const valueName = "x" === dataValueChannel ? xName : yName;
|
|
79
|
+
const categoryName = "x" === dataValueChannel ? yName : xName;
|
|
80
|
+
const valueItem = {
|
|
81
|
+
channel: valueChannel,
|
|
82
|
+
label: valueName,
|
|
83
|
+
text: formatValue ? (point)=>formatChannel(point, valueChannel) : void 0
|
|
84
|
+
};
|
|
85
|
+
const categoryChannel = "x" === valueChannel ? "y" : "x";
|
|
86
|
+
const categoryItem = {
|
|
87
|
+
channel: categoryChannel,
|
|
88
|
+
label: categoryName
|
|
89
|
+
};
|
|
90
|
+
return {
|
|
91
|
+
...tooltipBase,
|
|
92
|
+
items: "x" === valueChannel ? [
|
|
93
|
+
valueItem,
|
|
94
|
+
categoryItem
|
|
95
|
+
] : [
|
|
96
|
+
categoryItem,
|
|
97
|
+
valueItem
|
|
98
|
+
]
|
|
99
|
+
};
|
|
100
|
+
};
|
|
101
|
+
const polarTooltip = ()=>{
|
|
102
|
+
if (!config.showTooltip) return false;
|
|
103
|
+
return {
|
|
104
|
+
...tooltipBase,
|
|
105
|
+
items: [
|
|
106
|
+
{
|
|
107
|
+
id: "value",
|
|
108
|
+
label: yName,
|
|
109
|
+
text: (point)=>{
|
|
110
|
+
const row = point.datum.data;
|
|
111
|
+
const value = getY(row);
|
|
112
|
+
return formatValue ? formatValue(value) : String(value);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
]
|
|
116
|
+
};
|
|
117
|
+
};
|
|
118
|
+
const valueAxis = {
|
|
119
|
+
scale: scaleLinear,
|
|
120
|
+
nice: true,
|
|
121
|
+
grid: config.grid,
|
|
122
|
+
axis: {
|
|
123
|
+
label: config.yLabel,
|
|
124
|
+
ticks: valueTicks
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
const xAxis = resolveXAxis(config.data, getX, config.xLabel);
|
|
128
|
+
return {
|
|
129
|
+
data: config.data,
|
|
130
|
+
getX,
|
|
131
|
+
getY,
|
|
132
|
+
getSeries,
|
|
133
|
+
stacked: config.stacked,
|
|
134
|
+
points: config.points,
|
|
135
|
+
curve: config.smooth ? d3Curve(curveMonotoneX) : void 0,
|
|
136
|
+
grid: config.grid,
|
|
137
|
+
xLabel: config.xLabel,
|
|
138
|
+
yLabel: config.yLabel,
|
|
139
|
+
valueTicks,
|
|
140
|
+
valueAxis,
|
|
141
|
+
xAxis,
|
|
142
|
+
bandAxis: {
|
|
143
|
+
scale: ()=>scaleBand().padding(0.3),
|
|
144
|
+
axis: {
|
|
145
|
+
label: config.xLabel
|
|
146
|
+
}
|
|
147
|
+
},
|
|
148
|
+
tooltipFor,
|
|
149
|
+
polarTooltip,
|
|
150
|
+
shared: {
|
|
151
|
+
color: showLegend ? {
|
|
152
|
+
legend: colorLegend({
|
|
153
|
+
label: config.legendLabel
|
|
154
|
+
})
|
|
155
|
+
} : void 0,
|
|
156
|
+
animate: config.animate
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
function warnOnSeriesOverflow(data, getColorIdentity) {
|
|
161
|
+
const isProduction = "undefined" != typeof process && process.env?.NODE_ENV === "production";
|
|
162
|
+
if (null == getColorIdentity || isProduction) return;
|
|
163
|
+
const distinct = new Set(data.map((datum)=>String(getColorIdentity(datum))));
|
|
164
|
+
if (distinct.size > MAX_SERIES) console.warn(`Chart: ${distinct.size} distinct colors exceeds the ${MAX_SERIES}-color palette; everything beyond the sixth reuses an earlier color and cannot be told apart. Group the long tail into an “Other” bucket, or split into small multiples.`);
|
|
165
|
+
}
|
|
166
|
+
function buildLine(build) {
|
|
167
|
+
const { data, getX, getY, getSeries } = build;
|
|
168
|
+
return defineChartSpec({
|
|
169
|
+
marks: [
|
|
170
|
+
lineY(data, {
|
|
171
|
+
x: getX,
|
|
172
|
+
y: getY,
|
|
173
|
+
z: getSeries,
|
|
174
|
+
color: getSeries,
|
|
175
|
+
points: build.points,
|
|
176
|
+
curve: build.curve,
|
|
177
|
+
stroke: null == getSeries ? SERIES_1_PAINT : void 0
|
|
178
|
+
})
|
|
179
|
+
],
|
|
180
|
+
x: build.xAxis,
|
|
181
|
+
y: build.valueAxis,
|
|
182
|
+
...build.shared,
|
|
183
|
+
tooltip: build.tooltipFor("y")
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
function buildArea(build) {
|
|
187
|
+
const { data, getX, getY, getSeries } = build;
|
|
188
|
+
const isStacked = build.stacked ?? true;
|
|
189
|
+
return defineChartSpec({
|
|
190
|
+
marks: [
|
|
191
|
+
areaY(data, {
|
|
192
|
+
x: getX,
|
|
193
|
+
...null != getSeries && isStacked ? {
|
|
194
|
+
y: getY
|
|
195
|
+
} : {
|
|
196
|
+
y1: 0,
|
|
197
|
+
y2: getY
|
|
198
|
+
},
|
|
199
|
+
z: getSeries,
|
|
200
|
+
color: getSeries,
|
|
201
|
+
curve: build.curve,
|
|
202
|
+
fill: null == getSeries ? SERIES_1_PAINT : void 0,
|
|
203
|
+
fillOpacity: null != getSeries && isStacked ? 0.85 : 0.25,
|
|
204
|
+
stroke: null == getSeries ? SERIES_1_PAINT : SERIES_STROKE_FROM_COLOR,
|
|
205
|
+
strokeWidth: 2
|
|
206
|
+
})
|
|
207
|
+
],
|
|
208
|
+
x: build.xAxis,
|
|
209
|
+
y: build.valueAxis,
|
|
210
|
+
...build.shared,
|
|
211
|
+
tooltip: build.tooltipFor("y")
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
function barLayout(hasSeries, isStacked) {
|
|
215
|
+
if (!hasSeries) return;
|
|
216
|
+
return isStacked ? stack() : group({
|
|
217
|
+
padding: 0.15
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
function buildBars(build, horizontal) {
|
|
221
|
+
const { data, getX, getY, getSeries } = build;
|
|
222
|
+
const isStacked = build.stacked ?? false;
|
|
223
|
+
const barOptions = {
|
|
224
|
+
z: getSeries,
|
|
225
|
+
color: getSeries,
|
|
226
|
+
fill: null == getSeries ? SERIES_1_PAINT : void 0,
|
|
227
|
+
layout: barLayout(null != getSeries, isStacked),
|
|
228
|
+
radius: null != getSeries && isStacked ? 0 : 4
|
|
229
|
+
};
|
|
230
|
+
if (horizontal) return defineChartSpec({
|
|
231
|
+
marks: [
|
|
232
|
+
barX(data, {
|
|
233
|
+
x: getY,
|
|
234
|
+
y: getX,
|
|
235
|
+
...barOptions
|
|
236
|
+
})
|
|
237
|
+
],
|
|
238
|
+
x: {
|
|
239
|
+
scale: scaleLinear,
|
|
240
|
+
nice: true,
|
|
241
|
+
grid: build.grid,
|
|
242
|
+
axis: {
|
|
243
|
+
label: build.yLabel,
|
|
244
|
+
ticks: build.valueTicks
|
|
245
|
+
}
|
|
246
|
+
},
|
|
247
|
+
y: build.bandAxis,
|
|
248
|
+
...build.shared,
|
|
249
|
+
tooltip: build.tooltipFor("x", true)
|
|
250
|
+
});
|
|
251
|
+
return defineChartSpec({
|
|
252
|
+
marks: [
|
|
253
|
+
barY(data, {
|
|
254
|
+
x: getX,
|
|
255
|
+
y: getY,
|
|
256
|
+
...barOptions
|
|
257
|
+
})
|
|
258
|
+
],
|
|
259
|
+
x: build.bandAxis,
|
|
260
|
+
y: build.valueAxis,
|
|
261
|
+
...build.shared,
|
|
262
|
+
tooltip: build.tooltipFor("y")
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
function buildScatter(build) {
|
|
266
|
+
const { data, getX, getY, getSeries } = build;
|
|
267
|
+
return defineChartSpec({
|
|
268
|
+
marks: [
|
|
269
|
+
dot(data, {
|
|
270
|
+
x: getX,
|
|
271
|
+
y: getY,
|
|
272
|
+
z: getSeries,
|
|
273
|
+
color: getSeries,
|
|
274
|
+
r: 4,
|
|
275
|
+
fill: null == getSeries ? SERIES_1_PAINT : void 0
|
|
276
|
+
})
|
|
277
|
+
],
|
|
278
|
+
x: {
|
|
279
|
+
...build.xAxis,
|
|
280
|
+
grid: build.grid
|
|
281
|
+
},
|
|
282
|
+
y: build.valueAxis,
|
|
283
|
+
...build.shared,
|
|
284
|
+
tooltip: build.tooltipFor("y")
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
function buildPolar(build, donut) {
|
|
288
|
+
const { data, getX, getY } = build;
|
|
289
|
+
const sliceLabel = (slice)=>String(getX(slice.data));
|
|
290
|
+
const slices = pie().sort(null).padAngle(0.02).value((datum)=>getY(datum))([
|
|
291
|
+
...data
|
|
292
|
+
]);
|
|
293
|
+
return defineChartSpec({
|
|
294
|
+
x: null,
|
|
295
|
+
y: null,
|
|
296
|
+
marks: [
|
|
297
|
+
polar({
|
|
298
|
+
inset: POLAR_INSET,
|
|
299
|
+
marks: [
|
|
300
|
+
radialArc(slices, {
|
|
301
|
+
startAngle: "startAngle",
|
|
302
|
+
endAngle: "endAngle",
|
|
303
|
+
padAngle: "padAngle",
|
|
304
|
+
innerRadius: donut ? ({ radius })=>radius * DONUT_INNER_RADIUS_RATIO : 0,
|
|
305
|
+
cornerRadius: 2,
|
|
306
|
+
z: sliceLabel,
|
|
307
|
+
color: sliceLabel
|
|
308
|
+
})
|
|
309
|
+
]
|
|
310
|
+
})
|
|
311
|
+
],
|
|
312
|
+
...build.shared,
|
|
313
|
+
tooltip: build.polarTooltip()
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
function buildChartDefinition(config) {
|
|
317
|
+
const build = resolveBuild(config);
|
|
318
|
+
switch(config.type){
|
|
319
|
+
case "area":
|
|
320
|
+
return buildArea(build);
|
|
321
|
+
case "bar":
|
|
322
|
+
return buildBars(build, false);
|
|
323
|
+
case "bar-horizontal":
|
|
324
|
+
return buildBars(build, true);
|
|
325
|
+
case "scatter":
|
|
326
|
+
return buildScatter(build);
|
|
327
|
+
case "pie":
|
|
328
|
+
return buildPolar(build, false);
|
|
329
|
+
case "donut":
|
|
330
|
+
return buildPolar(build, true);
|
|
331
|
+
default:
|
|
332
|
+
return buildLine(build);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
function chart_Chart({ type = "line", data, x, y, series, stacked, points = false, smooth = false, grid = true, legend, legendLabel, xLabel, yLabel, formatValue, height, aspectRatio, initialWidth = 640, animate = true, tooltip = true, ariaLabel, ariaDescription, className, onSelect }) {
|
|
336
|
+
const { root, tooltip: tooltipSlot } = chartVariants();
|
|
337
|
+
const tooltipClassName = tooltipSlot();
|
|
338
|
+
const definition = useMemo(()=>buildChartDefinition({
|
|
339
|
+
type,
|
|
340
|
+
data,
|
|
341
|
+
x,
|
|
342
|
+
y,
|
|
343
|
+
series,
|
|
344
|
+
stacked,
|
|
345
|
+
points,
|
|
346
|
+
smooth,
|
|
347
|
+
grid,
|
|
348
|
+
legend,
|
|
349
|
+
legendLabel,
|
|
350
|
+
xLabel,
|
|
351
|
+
yLabel,
|
|
352
|
+
formatValue,
|
|
353
|
+
animate,
|
|
354
|
+
showTooltip: tooltip,
|
|
355
|
+
tooltipClassName
|
|
356
|
+
}), [
|
|
357
|
+
type,
|
|
358
|
+
data,
|
|
359
|
+
x,
|
|
360
|
+
y,
|
|
361
|
+
series,
|
|
362
|
+
stacked,
|
|
363
|
+
points,
|
|
364
|
+
smooth,
|
|
365
|
+
grid,
|
|
366
|
+
legend,
|
|
367
|
+
legendLabel,
|
|
368
|
+
xLabel,
|
|
369
|
+
yLabel,
|
|
370
|
+
formatValue,
|
|
371
|
+
animate,
|
|
372
|
+
tooltip,
|
|
373
|
+
tooltipClassName
|
|
374
|
+
]);
|
|
375
|
+
const isPolar = "pie" === type || "donut" === type;
|
|
376
|
+
const handleSelect = onSelect ? (point)=>{
|
|
377
|
+
if (null == point) return void onSelect(null);
|
|
378
|
+
onSelect(isPolar ? point.datum.data : point.datum);
|
|
379
|
+
} : void 0;
|
|
380
|
+
return /*#__PURE__*/ jsx(Chart, {
|
|
381
|
+
ariaDescription: ariaDescription,
|
|
382
|
+
ariaLabel: ariaLabel,
|
|
383
|
+
aspectRatio: null == height ? aspectRatio ?? 16 / 9 : void 0,
|
|
384
|
+
className: root({
|
|
385
|
+
className
|
|
386
|
+
}),
|
|
387
|
+
definition: definition,
|
|
388
|
+
height: height,
|
|
389
|
+
initialWidth: initialWidth,
|
|
390
|
+
onSelect: handleSelect
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
export { chart_Chart as Chart };
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
export type { ChartPoint } from "@tanstack/charts";
|
|
2
|
+
/** The supported chart forms; each maps to one TanStack Charts mark family. */
|
|
3
|
+
export type ChartType = "line" | "area" | "bar" | "bar-horizontal" | "scatter" | "pie" | "donut";
|
|
4
|
+
/** A channel: either a field name on the datum or an accessor function. */
|
|
5
|
+
export type ChartAccessor<TDatum, TValue> = (keyof TDatum & string) | ((datum: TDatum) => TValue);
|
|
6
|
+
export type ChartProps<TDatum> = {
|
|
7
|
+
/** Which chart form to render. Defaults to "line". */
|
|
8
|
+
type?: ChartType;
|
|
9
|
+
/** Source rows. Each row flows through unchanged into tooltips and onSelect. */
|
|
10
|
+
data: readonly TDatum[];
|
|
11
|
+
/** Category / x-position channel. For pie and donut this is the slice label. */
|
|
12
|
+
x: ChartAccessor<TDatum, string | number | Date>;
|
|
13
|
+
/** Numeric value channel. For pie and donut this is the slice value. */
|
|
14
|
+
y: ChartAccessor<TDatum, number>;
|
|
15
|
+
/**
|
|
16
|
+
* Optional series identity. Splits the data into one colored series per
|
|
17
|
+
* distinct value, assigned the `--color-chart-series-*` tokens in fixed
|
|
18
|
+
* order. Without it the chart renders a single series in series-1.
|
|
19
|
+
*/
|
|
20
|
+
series?: ChartAccessor<TDatum, string | number>;
|
|
21
|
+
/**
|
|
22
|
+
* Multi-series layout: stacked (true) or side-by-side/overlapping (false).
|
|
23
|
+
* Defaults to true for "area" and false for "bar"/"bar-horizontal".
|
|
24
|
+
*/
|
|
25
|
+
stacked?: boolean;
|
|
26
|
+
/** Draw a dot on every data point of a line chart. */
|
|
27
|
+
points?: boolean;
|
|
28
|
+
/** Smooth line/area paths with a monotone curve instead of straight segments. */
|
|
29
|
+
smooth?: boolean;
|
|
30
|
+
/** Grid lines on the value axis. Defaults to true. */
|
|
31
|
+
grid?: boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Show the categorical color legend. Defaults to true when `series` is set
|
|
34
|
+
* or the chart is a pie/donut, false otherwise.
|
|
35
|
+
*/
|
|
36
|
+
legend?: boolean;
|
|
37
|
+
/** Optional title above the legend swatches. */
|
|
38
|
+
legendLabel?: string;
|
|
39
|
+
/** Axis label for the category/x channel. */
|
|
40
|
+
xLabel?: string;
|
|
41
|
+
/** Axis label for the value channel. */
|
|
42
|
+
yLabel?: string;
|
|
43
|
+
/** Formats value-axis tick labels (e.g. currency). */
|
|
44
|
+
formatValue?: (value: number) => string;
|
|
45
|
+
/** Fixed pixel height. When omitted, `aspectRatio` controls sizing. */
|
|
46
|
+
height?: number;
|
|
47
|
+
/** Width/height ratio used when `height` is omitted. Defaults to 16/9. */
|
|
48
|
+
aspectRatio?: number;
|
|
49
|
+
/** Deterministic first-frame width for SSR and hidden containers. */
|
|
50
|
+
initialWidth?: number;
|
|
51
|
+
/** Animate mark transitions. Defaults to true. */
|
|
52
|
+
animate?: boolean;
|
|
53
|
+
/** Show the built-in tooltip on hover/focus. Defaults to true. */
|
|
54
|
+
tooltip?: boolean;
|
|
55
|
+
/** Accessible chart name. Required. */
|
|
56
|
+
ariaLabel: string;
|
|
57
|
+
/** Longer accessible description. */
|
|
58
|
+
ariaDescription?: string;
|
|
59
|
+
className?: string;
|
|
60
|
+
/** Fires with the source row of the selected point, or null on deselect. */
|
|
61
|
+
onSelect?: (datum: TDatum | null) => void;
|
|
62
|
+
};
|
|
63
|
+
export declare function Chart<TDatum>({ type, data, x, y, series, stacked, points, smooth, grid, legend, legendLabel, xLabel, yLabel, formatValue, height, aspectRatio, initialWidth, animate, tooltip, ariaLabel, ariaDescription, className, onSelect, }: ChartProps<TDatum>): import("react/jsx-runtime").JSX.Element;
|
|
64
|
+
//# sourceMappingURL=chart.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chart.d.ts","sourceRoot":"","sources":["../../../src/molecules/chart.tsx"],"names":[],"mappings":"AAyDA,YAAY,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAElD,+EAA+E;AAC/E,MAAM,MAAM,SAAS,GACjB,MAAM,GACN,MAAM,GACN,KAAK,GACL,gBAAgB,GAChB,SAAS,GACT,KAAK,GACL,OAAO,CAAA;AAEX,2EAA2E;AAC3E,MAAM,MAAM,aAAa,CAAC,MAAM,EAAE,MAAM,IACpC,CAAC,MAAM,MAAM,GAAG,MAAM,CAAC,GACvB,CAAC,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC,CAAA;AAE/B,MAAM,MAAM,UAAU,CAAC,MAAM,IAAI;IAC/B,sDAAsD;IACtD,IAAI,CAAC,EAAE,SAAS,CAAA;IAChB,gFAAgF;IAChF,IAAI,EAAE,SAAS,MAAM,EAAE,CAAA;IACvB,gFAAgF;IAChF,CAAC,EAAE,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC,CAAA;IAChD,wEAAwE;IACxE,CAAC,EAAE,aAAa,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAChC;;;;OAIG;IACH,MAAM,CAAC,EAAE,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAA;IAC/C;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,sDAAsD;IACtD,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,iFAAiF;IACjF,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,sDAAsD;IACtD,IAAI,CAAC,EAAE,OAAO,CAAA;IACd;;;OAGG;IACH,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,gDAAgD;IAChD,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,6CAA6C;IAC7C,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,wCAAwC;IACxC,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,sDAAsD;IACtD,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAA;IACvC,uEAAuE;IACvE,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,0EAA0E;IAC1E,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,qEAAqE;IACrE,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,kDAAkD;IAClD,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,kEAAkE;IAClE,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,uCAAuC;IACvC,SAAS,EAAE,MAAM,CAAA;IACjB,qCAAqC;IACrC,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,4EAA4E;IAC5E,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAA;CAC1C,CAAA;AA2gBD,wBAAgB,KAAK,CAAC,MAAM,EAAE,EAC5B,IAAa,EACb,IAAI,EACJ,CAAC,EACD,CAAC,EACD,MAAM,EACN,OAAO,EACP,MAAc,EACd,MAAc,EACd,IAAW,EACX,MAAM,EACN,WAAW,EACX,MAAM,EACN,MAAM,EACN,WAAW,EACX,MAAM,EACN,WAAW,EACX,YAAkB,EAClB,OAAc,EACd,OAAc,EACd,SAAS,EACT,eAAe,EACf,SAAS,EACT,QAAQ,GACT,EAAE,UAAU,CAAC,MAAM,CAAC,2CA2EpB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"changelog.stories.d.ts","sourceRoot":"","sources":["../../../stories/changelog/changelog.stories.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAA;
|
|
1
|
+
{"version":3,"file":"changelog.stories.d.ts","sourceRoot":"","sources":["../../../stories/changelog/changelog.stories.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAoKtD,iBAAS,SAAS,4CAUjB;AAED,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,OAAO,SAAS,CAKhC,CAAA;AAED,eAAe,IAAI,CAAA;AAEnB,eAAO,MAAM,UAAU,EAAE,QAAQ,CAAC,OAAO,SAAS,CAAM,CAAA"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
|
+
import { Chart } from "../../src/molecules/chart";
|
|
3
|
+
type RevenuePoint = {
|
|
4
|
+
month: string;
|
|
5
|
+
revenue: number;
|
|
6
|
+
channel: "Online" | "Retail" | "Wholesale";
|
|
7
|
+
};
|
|
8
|
+
declare const meta: Meta<typeof Chart<RevenuePoint>>;
|
|
9
|
+
export default meta;
|
|
10
|
+
type Story = StoryObj<typeof meta>;
|
|
11
|
+
export declare const Playground: Story;
|
|
12
|
+
/** Every value of the `type` prop, driven by the channel that fits it. */
|
|
13
|
+
export declare const Types: Story;
|
|
14
|
+
/** One series: painted with `--color-chart-series-1`, no legend box. */
|
|
15
|
+
export declare const SingleSeries: Story;
|
|
16
|
+
/** `stacked` flips bars between side-by-side groups and stacked segments. */
|
|
17
|
+
export declare const Stacking: Story;
|
|
18
|
+
/** Selecting a mark reports the original source row through `onSelect`. */
|
|
19
|
+
export declare const Interactive: Story;
|
|
20
|
+
/** Date x values ride a UTC time scale, so uneven gaps keep their spacing. */
|
|
21
|
+
export declare const TimeSeries: Story;
|
|
22
|
+
/** Chart chrome switched off for embedding in dense dashboard tiles. */
|
|
23
|
+
export declare const Minimal: Story;
|
|
24
|
+
//# sourceMappingURL=chart.stories.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chart.stories.d.ts","sourceRoot":"","sources":["../../../stories/molecules/chart.stories.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAItD,OAAO,EAAE,KAAK,EAAkB,MAAM,2BAA2B,CAAA;AAIjE,KAAK,YAAY,GAAG;IAClB,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,QAAQ,GAAG,QAAQ,GAAG,WAAW,CAAA;CAC3C,CAAA;AAiED,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,OAAO,KAAK,CAAC,YAAY,CAAC,CAqEC,CAAA;AAE5C,eAAe,IAAI,CAAA;AACnB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,IAAI,CAAC,CAAA;AAIlC,eAAO,MAAM,UAAU,EAAE,KASxB,CAAA;AAED,0EAA0E;AAC1E,eAAO,MAAM,KAAK,EAAE,KAsFnB,CAAA;AAED,wEAAwE;AACxE,eAAO,MAAM,YAAY,EAAE,KAe1B,CAAA;AAED,6EAA6E;AAC7E,eAAO,MAAM,QAAQ,EAAE,KA4CtB,CAAA;AAED,2EAA2E;AAC3E,eAAO,MAAM,WAAW,EAAE,KAyBzB,CAAA;AAaD,8EAA8E;AAC9E,eAAO,MAAM,UAAU,EAAE,KAcxB,CAAA;AAED,wEAAwE;AACxE,eAAO,MAAM,OAAO,EAAE,KAiBrB,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@techsio/ui-kit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.39.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -86,6 +86,9 @@
|
|
|
86
86
|
"@iconify-json/mdi-light": "^1.2.2",
|
|
87
87
|
"@iconify-json/svg-spinners": "^1.2.4",
|
|
88
88
|
"@iconify/tailwind4": "^1.1.0",
|
|
89
|
+
"@tanstack/charts": "^0.6.5",
|
|
90
|
+
"@tanstack/charts-scales": "^0.6.5",
|
|
91
|
+
"@tanstack/react-charts": "^0.6.5",
|
|
89
92
|
"@tanstack/react-table": "^9.1.2",
|
|
90
93
|
"@tanstack/react-virtual": "^3.14.8",
|
|
91
94
|
"@zag-js/accordion": "^1.41.2",
|
|
@@ -110,6 +113,8 @@
|
|
|
110
113
|
"@zag-js/tooltip": "^1.41.2",
|
|
111
114
|
"@zag-js/tree-view": "^1.41.2",
|
|
112
115
|
"better-themes": "^1.1.0",
|
|
116
|
+
"d3-scale": "^4.0.2",
|
|
117
|
+
"d3-shape": "^3.2.0",
|
|
113
118
|
"libphonenumber-js": "^1.13.2",
|
|
114
119
|
"tailwind-merge": "^3.4.0",
|
|
115
120
|
"tailwind-variants": "^3.1.1",
|
|
@@ -135,6 +140,8 @@
|
|
|
135
140
|
"@tailwindcss/postcss": "^4.1.17",
|
|
136
141
|
"@techsio/storybook-a11y-reporter": "0.1.3",
|
|
137
142
|
"@techsio/storybook-better-a11y": "0.1.3",
|
|
143
|
+
"@types/d3-scale": "^4.0.9",
|
|
144
|
+
"@types/d3-shape": "^3.1.8",
|
|
138
145
|
"@typescript-eslint/parser": "^8.47.0",
|
|
139
146
|
"eslint": "^9.39.1",
|
|
140
147
|
"eslint-plugin-tailwindcss": "beta",
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
@import "./molecules/_accordion.css";
|
|
20
20
|
@import "./molecules/_breadcrumb.css";
|
|
21
21
|
@import "./molecules/_carousel.css";
|
|
22
|
+
@import "./molecules/_chart.css";
|
|
22
23
|
@import "./molecules/_color-select.css";
|
|
23
24
|
@import "./molecules/_combobox.css";
|
|
24
25
|
@import "./molecules/_dialog.css";
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Chart — PROTOTYPE tokens (not yet exported from Figma; once the MVP look is
|
|
3
|
+
* signed off, mirror these into Figma via the figma-token-binding skill).
|
|
4
|
+
*
|
|
5
|
+
* Series palette: six fixed-order categorical hues for data identity. The
|
|
6
|
+
* exact primitive steps were chosen so the SAME values pass the dataviz
|
|
7
|
+
* six-checks validator (lightness band, chroma floor, adjacent-pair CVD
|
|
8
|
+
* separation, normal-vision floor, >=3:1 surface contrast) on BOTH the light
|
|
9
|
+
* (white) and dark (gray-950) chart surfaces — hence no light-dark() split.
|
|
10
|
+
*
|
|
11
|
+
* `--color-primary` (blue-300/sky-300) is deliberately NOT series-1: it sits
|
|
12
|
+
* outside the chart lightness band in light mode. Brand themes that want
|
|
13
|
+
* branded charts should override `--color-chart-series-*` with steps that
|
|
14
|
+
* re-pass the validator, not alias the brand accent directly.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
@theme static {
|
|
18
|
+
/* Reference layer — series identity, assigned in fixed order. The palette is
|
|
19
|
+
* six entries and TanStack indexes it modulo its length (scales.js), so a 7th
|
|
20
|
+
* distinct series wraps back onto series-1 rather than erroring. Keep charts
|
|
21
|
+
* to <=6 series; Chart warns in dev when a dataset exceeds that.
|
|
22
|
+
*
|
|
23
|
+
* Note: `--color-blue-500` is re-declared by figma/variables.css, which is
|
|
24
|
+
* imported after this file — series-1 therefore resolves to the Figma step
|
|
25
|
+
* (chroma 0.188), not Tailwind's default (chroma 0.214). Series 2-6 have no
|
|
26
|
+
* Figma counterpart and resolve to Tailwind defaults. Re-run the dataviz
|
|
27
|
+
* validator against these *resolved* values before treating the set as
|
|
28
|
+
* signed off. */
|
|
29
|
+
--color-chart-series-1: var(--color-blue-500);
|
|
30
|
+
--color-chart-series-2: var(--color-orange-600);
|
|
31
|
+
--color-chart-series-3: var(--color-emerald-600);
|
|
32
|
+
--color-chart-series-4: var(--color-violet-500);
|
|
33
|
+
--color-chart-series-5: var(--color-rose-500);
|
|
34
|
+
--color-chart-series-6: var(--color-cyan-600);
|
|
35
|
+
|
|
36
|
+
/* Derived layer — chart chrome and tooltip surface. Axis/tick/legend text
|
|
37
|
+
* renders at small sizes inside the SVG, so it wears primary ink rather than
|
|
38
|
+
* the secondary tone.
|
|
39
|
+
*
|
|
40
|
+
* Caveat on the contrast math: TanStack composites this color before it
|
|
41
|
+
* paints — tick/legend labels at fillOpacity 0.68, axis lines at
|
|
42
|
+
* strokeOpacity 0.28 (scene.js). So the *rendered* label is fg-primary at 68%
|
|
43
|
+
* over the surface, not fg-primary itself, and it does not reach the repo's
|
|
44
|
+
* 90 Lc gold gate on its own. Fixing that properly means compensating for the
|
|
45
|
+
* library opacity (or overriding it), not swapping this token — tracked in
|
|
46
|
+
* https://github.com/TechsioCZ/new-engine/issues/608 rather than silently
|
|
47
|
+
* claimed as passing. */
|
|
48
|
+
--color-chart-fg: var(--color-fg-primary);
|
|
49
|
+
--color-chart-tooltip-bg: var(--color-tooltip-bg);
|
|
50
|
+
--color-chart-tooltip-fg: var(--color-tooltip-fg);
|
|
51
|
+
--color-chart-tooltip-border: var(--color-tooltip-border-outline);
|
|
52
|
+
--radius-chart-tooltip: var(--radius-tooltip);
|
|
53
|
+
--text-chart-tooltip: var(--text-tooltip-md);
|
|
54
|
+
--padding-chart-tooltip: var(--padding-tooltip-md);
|
|
55
|
+
--border-width-chart-tooltip: var(--border-width-tooltip);
|
|
56
|
+
--font-chart-tooltip: var(--font-body);
|
|
57
|
+
--font-weight-chart-tooltip: var(--font-weight-medium);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/*
|
|
61
|
+
* Bridge our series tokens into TanStack Charts' categorical palette
|
|
62
|
+
* variables (`--ts-chart-1..6`). Axis text, muted labels and grid lines
|
|
63
|
+
* inherit `currentColor`, so the host also carries `text-chart-fg`.
|
|
64
|
+
*/
|
|
65
|
+
@utility chart-base {
|
|
66
|
+
--ts-chart-1: var(--color-chart-series-1);
|
|
67
|
+
--ts-chart-2: var(--color-chart-series-2);
|
|
68
|
+
--ts-chart-3: var(--color-chart-series-3);
|
|
69
|
+
--ts-chart-4: var(--color-chart-series-4);
|
|
70
|
+
--ts-chart-5: var(--color-chart-series-5);
|
|
71
|
+
--ts-chart-6: var(--color-chart-series-6);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/*
|
|
75
|
+
* Surface styling for the library's native HTML tooltip element.
|
|
76
|
+
*
|
|
77
|
+
* `!important` is load-bearing, not defensive: TanStack's `createTooltip()`
|
|
78
|
+
* writes background/color/border/border-radius/padding and a `font` shorthand
|
|
79
|
+
* directly onto `element.style`, then only *appends* this class. Inline styles
|
|
80
|
+
* beat class rules, so without `!important` every declaration below is dead and
|
|
81
|
+
* the tooltip renders in the system `Canvas`/`CanvasText` colors — visible as an
|
|
82
|
+
* unthemed tooltip under the neo/cyber brands and in dark mode. The `font`
|
|
83
|
+
* shorthand also resets font-size, so that too must win.
|
|
84
|
+
*/
|
|
85
|
+
@utility chart-tooltip {
|
|
86
|
+
background: var(--color-chart-tooltip-bg) !important;
|
|
87
|
+
color: var(--color-chart-tooltip-fg) !important;
|
|
88
|
+
border: var(--border-width-chart-tooltip) solid
|
|
89
|
+
var(--color-chart-tooltip-border) !important;
|
|
90
|
+
border-radius: var(--radius-chart-tooltip) !important;
|
|
91
|
+
padding: var(--padding-chart-tooltip) !important;
|
|
92
|
+
/* The inline `font` shorthand sets family, weight, size and line-height in
|
|
93
|
+
* one go, so countering only the size would leave the chart tooltip in
|
|
94
|
+
* system-ui while every other tooltip in the library uses --font-body. */
|
|
95
|
+
font-family: var(--font-chart-tooltip) !important;
|
|
96
|
+
font-size: var(--text-chart-tooltip) !important;
|
|
97
|
+
font-weight: var(--font-weight-chart-tooltip) !important;
|
|
98
|
+
}
|