@scalewing/react 1.38.0 → 1.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/CHANGELOG.md +9 -0
- package/README.md +53 -1
- package/dist/chart-axis.d.ts +61 -0
- package/dist/chart-axis.js +83 -0
- package/dist/chart-dates.d.ts +6 -0
- package/dist/chart-dates.js +15 -0
- package/dist/chart-metrics.d.ts +40 -0
- package/dist/chart-metrics.js +48 -0
- package/dist/chart-scale.d.ts +48 -0
- package/dist/chart-scale.js +116 -0
- package/dist/chart-table-rows.d.ts +8 -0
- package/dist/chart-table-rows.js +1 -0
- package/dist/column-chart-bars.d.ts +38 -0
- package/dist/column-chart-bars.js +82 -0
- package/dist/column-chart-callouts.d.ts +48 -0
- package/dist/column-chart-callouts.js +54 -0
- package/dist/column-chart-input.d.ts +40 -0
- package/dist/column-chart-input.js +60 -0
- package/dist/column-chart-layout.d.ts +63 -0
- package/dist/column-chart-layout.js +97 -0
- package/dist/components/BarChart.d.ts +1 -1
- package/dist/components/BarChart.js +1 -1
- package/dist/components/ChartLegend.d.ts +38 -0
- package/dist/components/ChartLegend.js +32 -0
- package/dist/components/ColumnChart.d.ts +72 -0
- package/dist/components/ColumnChart.js +71 -0
- package/dist/components/DataBar.d.ts +6 -6
- package/dist/components/LineChart.d.ts +83 -0
- package/dist/components/LineChart.js +77 -0
- package/dist/components/chart/ChartTable.d.ts +16 -0
- package/dist/components/chart/ChartTable.js +12 -0
- package/dist/components/chart/ChartValueAxis.d.ts +14 -0
- package/dist/components/chart/ChartValueAxis.js +11 -0
- package/dist/components/chart/use-chart-width.d.ts +8 -0
- package/dist/components/chart/use-chart-width.js +34 -0
- package/dist/components/column-chart/ChartCalloutList.d.ts +16 -0
- package/dist/components/column-chart/ChartCalloutList.js +13 -0
- package/dist/components/column-chart/ColumnChartDrawing.d.ts +17 -0
- package/dist/components/column-chart/ColumnChartDrawing.js +18 -0
- package/dist/components/line-chart/LineChartDrawing.d.ts +17 -0
- package/dist/components/line-chart/LineChartDrawing.js +16 -0
- package/dist/css/chart-marks.d.ts +10 -0
- package/dist/css/chart-marks.js +15 -0
- package/dist/css/chart-tones.d.ts +68 -0
- package/dist/css/chart-tones.js +130 -0
- package/dist/css/{css-chart.d.ts → css-bar-chart.d.ts} +2 -2
- package/dist/css/{css-chart.js → css-bar-chart.js} +2 -2
- package/dist/css/css-chart-legend.d.ts +5 -0
- package/dist/css/css-chart-legend.js +102 -0
- package/dist/css/css-chart-plot.d.ts +2 -0
- package/dist/css/css-chart-plot.js +170 -0
- package/dist/css/css-components.js +14 -2
- package/dist/css/css-data-bar.d.ts +4 -9
- package/dist/css/css-data-bar.js +15 -31
- package/dist/css/css-line-chart.d.ts +2 -0
- package/dist/css/css-line-chart.js +31 -0
- package/dist/css/stylesheet.js +10 -2
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/is-one-of.d.ts +5 -0
- package/dist/is-one-of.js +7 -0
- package/dist/line-chart-input.d.ts +39 -0
- package/dist/line-chart-input.js +70 -0
- package/dist/line-chart-layout.d.ts +68 -0
- package/dist/line-chart-layout.js +99 -0
- package/dist/line-chart-markers.d.ts +29 -0
- package/dist/line-chart-markers.js +57 -0
- package/dist/line-chart-path.d.ts +18 -0
- package/dist/line-chart-path.js +33 -0
- package/dist/line-chart-points.d.ts +6 -0
- package/dist/line-chart-points.js +8 -0
- package/dist/line-chart-table.d.ts +15 -0
- package/dist/line-chart-table.js +29 -0
- package/dist/styles.css +219 -0
- package/package.json +1 -1
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { formatBarChartValue } from '@scalewing/tokens';
|
|
4
|
+
import { forwardRef, useImperativeHandle, useRef, } from 'react';
|
|
5
|
+
import { cx } from '../class-names.js';
|
|
6
|
+
import { columnChartModes, } from '../column-chart-bars.js';
|
|
7
|
+
import { assertColumnChartInput, } from '../column-chart-input.js';
|
|
8
|
+
import { columnChartLayout } from '../column-chart-layout.js';
|
|
9
|
+
import { ChartTable } from './chart/ChartTable.js';
|
|
10
|
+
import { useChartWidth } from './chart/use-chart-width.js';
|
|
11
|
+
import { ChartCalloutList } from './column-chart/ChartCalloutList.js';
|
|
12
|
+
import { ColumnChartDrawing } from './column-chart/ColumnChartDrawing.js';
|
|
13
|
+
export { columnChartModes };
|
|
14
|
+
const noCallouts = [];
|
|
15
|
+
/**
|
|
16
|
+
* Columns for each category (a week, a month), stacked or grouped by
|
|
17
|
+
* series, with an optional line on its own right-hand scale whose zero
|
|
18
|
+
* and grid lines are level with the columns', the current period boxed
|
|
19
|
+
* and callouts pinned above their columns. It fills its container's width
|
|
20
|
+
* and is laid out for it: a narrow chart labels fewer categories and lists
|
|
21
|
+
* its callouts under the plot, never scrolling sideways. The drawing is
|
|
22
|
+
* hidden from assistive technology; a visually hidden table named by
|
|
23
|
+
* `label` carries every figure, and the callouts stay a list. Put it on
|
|
24
|
+
* the surface (a `Card`) with a `ChartLegend` above it naming each series
|
|
25
|
+
* (`bar` in its tone), the line (`line`, `accent`) and the box (`box`,
|
|
26
|
+
* `accent`). Input it cannot draw throws a `RangeError`.
|
|
27
|
+
*/
|
|
28
|
+
export const ColumnChart = forwardRef(function ColumnChart({ callouts = noCallouts, categories, className, formatTick, formatValue = formatBarChartValue, highlight, label, line, mode = 'stacked', series, ...rest }, ref) {
|
|
29
|
+
assertColumnChartInput({
|
|
30
|
+
label,
|
|
31
|
+
categories,
|
|
32
|
+
series,
|
|
33
|
+
mode,
|
|
34
|
+
line,
|
|
35
|
+
highlight,
|
|
36
|
+
callouts,
|
|
37
|
+
});
|
|
38
|
+
const box = useRef(null);
|
|
39
|
+
useImperativeHandle(ref, () => box.current, []);
|
|
40
|
+
const width = useChartWidth(box);
|
|
41
|
+
const lastLineValue = line?.values.at(-1);
|
|
42
|
+
const layout = columnChartLayout({
|
|
43
|
+
width,
|
|
44
|
+
categories,
|
|
45
|
+
series: series.map((s) => s.values),
|
|
46
|
+
mode,
|
|
47
|
+
line: line
|
|
48
|
+
? {
|
|
49
|
+
values: line.values,
|
|
50
|
+
endLabel: line.endLabel === false || lastLineValue === undefined
|
|
51
|
+
? null
|
|
52
|
+
: formatValue(lastLineValue),
|
|
53
|
+
}
|
|
54
|
+
: null,
|
|
55
|
+
highlight: highlight ?? null,
|
|
56
|
+
callouts,
|
|
57
|
+
formatTick: formatTick ?? formatValue,
|
|
58
|
+
});
|
|
59
|
+
return (_jsxs("div", { ref: box, className: cx('sw-chart', className), ...rest, children: [_jsx(ColumnChartDrawing, { calloutTones: callouts.map((callout) => callout.tone), layout: layout, tones: series.map((s) => s.tone) }), _jsx(ChartTable, { caption: label, columns: [
|
|
60
|
+
...series.map((s) => s.label),
|
|
61
|
+
...(line ? [line.label] : []),
|
|
62
|
+
], rows: categories.map((category, index) => ({
|
|
63
|
+
key: `${category}-${index}`,
|
|
64
|
+
header: category,
|
|
65
|
+
cells: [
|
|
66
|
+
...series.map((s) => formatValue(s.values[index])),
|
|
67
|
+
...(line ? [formatValue(line.values[index])] : []),
|
|
68
|
+
],
|
|
69
|
+
current: index === highlight,
|
|
70
|
+
})) }), _jsx(ChartCalloutList, { callouts: callouts, visible: layout.compact })] }));
|
|
71
|
+
});
|
|
@@ -13,9 +13,9 @@ export type DataBarProps = Omit<HTMLAttributes<HTMLSpanElement>, 'children' | 'a
|
|
|
13
13
|
max: number;
|
|
14
14
|
/**
|
|
15
15
|
* What the bar means (default `neutral`): `success` for money in,
|
|
16
|
-
* `muted` for money out (an outflow is not an error, so
|
|
17
|
-
* danger
|
|
18
|
-
*
|
|
16
|
+
* `muted` for money out (an outflow is not an error, so it is never
|
|
17
|
+
* danger), `strong` for a subtotal, `accent` for a counted or recorded
|
|
18
|
+
* figure and `danger` for a shortage, never for cash leaving.
|
|
19
19
|
*/
|
|
20
20
|
tone?: DataBarTone;
|
|
21
21
|
};
|
|
@@ -40,9 +40,9 @@ export declare const DataBar: import("react").ForwardRefExoticComponent<Omit<HTM
|
|
|
40
40
|
max: number;
|
|
41
41
|
/**
|
|
42
42
|
* What the bar means (default `neutral`): `success` for money in,
|
|
43
|
-
* `muted` for money out (an outflow is not an error, so
|
|
44
|
-
* danger
|
|
45
|
-
*
|
|
43
|
+
* `muted` for money out (an outflow is not an error, so it is never
|
|
44
|
+
* danger), `strong` for a subtotal, `accent` for a counted or recorded
|
|
45
|
+
* figure and `danger` for a shortage, never for cash leaving.
|
|
46
46
|
*/
|
|
47
47
|
tone?: DataBarTone;
|
|
48
48
|
} & import("react").RefAttributes<HTMLSpanElement>>;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { type HTMLAttributes } from 'react';
|
|
2
|
+
import { type ChartStrokeTone } from '../css/chart-tones.js';
|
|
3
|
+
import { type LineChartMarker, type LineChartPeriod, type LineChartPoint } from '../line-chart-input.js';
|
|
4
|
+
import { type LineChartCurve, lineChartCurves } from '../line-chart-path.js';
|
|
5
|
+
export type { LineChartCurve, LineChartMarker, LineChartPeriod, LineChartPoint, };
|
|
6
|
+
export { lineChartCurves };
|
|
7
|
+
export type LineChartProps = Omit<HTMLAttributes<HTMLDivElement>, 'children'> & {
|
|
8
|
+
/** Names the chart: the caption of the table that carries its figures. */
|
|
9
|
+
label: string;
|
|
10
|
+
/** Heads the table's value column; defaults to `label`. */
|
|
11
|
+
valueLabel?: string;
|
|
12
|
+
/** The values by date (`YYYY-MM-DD`), in date order. */
|
|
13
|
+
points: readonly LineChartPoint[];
|
|
14
|
+
/**
|
|
15
|
+
* `linear` (the default) joins each point to the next; `step` holds a
|
|
16
|
+
* value until the next date, as a balance that changes on a day does.
|
|
17
|
+
*/
|
|
18
|
+
curve?: LineChartCurve;
|
|
19
|
+
/** Fills the area between the line and zero with a tint of its tone. */
|
|
20
|
+
area?: boolean;
|
|
21
|
+
/** The line's stroke tone (default `accent`). */
|
|
22
|
+
tone?: ChartStrokeTone;
|
|
23
|
+
/** Downward triangles on the line at some of its dates, with words. */
|
|
24
|
+
markers?: readonly LineChartMarker[];
|
|
25
|
+
/** Heads the table's column of marker words; required with markers. */
|
|
26
|
+
markersLabel?: string;
|
|
27
|
+
/** The dates labelled on the time axis; the first and last by default. */
|
|
28
|
+
ticks?: readonly string[];
|
|
29
|
+
/** A period to box, such as the current week. */
|
|
30
|
+
highlight?: LineChartPeriod;
|
|
31
|
+
/** Writes the last value beside the plot's end (default `true`). */
|
|
32
|
+
endLabel?: boolean;
|
|
33
|
+
/** Writes each figure in the table and the last value. */
|
|
34
|
+
formatValue?: (value: number) => string;
|
|
35
|
+
/** Writes the value axis; defaults to `formatValue`. */
|
|
36
|
+
formatTick?: (value: number) => string;
|
|
37
|
+
/** Writes a date for the time axis and the table; the date itself by default. */
|
|
38
|
+
formatDate?: (date: string) => string;
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* A value over time: a line through its points by date, straight or in
|
|
42
|
+
* steps, optionally over a tinted area, with markers on chosen dates, a
|
|
43
|
+
* boxed period and its last value beside the plot. It fills its
|
|
44
|
+
* container's width and is laid out for it, labelling fewer dates when
|
|
45
|
+
* they would meet, never scrolling sideways. The drawing is hidden from
|
|
46
|
+
* assistive technology; a visually hidden table named by `label` carries
|
|
47
|
+
* every date, value and marker. Pair it with a `ChartLegend` (`line` in its
|
|
48
|
+
* tone, `marker` in each marker's, `box` in `accent`), on the surface.
|
|
49
|
+
* Input it cannot draw throws a `RangeError`.
|
|
50
|
+
*/
|
|
51
|
+
export declare const LineChart: import("react").ForwardRefExoticComponent<Omit<HTMLAttributes<HTMLDivElement>, "children"> & {
|
|
52
|
+
/** Names the chart: the caption of the table that carries its figures. */
|
|
53
|
+
label: string;
|
|
54
|
+
/** Heads the table's value column; defaults to `label`. */
|
|
55
|
+
valueLabel?: string;
|
|
56
|
+
/** The values by date (`YYYY-MM-DD`), in date order. */
|
|
57
|
+
points: readonly LineChartPoint[];
|
|
58
|
+
/**
|
|
59
|
+
* `linear` (the default) joins each point to the next; `step` holds a
|
|
60
|
+
* value until the next date, as a balance that changes on a day does.
|
|
61
|
+
*/
|
|
62
|
+
curve?: LineChartCurve;
|
|
63
|
+
/** Fills the area between the line and zero with a tint of its tone. */
|
|
64
|
+
area?: boolean;
|
|
65
|
+
/** The line's stroke tone (default `accent`). */
|
|
66
|
+
tone?: ChartStrokeTone;
|
|
67
|
+
/** Downward triangles on the line at some of its dates, with words. */
|
|
68
|
+
markers?: readonly LineChartMarker[];
|
|
69
|
+
/** Heads the table's column of marker words; required with markers. */
|
|
70
|
+
markersLabel?: string;
|
|
71
|
+
/** The dates labelled on the time axis; the first and last by default. */
|
|
72
|
+
ticks?: readonly string[];
|
|
73
|
+
/** A period to box, such as the current week. */
|
|
74
|
+
highlight?: LineChartPeriod;
|
|
75
|
+
/** Writes the last value beside the plot's end (default `true`). */
|
|
76
|
+
endLabel?: boolean;
|
|
77
|
+
/** Writes each figure in the table and the last value. */
|
|
78
|
+
formatValue?: (value: number) => string;
|
|
79
|
+
/** Writes the value axis; defaults to `formatValue`. */
|
|
80
|
+
formatTick?: (value: number) => string;
|
|
81
|
+
/** Writes a date for the time axis and the table; the date itself by default. */
|
|
82
|
+
formatDate?: (date: string) => string;
|
|
83
|
+
} & import("react").RefAttributes<HTMLDivElement>>;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { formatBarChartValue } from '@scalewing/tokens';
|
|
4
|
+
import { forwardRef, useImperativeHandle, useRef, } from 'react';
|
|
5
|
+
import { cx } from '../class-names.js';
|
|
6
|
+
import { assertLineChartInput, lineChartDay, } from '../line-chart-input.js';
|
|
7
|
+
import { lineChartLayout } from '../line-chart-layout.js';
|
|
8
|
+
import { lineChartRows } from '../line-chart-table.js';
|
|
9
|
+
import { lineChartCurves } from '../line-chart-path.js';
|
|
10
|
+
import { ChartTable } from './chart/ChartTable.js';
|
|
11
|
+
import { useChartWidth } from './chart/use-chart-width.js';
|
|
12
|
+
import { LineChartDrawing } from './line-chart/LineChartDrawing.js';
|
|
13
|
+
export { lineChartCurves };
|
|
14
|
+
const none = [];
|
|
15
|
+
const sameDate = (date) => date;
|
|
16
|
+
/**
|
|
17
|
+
* A value over time: a line through its points by date, straight or in
|
|
18
|
+
* steps, optionally over a tinted area, with markers on chosen dates, a
|
|
19
|
+
* boxed period and its last value beside the plot. It fills its
|
|
20
|
+
* container's width and is laid out for it, labelling fewer dates when
|
|
21
|
+
* they would meet, never scrolling sideways. The drawing is hidden from
|
|
22
|
+
* assistive technology; a visually hidden table named by `label` carries
|
|
23
|
+
* every date, value and marker. Pair it with a `ChartLegend` (`line` in its
|
|
24
|
+
* tone, `marker` in each marker's, `box` in `accent`), on the surface.
|
|
25
|
+
* Input it cannot draw throws a `RangeError`.
|
|
26
|
+
*/
|
|
27
|
+
export const LineChart = forwardRef(function LineChart({ area = false, className, curve = 'linear', endLabel = true, formatDate = sameDate, formatTick, formatValue = formatBarChartValue, highlight, label, markers = none, markersLabel, points, ticks, tone = 'accent', valueLabel, ...rest }, ref) {
|
|
28
|
+
assertLineChartInput({
|
|
29
|
+
label,
|
|
30
|
+
valueLabel,
|
|
31
|
+
points,
|
|
32
|
+
curve,
|
|
33
|
+
tone,
|
|
34
|
+
markers,
|
|
35
|
+
markersLabel,
|
|
36
|
+
ticks,
|
|
37
|
+
highlight,
|
|
38
|
+
});
|
|
39
|
+
const box = useRef(null);
|
|
40
|
+
useImperativeHandle(ref, () => box.current, []);
|
|
41
|
+
const width = useChartWidth(box);
|
|
42
|
+
const lastPoint = points.at(-1);
|
|
43
|
+
const tickDates = ticks ??
|
|
44
|
+
[...new Set([points[0]?.x, lastPoint?.x])].filter((date) => date !== undefined);
|
|
45
|
+
const layout = lineChartLayout({
|
|
46
|
+
width,
|
|
47
|
+
points: points.map((p) => ({ day: lineChartDay('x', p.x), y: p.y })),
|
|
48
|
+
curve,
|
|
49
|
+
area,
|
|
50
|
+
markers: markers.map((m) => ({
|
|
51
|
+
day: lineChartDay('x', m.x),
|
|
52
|
+
label: m.label,
|
|
53
|
+
})),
|
|
54
|
+
ticks: tickDates.map((date) => ({
|
|
55
|
+
day: lineChartDay('tick', date),
|
|
56
|
+
text: formatDate(date),
|
|
57
|
+
})),
|
|
58
|
+
highlight: highlight
|
|
59
|
+
? {
|
|
60
|
+
from: lineChartDay('from', highlight.from),
|
|
61
|
+
to: lineChartDay('to', highlight.to),
|
|
62
|
+
}
|
|
63
|
+
: null,
|
|
64
|
+
endLabel: endLabel && lastPoint ? formatValue(lastPoint.y) : null,
|
|
65
|
+
formatTick: formatTick ?? formatValue,
|
|
66
|
+
});
|
|
67
|
+
return (_jsxs("div", { ref: box, className: cx('sw-chart', className), ...rest, children: [_jsx(LineChartDrawing, { layout: layout, markerTones: markers.map((m) => m.tone), tone: tone }), _jsx(ChartTable, { caption: label, columns: [
|
|
68
|
+
valueLabel ?? label,
|
|
69
|
+
...(markers.length > 0 ? [markersLabel] : []),
|
|
70
|
+
], rows: lineChartRows({
|
|
71
|
+
formatDate,
|
|
72
|
+
formatValue,
|
|
73
|
+
highlight,
|
|
74
|
+
markers,
|
|
75
|
+
points,
|
|
76
|
+
}) })] }));
|
|
77
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type ChartTableRow } from '../../chart-table-rows.js';
|
|
2
|
+
export type ChartTableProps = {
|
|
3
|
+
caption: string;
|
|
4
|
+
/** The columns' headers, after the rows' own header column. */
|
|
5
|
+
columns: readonly string[];
|
|
6
|
+
rows: readonly ChartTableRow[];
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Every figure a chart draws, as a table kept for assistive technology and
|
|
10
|
+
* not drawn: it sits in a `sw-sr-only` box, because a table is never
|
|
11
|
+
* narrower than its contents and would widen the page. Its caption is the
|
|
12
|
+
* chart's name, with a header per column and per row, and the boxed
|
|
13
|
+
* period's row marked current. The drawing itself is hidden, so this is
|
|
14
|
+
* what a screen reader reads.
|
|
15
|
+
*/
|
|
16
|
+
export declare function ChartTable({ caption, columns, rows }: ChartTableProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* Every figure a chart draws, as a table kept for assistive technology and
|
|
4
|
+
* not drawn: it sits in a `sw-sr-only` box, because a table is never
|
|
5
|
+
* narrower than its contents and would widen the page. Its caption is the
|
|
6
|
+
* chart's name, with a header per column and per row, and the boxed
|
|
7
|
+
* period's row marked current. The drawing itself is hidden, so this is
|
|
8
|
+
* what a screen reader reads.
|
|
9
|
+
*/
|
|
10
|
+
export function ChartTable({ caption, columns, rows }) {
|
|
11
|
+
return (_jsx("div", { className: "sw-sr-only", children: _jsxs("table", { children: [_jsx("caption", { children: caption }), columns.length > 0 ? (_jsx("thead", { children: _jsxs("tr", { children: [_jsx("td", {}), columns.map((column, index) => (_jsx("th", { scope: "col", children: column }, `${column}-${index}`)))] }) })) : null, _jsx("tbody", { children: rows.map((row) => (_jsxs("tr", { "aria-current": row.current ? 'true' : undefined, children: [_jsx("th", { scope: "row", children: row.header }), row.cells.map((cell, index) => (_jsx("td", { children: cell }, index)))] }, row.key))) })] }) }));
|
|
12
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type ChartPlot, type ChartTick } from '../../chart-axis.js';
|
|
2
|
+
export type ChartValueAxisProps = {
|
|
3
|
+
plot: ChartPlot;
|
|
4
|
+
ticks: readonly ChartTick[];
|
|
5
|
+
/** The side its words sit on: the plot's start, or its end for a line's own scale. */
|
|
6
|
+
side: 'start' | 'end';
|
|
7
|
+
/** Draws the grid lines across the plot (the start axis does). */
|
|
8
|
+
grid?: boolean;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* A value axis inside a chart's SVG: a grid line at each tick across the
|
|
12
|
+
* plot, the zero line set apart, and each tick's words beside the plot.
|
|
13
|
+
*/
|
|
14
|
+
export declare function ChartValueAxis({ grid, plot, side, ticks, }: ChartValueAxisProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { chartGap } from '../../chart-metrics.js';
|
|
3
|
+
import { cx } from '../../class-names.js';
|
|
4
|
+
/**
|
|
5
|
+
* A value axis inside a chart's SVG: a grid line at each tick across the
|
|
6
|
+
* plot, the zero line set apart, and each tick's words beside the plot.
|
|
7
|
+
*/
|
|
8
|
+
export function ChartValueAxis({ grid = false, plot, side, ticks, }) {
|
|
9
|
+
const x = side === 'start' ? plot.left - chartGap : plot.right + chartGap;
|
|
10
|
+
return (_jsx("g", { children: ticks.map((tick) => (_jsxs("g", { children: [grid ? (_jsx("line", { className: tick.value === 0 ? 'sw-chart-zero' : 'sw-chart-grid', x1: plot.left, x2: plot.right, y1: tick.y, y2: tick.y })) : null, _jsx("text", { className: cx('sw-chart-tick', side === 'end' && 'sw-chart-tick-line'), dominantBaseline: "central", textAnchor: side === 'start' ? 'end' : 'start', x: x, y: tick.y, children: tick.label })] }, tick.value))) }));
|
|
11
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type RefObject } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Follows the width a chart is given, so its drawing is laid out in its
|
|
4
|
+
* own pixels: on mount, before the first paint, and whenever its box
|
|
5
|
+
* changes size. On the server it reports `chartFallbackWidth`, and a browser with
|
|
6
|
+
* no ResizeObserver measures on window resizes instead.
|
|
7
|
+
*/
|
|
8
|
+
export declare function useChartWidth(ref: RefObject<HTMLElement | null>): number;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { useLayoutEffect, useState } from 'react';
|
|
3
|
+
import { chartFallbackWidth } from '../../chart-metrics.js';
|
|
4
|
+
/**
|
|
5
|
+
* Follows the width a chart is given, so its drawing is laid out in its
|
|
6
|
+
* own pixels: on mount, before the first paint, and whenever its box
|
|
7
|
+
* changes size. On the server it reports `chartFallbackWidth`, and a browser with
|
|
8
|
+
* no ResizeObserver measures on window resizes instead.
|
|
9
|
+
*/
|
|
10
|
+
export function useChartWidth(ref) {
|
|
11
|
+
const [width, setWidth] = useState(chartFallbackWidth);
|
|
12
|
+
// Before paint, so a phone never shows the wide layout first.
|
|
13
|
+
useLayoutEffect(() => {
|
|
14
|
+
const box = ref.current;
|
|
15
|
+
if (!box)
|
|
16
|
+
return;
|
|
17
|
+
function measure() {
|
|
18
|
+
if (!box)
|
|
19
|
+
return;
|
|
20
|
+
const next = Math.round(box.getBoundingClientRect().width);
|
|
21
|
+
if (next > 0)
|
|
22
|
+
setWidth(next);
|
|
23
|
+
}
|
|
24
|
+
measure();
|
|
25
|
+
if (typeof ResizeObserver === 'undefined') {
|
|
26
|
+
window.addEventListener('resize', measure);
|
|
27
|
+
return () => window.removeEventListener('resize', measure);
|
|
28
|
+
}
|
|
29
|
+
const observer = new ResizeObserver(measure);
|
|
30
|
+
observer.observe(box);
|
|
31
|
+
return () => observer.disconnect();
|
|
32
|
+
}, [ref]);
|
|
33
|
+
return width;
|
|
34
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type CategoricalColor } from '@scalewing/tokens';
|
|
2
|
+
export type ChartCalloutListProps = {
|
|
3
|
+
callouts: readonly {
|
|
4
|
+
text: string;
|
|
5
|
+
tone?: CategoricalColor;
|
|
6
|
+
}[];
|
|
7
|
+
/** Shows the list under the plot; otherwise it is kept for assistive technology only. */
|
|
8
|
+
visible: boolean;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* A chart's callouts as a list of their words, each after a dot in its
|
|
12
|
+
* hue: shown under the plot on a compact chart, where they no longer fit
|
|
13
|
+
* above it, and otherwise kept for assistive technology (`sw-sr-only`)
|
|
14
|
+
* while the drawing pins them above their columns.
|
|
15
|
+
*/
|
|
16
|
+
export declare function ChartCalloutList({ callouts, visible }: ChartCalloutListProps): import("react").JSX.Element | null;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { cx } from '../../class-names.js';
|
|
3
|
+
/**
|
|
4
|
+
* A chart's callouts as a list of their words, each after a dot in its
|
|
5
|
+
* hue: shown under the plot on a compact chart, where they no longer fit
|
|
6
|
+
* above it, and otherwise kept for assistive technology (`sw-sr-only`)
|
|
7
|
+
* while the drawing pins them above their columns.
|
|
8
|
+
*/
|
|
9
|
+
export function ChartCalloutList({ callouts, visible }) {
|
|
10
|
+
if (callouts.length === 0)
|
|
11
|
+
return null;
|
|
12
|
+
return (_jsx("ul", { className: cx('sw-chart-callouts', !visible && 'sw-sr-only'), role: "list", children: callouts.map((callout, index) => (_jsxs("li", { className: cx('sw-chart-callout', callout.tone && `sw-chart-tone-${callout.tone}`), children: [_jsx("span", { "aria-hidden": "true", className: "sw-chart-callout-dot" }), callout.text] }, `${callout.text}-${index}`))) }));
|
|
13
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type CategoricalColor } from '@scalewing/tokens';
|
|
2
|
+
import { type ColumnChartLayout } from '../../column-chart-layout.js';
|
|
3
|
+
export type ColumnChartDrawingProps = {
|
|
4
|
+
layout: ColumnChartLayout;
|
|
5
|
+
/** Each series' hue, in series order. */
|
|
6
|
+
tones: readonly CategoricalColor[];
|
|
7
|
+
/** Each callout's hue, in callout order. */
|
|
8
|
+
calloutTones: readonly (CategoricalColor | undefined)[];
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* A column chart's picture, hidden from assistive technology (the table
|
|
12
|
+
* beside it carries every figure): the grid and value axes, the boxed
|
|
13
|
+
* period, the columns, the line and its last value, the category labels
|
|
14
|
+
* and the callouts with their leaders. It is laid out in its own pixels,
|
|
15
|
+
* left to right whatever the writing direction, as a time axis reads.
|
|
16
|
+
*/
|
|
17
|
+
export declare function ColumnChartDrawing({ calloutTones, layout, tones, }: ColumnChartDrawingProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { chartBoxRadius, chartColumnRadius, chartDotRadius, } from '../../chart-metrics.js';
|
|
3
|
+
import { cx } from '../../class-names.js';
|
|
4
|
+
import { ChartValueAxis } from '../chart/ChartValueAxis.js';
|
|
5
|
+
function toneClass(tone) {
|
|
6
|
+
return tone ? `sw-chart-tone-${tone}` : undefined;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* A column chart's picture, hidden from assistive technology (the table
|
|
10
|
+
* beside it carries every figure): the grid and value axes, the boxed
|
|
11
|
+
* period, the columns, the line and its last value, the category labels
|
|
12
|
+
* and the callouts with their leaders. It is laid out in its own pixels,
|
|
13
|
+
* left to right whatever the writing direction, as a time axis reads.
|
|
14
|
+
*/
|
|
15
|
+
export function ColumnChartDrawing({ calloutTones, layout, tones, }) {
|
|
16
|
+
const { plot } = layout;
|
|
17
|
+
return (_jsxs("svg", { "aria-hidden": "true", className: "sw-chart-svg", direction: "ltr", focusable: "false", height: layout.height, viewBox: `0 0 ${layout.width} ${layout.height}`, width: layout.width, children: [_jsx(ChartValueAxis, { grid: true, plot: plot, side: "start", ticks: layout.ticks }), layout.highlight ? (_jsx("rect", { className: "sw-chart-highlight sw-chart-tone-accent", height: layout.highlight.height, rx: chartBoxRadius, width: layout.highlight.width, x: layout.highlight.x, y: layout.highlight.y })) : null, layout.columns.map((column) => (_jsx("rect", { className: cx('sw-chart-column', toneClass(tones[column.series])), height: column.height, rx: Math.min(chartColumnRadius, column.width / 2, column.height / 2), width: column.width, x: column.x, y: column.y }, `${column.series}-${column.category}`))), layout.line ? (_jsxs("g", { className: "sw-chart-tone-accent", children: [_jsx("polyline", { className: "sw-chart-line", points: layout.line.points.map((p) => `${p.x},${p.y}`).join(' ') }), _jsx(ChartValueAxis, { plot: plot, side: "end", ticks: layout.lineTicks }), layout.line.end || layout.line.points.length === 1 ? (_jsx("circle", { className: "sw-chart-dot", cx: layout.line.points.at(-1).x, cy: layout.line.points.at(-1).y, r: chartDotRadius })) : null, layout.line.end ? (_jsx("text", { className: "sw-chart-end-label", dominantBaseline: "central", x: layout.line.end.x, y: layout.line.end.y, children: layout.line.end.text })) : null] })) : null, layout.categoryLabels.map((label) => (_jsx("text", { className: "sw-chart-category", textAnchor: "middle", x: label.x, y: label.y, children: label.text }, label.index))), layout.callouts.map((callout) => (_jsxs("g", { className: toneClass(calloutTones[callout.order]), children: [_jsx("line", { className: "sw-chart-leader", x1: callout.x, x2: callout.x, y1: callout.leaderStart, y2: callout.leaderEnd }), _jsx("text", { className: "sw-chart-callout-text", textAnchor: callout.anchor, x: callout.textX, y: callout.textY, children: callout.text })] }, callout.order)))] }));
|
|
18
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type ChartStrokeTone } from '../../css/chart-tones.js';
|
|
2
|
+
import { type LineChartLayout } from '../../line-chart-layout.js';
|
|
3
|
+
export type LineChartDrawingProps = {
|
|
4
|
+
layout: LineChartLayout;
|
|
5
|
+
tone: ChartStrokeTone;
|
|
6
|
+
/** Each marker's tone, in marker order. */
|
|
7
|
+
markerTones: readonly ChartStrokeTone[];
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* A line chart's picture, hidden from assistive technology (the table
|
|
11
|
+
* beside it carries every figure): the grid and value axis, the boxed
|
|
12
|
+
* period, the area and line in the line's tone with a dot and words at its
|
|
13
|
+
* last value, the date labels, and each marker's triangle and words in its
|
|
14
|
+
* own tone. It is laid out in its own pixels, left to right whatever the
|
|
15
|
+
* writing direction, as a time axis reads.
|
|
16
|
+
*/
|
|
17
|
+
export declare function LineChartDrawing({ layout, markerTones, tone, }: LineChartDrawingProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { chartBoxRadius, chartDotRadius } from '../../chart-metrics.js';
|
|
3
|
+
import { svgPoints } from '../../line-chart-path.js';
|
|
4
|
+
import { ChartValueAxis } from '../chart/ChartValueAxis.js';
|
|
5
|
+
/**
|
|
6
|
+
* A line chart's picture, hidden from assistive technology (the table
|
|
7
|
+
* beside it carries every figure): the grid and value axis, the boxed
|
|
8
|
+
* period, the area and line in the line's tone with a dot and words at its
|
|
9
|
+
* last value, the date labels, and each marker's triangle and words in its
|
|
10
|
+
* own tone. It is laid out in its own pixels, left to right whatever the
|
|
11
|
+
* writing direction, as a time axis reads.
|
|
12
|
+
*/
|
|
13
|
+
export function LineChartDrawing({ layout, markerTones, tone, }) {
|
|
14
|
+
const { last, plot } = layout;
|
|
15
|
+
return (_jsxs("svg", { "aria-hidden": "true", className: "sw-chart-svg", direction: "ltr", focusable: "false", height: layout.height, viewBox: `0 0 ${layout.width} ${layout.height}`, width: layout.width, children: [_jsx(ChartValueAxis, { grid: true, plot: plot, side: "start", ticks: layout.ticks }), layout.highlight ? (_jsx("rect", { className: "sw-chart-highlight sw-chart-tone-accent", height: layout.highlight.height, rx: chartBoxRadius, width: layout.highlight.width, x: layout.highlight.x, y: layout.highlight.y })) : null, _jsxs("g", { className: `sw-chart-tone-${tone}`, children: [layout.area.length > 0 ? (_jsx("polygon", { className: "sw-chart-area", points: svgPoints(layout.area) })) : null, _jsx("polyline", { className: "sw-chart-line", points: svgPoints(layout.path) }), last && (layout.end || layout.path.length === 1) ? (_jsx("circle", { className: "sw-chart-dot", cx: last.x, cy: last.y, r: chartDotRadius })) : null, layout.end ? (_jsx("text", { className: "sw-chart-end-label", dominantBaseline: "central", x: layout.end.x, y: layout.end.y, children: layout.end.text })) : null] }), layout.markers.map((marker) => (_jsxs("g", { className: `sw-chart-tone-${markerTones[marker.order]}`, children: [_jsx("polygon", { className: "sw-chart-marker", points: marker.triangle }), marker.labelShown ? (_jsx("text", { className: "sw-chart-marker-label", textAnchor: "middle", x: marker.labelX, y: marker.labelY, children: marker.label })) : null] }, marker.order))), layout.dateLabels.map((label) => (_jsx("text", { className: "sw-chart-category", textAnchor: "middle", x: label.x, y: label.y, children: label.text }, label.day)))] }));
|
|
16
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** A line's thickness (a chart's line and a legend's line swatch), in px. */
|
|
2
|
+
export declare const chartLineWidth = 2;
|
|
3
|
+
/** A hairline: a grid line, a leader, the boxed period's dashed edge. */
|
|
4
|
+
export declare const chartHairline = 1;
|
|
5
|
+
/** The boxed period's dashes: 3 px drawn, 3 px left out. */
|
|
6
|
+
export declare const chartBoxDash = "3 3";
|
|
7
|
+
/** A callout's leader: finer dashes, 2 px drawn, 2 px left out. */
|
|
8
|
+
export declare const chartLeaderDash = "2 2";
|
|
9
|
+
/** The gap between grouped columns side by side, at most. */
|
|
10
|
+
export declare const chartColumnGap = 2;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* The sizes every chart's marks share, so a legend swatch is drawn at the
|
|
3
|
+
* size of the mark it names. They are geometry, not theme values: the
|
|
4
|
+
* colours come from `chart-tones.ts` and the type from the caption variant.
|
|
5
|
+
*/
|
|
6
|
+
/** A line's thickness (a chart's line and a legend's line swatch), in px. */
|
|
7
|
+
export const chartLineWidth = 2;
|
|
8
|
+
/** A hairline: a grid line, a leader, the boxed period's dashed edge. */
|
|
9
|
+
export const chartHairline = 1;
|
|
10
|
+
/** The boxed period's dashes: 3 px drawn, 3 px left out. */
|
|
11
|
+
export const chartBoxDash = '3 3';
|
|
12
|
+
/** A callout's leader: finer dashes, 2 px drawn, 2 px left out. */
|
|
13
|
+
export const chartLeaderDash = '2 2';
|
|
14
|
+
/** The gap between grouped columns side by side, at most. */
|
|
15
|
+
export const chartColumnGap = chartLineWidth;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { type CategoricalColor } from '@scalewing/tokens';
|
|
2
|
+
/** Where a mark's colour comes from: a semantic or a categorical colour. */
|
|
3
|
+
export type ChartColorSource = {
|
|
4
|
+
semantic: 'accent' | 'success' | 'warning' | 'danger' | 'muted' | 'text';
|
|
5
|
+
} | {
|
|
6
|
+
categorical: CategoricalColor;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* A colour mixed into the surface or toward the text colour by a share of
|
|
10
|
+
* its own (1 is the colour unmixed).
|
|
11
|
+
*/
|
|
12
|
+
export type ChartColorMix = ChartColorSource & {
|
|
13
|
+
share: number;
|
|
14
|
+
into: 'surface' | 'text';
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* The statement tones a range bar's fill takes (see DataBar). `danger` is
|
|
18
|
+
* for a shortage, never for cash leaving: an outflow is `muted`.
|
|
19
|
+
*/
|
|
20
|
+
export declare const statementFillTones: readonly ["neutral", "success", "muted", "strong", "accent", "danger"];
|
|
21
|
+
/**
|
|
22
|
+
* Every fill tone: the categorical colours (a chart's series), then the
|
|
23
|
+
* statement tones a range bar adds. `neutral` is in both and is one grey.
|
|
24
|
+
*/
|
|
25
|
+
export declare const chartFillTones: readonly ("danger" | "muted" | "accent" | "success" | CategoricalColor | "strong")[];
|
|
26
|
+
export type ChartFillTone = CategoricalColor | (typeof statementFillTones)[number];
|
|
27
|
+
/** The tones a line, a marker or the boxed period is drawn in. */
|
|
28
|
+
export declare const chartStrokeTones: readonly ["accent", "success", "warning", "danger", "neutral"];
|
|
29
|
+
export type ChartStrokeTone = (typeof chartStrokeTones)[number];
|
|
30
|
+
/** Every tone a chart or a legend swatch can take. */
|
|
31
|
+
export type ChartTone = ChartFillTone | ChartStrokeTone;
|
|
32
|
+
export declare const chartTones: readonly ChartTone[];
|
|
33
|
+
/**
|
|
34
|
+
* How much of a categorical colour a column's fill mixes into the surface:
|
|
35
|
+
* a lighter fill, so a line drawn over the columns stands apart, that
|
|
36
|
+
* keeps 1.5:1 on the surface in every palette and scheme.
|
|
37
|
+
*/
|
|
38
|
+
export declare const categoricalFillShare = 0.72;
|
|
39
|
+
/**
|
|
40
|
+
* How much of the danger colour its fill mixes into the surface (a
|
|
41
|
+
* shortage's range bar, never cash leaving): a light red, at 1.5:1 or more
|
|
42
|
+
* on the surface in every palette and scheme, as every other fill.
|
|
43
|
+
*/
|
|
44
|
+
export declare const dangerFillShare = 0.4;
|
|
45
|
+
/** Each fill tone's colour, mixed into the surface. */
|
|
46
|
+
export declare const chartFillMixes: Record<ChartFillTone, ChartColorMix>;
|
|
47
|
+
/** Each stroke tone's colour: a semantic colour, or muted for neutral. */
|
|
48
|
+
export declare const chartStrokeColors: Record<ChartStrokeTone, ChartColorSource>;
|
|
49
|
+
/**
|
|
50
|
+
* How much of a line's stroke the area under it takes, over transparency:
|
|
51
|
+
* a quiet tint behind the line, decorative (the line carries the values).
|
|
52
|
+
*/
|
|
53
|
+
export declare const chartAreaShare = 0.12;
|
|
54
|
+
/** How much of a hue's colour its words keep, moved toward the text colour. */
|
|
55
|
+
export declare const categoricalInkShare = 0.3;
|
|
56
|
+
/** How much of a semantic colour its words keep, moved toward the text colour. */
|
|
57
|
+
export declare const semanticInkShare = 0.5;
|
|
58
|
+
/** The tones whose words are drawn beside a mark: a series' and a stroke's. */
|
|
59
|
+
export declare const chartInkTones: readonly ["green", "blue", "orange", "purple", "teal", "rose", "accent", "success", "warning", "danger", "neutral"];
|
|
60
|
+
export type ChartInkTone = (typeof chartInkTones)[number];
|
|
61
|
+
/** The words beside a mark: its tone moved toward the text colour. */
|
|
62
|
+
export declare const chartInkMixes: Record<ChartInkTone, ChartColorMix>;
|
|
63
|
+
/** A mix as CSS: the colour alone when unmixed. */
|
|
64
|
+
export declare function chartColorMix(mix: ChartColorMix): string;
|
|
65
|
+
/** A fill tone's colour. */
|
|
66
|
+
export declare function chartFill(tone: ChartFillTone): string;
|
|
67
|
+
export declare function cssChartToneClasses(): string;
|
|
68
|
+
export declare function chartToneClassCatalog(): string[];
|