@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.
@@ -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;AAiKtD,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"}
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.38.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
+ }