@scalewing/react 1.37.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.
Files changed (117) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +91 -1
  3. package/dist/accessible-name.d.ts +13 -0
  4. package/dist/accessible-name.js +1 -0
  5. package/dist/chart-axis.d.ts +61 -0
  6. package/dist/chart-axis.js +83 -0
  7. package/dist/chart-dates.d.ts +6 -0
  8. package/dist/chart-dates.js +15 -0
  9. package/dist/chart-metrics.d.ts +40 -0
  10. package/dist/chart-metrics.js +48 -0
  11. package/dist/chart-scale.d.ts +48 -0
  12. package/dist/chart-scale.js +116 -0
  13. package/dist/chart-table-rows.d.ts +8 -0
  14. package/dist/chart-table-rows.js +1 -0
  15. package/dist/column-chart-bars.d.ts +38 -0
  16. package/dist/column-chart-bars.js +82 -0
  17. package/dist/column-chart-callouts.d.ts +48 -0
  18. package/dist/column-chart-callouts.js +54 -0
  19. package/dist/column-chart-input.d.ts +40 -0
  20. package/dist/column-chart-input.js +60 -0
  21. package/dist/column-chart-layout.d.ts +63 -0
  22. package/dist/column-chart-layout.js +97 -0
  23. package/dist/components/BarChart.d.ts +1 -1
  24. package/dist/components/BarChart.js +1 -1
  25. package/dist/components/ChartLegend.d.ts +38 -0
  26. package/dist/components/ChartLegend.js +32 -0
  27. package/dist/components/ColumnChart.d.ts +72 -0
  28. package/dist/components/ColumnChart.js +71 -0
  29. package/dist/components/DataBar.d.ts +48 -0
  30. package/dist/components/DataBar.js +28 -0
  31. package/dist/components/LineChart.d.ts +83 -0
  32. package/dist/components/LineChart.js +77 -0
  33. package/dist/components/Pagination.d.ts +34 -0
  34. package/dist/components/Pagination.js +27 -0
  35. package/dist/components/ScrollRegion.d.ts +9 -5
  36. package/dist/components/ScrollRegion.js +8 -6
  37. package/dist/components/SectionNav.d.ts +2 -8
  38. package/dist/components/SegmentedControl.d.ts +2 -8
  39. package/dist/components/Table.d.ts +48 -4
  40. package/dist/components/Table.js +9 -3
  41. package/dist/components/Tabs.d.ts +2 -9
  42. package/dist/components/chart/ChartTable.d.ts +16 -0
  43. package/dist/components/chart/ChartTable.js +12 -0
  44. package/dist/components/chart/ChartValueAxis.d.ts +14 -0
  45. package/dist/components/chart/ChartValueAxis.js +11 -0
  46. package/dist/components/chart/use-chart-width.d.ts +8 -0
  47. package/dist/components/chart/use-chart-width.js +34 -0
  48. package/dist/components/column-chart/ChartCalloutList.d.ts +16 -0
  49. package/dist/components/column-chart/ChartCalloutList.js +13 -0
  50. package/dist/components/column-chart/ColumnChartDrawing.d.ts +17 -0
  51. package/dist/components/column-chart/ColumnChartDrawing.js +18 -0
  52. package/dist/components/line-chart/LineChartDrawing.d.ts +17 -0
  53. package/dist/components/line-chart/LineChartDrawing.js +16 -0
  54. package/dist/components/table/TableRow.d.ts +8 -2
  55. package/dist/components/table/TableRow.js +1 -1
  56. package/dist/components/table/table-max-height.d.ts +20 -0
  57. package/dist/components/table/table-max-height.js +23 -0
  58. package/dist/components/use-scroll-overflow.d.ts +10 -8
  59. package/dist/components/use-scroll-overflow.js +23 -10
  60. package/dist/css/chart-marks.d.ts +10 -0
  61. package/dist/css/chart-marks.js +15 -0
  62. package/dist/css/chart-tones.d.ts +68 -0
  63. package/dist/css/chart-tones.js +130 -0
  64. package/dist/css/chevron.d.ts +6 -3
  65. package/dist/css/chevron.js +9 -4
  66. package/dist/css/{css-chart.d.ts → css-bar-chart.d.ts} +2 -2
  67. package/dist/css/{css-chart.js → css-bar-chart.js} +2 -2
  68. package/dist/css/css-chart-legend.d.ts +5 -0
  69. package/dist/css/css-chart-legend.js +102 -0
  70. package/dist/css/css-chart-plot.d.ts +2 -0
  71. package/dist/css/css-chart-plot.js +170 -0
  72. package/dist/css/css-components.js +20 -2
  73. package/dist/css/css-data-bar.d.ts +11 -0
  74. package/dist/css/css-data-bar.js +91 -0
  75. package/dist/css/css-line-chart.d.ts +2 -0
  76. package/dist/css/css-line-chart.js +31 -0
  77. package/dist/css/css-pagination.d.ts +2 -0
  78. package/dist/css/css-pagination.js +63 -0
  79. package/dist/css/css-scroll-region.d.ts +1 -0
  80. package/dist/css/css-scroll-region.js +10 -5
  81. package/dist/css/css-table-bounded-edges.d.ts +1 -0
  82. package/dist/css/css-table-bounded-edges.js +132 -0
  83. package/dist/css/css-table-bounded.d.ts +2 -0
  84. package/dist/css/css-table-bounded.js +73 -0
  85. package/dist/css/css-table-ledger.d.ts +5 -2
  86. package/dist/css/css-table-ledger.js +5 -2
  87. package/dist/css/css-table-metrics.d.ts +14 -0
  88. package/dist/css/css-table-metrics.js +13 -0
  89. package/dist/css/css-table-sort.js +2 -1
  90. package/dist/css/css-table-total-row.d.ts +2 -0
  91. package/dist/css/css-table-total-row.js +29 -0
  92. package/dist/css/css-table.js +14 -3
  93. package/dist/css/stylesheet.js +14 -2
  94. package/dist/data-bar-geometry.d.ts +24 -0
  95. package/dist/data-bar-geometry.js +41 -0
  96. package/dist/index.d.ts +6 -1
  97. package/dist/index.js +5 -0
  98. package/dist/is-one-of.d.ts +5 -0
  99. package/dist/is-one-of.js +7 -0
  100. package/dist/line-chart-input.d.ts +39 -0
  101. package/dist/line-chart-input.js +70 -0
  102. package/dist/line-chart-layout.d.ts +68 -0
  103. package/dist/line-chart-layout.js +99 -0
  104. package/dist/line-chart-markers.d.ts +29 -0
  105. package/dist/line-chart-markers.js +57 -0
  106. package/dist/line-chart-path.d.ts +18 -0
  107. package/dist/line-chart-path.js +33 -0
  108. package/dist/line-chart-points.d.ts +6 -0
  109. package/dist/line-chart-points.js +8 -0
  110. package/dist/line-chart-table.d.ts +15 -0
  111. package/dist/line-chart-table.js +29 -0
  112. package/dist/pagination-pages.d.ts +13 -0
  113. package/dist/pagination-pages.js +22 -0
  114. package/dist/scroll-overflow.d.ts +12 -0
  115. package/dist/scroll-overflow.js +31 -7
  116. package/dist/styles.css +442 -0
  117. package/package.json +1 -1
@@ -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
+ }
@@ -7,7 +7,10 @@ export type TableRowProps = HTMLAttributes<HTMLTableRowElement> & {
7
7
  * `sub` sets the row under the one above it, as an item of a batch: its
8
8
  * row header (`TableCell as="th" scope="row"`) is indented with a guide
9
9
  * bar at the indent; a sub row without a row header is not indented.
10
- * `default` is a row of its own.
10
+ * `total` is a total mid-table, such as a subtotal the rows above add up
11
+ * to: its cells at weight 600 under a 2 px rule, as a
12
+ * `TableFooter` total is; name it with a row header and keep it in the
13
+ * body. `default` is a row of its own.
11
14
  */
12
15
  variant?: TableRowVariant;
13
16
  /**
@@ -40,7 +43,10 @@ export declare const TableRow: import("react").ForwardRefExoticComponent<HTMLAtt
40
43
  * `sub` sets the row under the one above it, as an item of a batch: its
41
44
  * row header (`TableCell as="th" scope="row"`) is indented with a guide
42
45
  * bar at the indent; a sub row without a row header is not indented.
43
- * `default` is a row of its own.
46
+ * `total` is a total mid-table, such as a subtotal the rows above add up
47
+ * to: its cells at weight 600 under a 2 px rule, as a
48
+ * `TableFooter` total is; name it with a row header and keep it in the
49
+ * body. `default` is a row of its own.
44
50
  */
45
51
  variant?: TableRowVariant;
46
52
  /**
@@ -25,5 +25,5 @@ export const TableRow = forwardRef(function TableRow({ className, onClick, press
25
25
  forwarding.current = false;
26
26
  }
27
27
  };
28
- return (_jsx("tr", { ref: ref, "aria-selected": selected || undefined, className: cx(selected && 'sw-table-row-selected', variant === 'sub' && 'sw-table-row-sub', struck && 'sw-table-row-struck', pressable && 'sw-table-row-press', className), onClick: pressable || onClick ? press : undefined, ...rest }));
28
+ return (_jsx("tr", { ref: ref, "aria-selected": selected || undefined, className: cx(selected && 'sw-table-row-selected', variant === 'sub' && 'sw-table-row-sub', variant === 'total' && 'sw-table-row-total', struck && 'sw-table-row-struck', pressable && 'sw-table-row-press', className), onClick: pressable || onClick ? press : undefined, ...rest }));
29
29
  });
@@ -0,0 +1,20 @@
1
+ import type { CSSProperties } from 'react';
2
+ import type { TableStickyHeader } from './table-layout.js';
3
+ /**
4
+ * A table's bounded height, counted in rows: the table's region is as tall
5
+ * as `rows` single-line body rows plus its header row and, when it has one,
6
+ * its footer row, and the rest of the rows scroll inside the region. Rows
7
+ * taller than one line show fewer at a time.
8
+ */
9
+ export type TableMaxHeight = {
10
+ rows: number;
11
+ };
12
+ /**
13
+ * A bounded table pins its header to its own region (`stickyHeader` true,
14
+ * the default); `'page'` and `false` contradict that and throw, as do a
15
+ * value that is not `{ rows }` and a row count that is not a whole number
16
+ * from 1.
17
+ */
18
+ export declare function assertTableMaxHeight(maxHeight: TableMaxHeight | undefined, stickyHeader: TableStickyHeader): void;
19
+ /** The row count the generated `sw-table-bounded` height reads. */
20
+ export declare function tableMaxHeightStyle(maxHeight: TableMaxHeight | undefined): CSSProperties | undefined;
@@ -0,0 +1,23 @@
1
+ /**
2
+ * A bounded table pins its header to its own region (`stickyHeader` true,
3
+ * the default); `'page'` and `false` contradict that and throw, as do a
4
+ * value that is not `{ rows }` and a row count that is not a whole number
5
+ * from 1.
6
+ */
7
+ export function assertTableMaxHeight(maxHeight, stickyHeader) {
8
+ if (maxHeight === undefined)
9
+ return;
10
+ if (typeof maxHeight !== 'object' || maxHeight === null)
11
+ throw new RangeError(`Table maxHeight must be a row count, { rows }: ${String(maxHeight)}`);
12
+ const rows = maxHeight.rows;
13
+ if (typeof rows !== 'number' || !Number.isInteger(rows) || rows < 1)
14
+ throw new RangeError(`Table maxHeight rows must be a whole number from 1: ${String(rows)}`);
15
+ if (stickyHeader !== true)
16
+ throw new RangeError(`Table maxHeight pins the header to the table's own region; stickyHeader ${String(stickyHeader)} cannot be used with it`);
17
+ }
18
+ /** The row count the generated `sw-table-bounded` height reads. */
19
+ export function tableMaxHeightStyle(maxHeight) {
20
+ if (maxHeight === undefined)
21
+ return undefined;
22
+ return { '--sw-table-max-rows': maxHeight.rows };
23
+ }
@@ -1,14 +1,16 @@
1
1
  import { type RefObject } from 'react';
2
- import { type ScrollOverflow } from '../scroll-overflow.js';
3
- /** A box's overflow, and whether it has been measured yet. */
4
- export type MeasuredScrollOverflow = ScrollOverflow & {
2
+ import { type BlockScrollOverflow, type ScrollOverflow } from '../scroll-overflow.js';
3
+ /** A box's overflow on both axes, and whether it has been measured yet. */
4
+ export type MeasuredScrollOverflow = ScrollOverflow & BlockScrollOverflow & {
5
5
  measured: boolean;
6
6
  };
7
7
  /**
8
- * Follows which inline edges of a scroll box have more content past them:
9
- * on mount, on every scroll, and whenever the box or its content changes
10
- * size. Before mount (and on the server) it reports no overflow and
11
- * `measured: false`, so the box renders as it always has until it is
12
- * measured; a measured box with neither edge set fits its content.
8
+ * Follows which edges of a scroll box have more content past them, the
9
+ * inline start and end and the top and bottom: on mount, on every scroll,
10
+ * and whenever the box or its content changes size. Before mount (and on
11
+ * the server) it reports no overflow and `measured: false`, so the box
12
+ * renders as it always has until it is measured; a measured box with
13
+ * neither inline edge set fits its content's width. A box whose height
14
+ * follows its content never reports a block edge.
13
15
  */
14
16
  export declare function useScrollOverflow(ref: RefObject<HTMLElement | null>): MeasuredScrollOverflow;
@@ -1,16 +1,31 @@
1
1
  'use client';
2
2
  import { useEffect, useState } from 'react';
3
- import { noScrollOverflow, sameScrollOverflow, scrollOverflow, } from '../scroll-overflow.js';
3
+ import { blockScrollOverflow, noBlockScrollOverflow, noScrollOverflow, sameBlockScrollOverflow, sameScrollOverflow, scrollOverflow, } from '../scroll-overflow.js';
4
4
  const unmeasured = {
5
5
  ...noScrollOverflow,
6
+ ...noBlockScrollOverflow,
6
7
  measured: false,
7
8
  };
9
+ function measureBox(box) {
10
+ return {
11
+ ...scrollOverflow(box.scrollLeft, box.scrollWidth, box.clientWidth),
12
+ ...blockScrollOverflow(box.scrollTop, box.scrollHeight, box.clientHeight),
13
+ measured: true,
14
+ };
15
+ }
16
+ function sameMeasure(current, next) {
17
+ return (current.measured &&
18
+ sameScrollOverflow(current, next) &&
19
+ sameBlockScrollOverflow(current, next));
20
+ }
8
21
  /**
9
- * Follows which inline edges of a scroll box have more content past them:
10
- * on mount, on every scroll, and whenever the box or its content changes
11
- * size. Before mount (and on the server) it reports no overflow and
12
- * `measured: false`, so the box renders as it always has until it is
13
- * measured; a measured box with neither edge set fits its content.
22
+ * Follows which edges of a scroll box have more content past them, the
23
+ * inline start and end and the top and bottom: on mount, on every scroll,
24
+ * and whenever the box or its content changes size. Before mount (and on
25
+ * the server) it reports no overflow and `measured: false`, so the box
26
+ * renders as it always has until it is measured; a measured box with
27
+ * neither inline edge set fits its content's width. A box whose height
28
+ * follows its content never reports a block edge.
14
29
  */
15
30
  export function useScrollOverflow(ref) {
16
31
  const [overflow, setOverflow] = useState(unmeasured);
@@ -25,10 +40,8 @@ export function useScrollOverflow(ref) {
25
40
  function measure() {
26
41
  if (!box)
27
42
  return;
28
- const next = scrollOverflow(box.scrollLeft, box.scrollWidth, box.clientWidth);
29
- setOverflow((current) => current.measured && sameScrollOverflow(current, next)
30
- ? current
31
- : { ...next, measured: true });
43
+ const next = measureBox(box);
44
+ setOverflow((current) => (sameMeasure(current, next) ? current : next));
32
45
  }
33
46
  measure();
34
47
  box.addEventListener('scroll', measure, { passive: true });
@@ -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[];
@@ -0,0 +1,130 @@
1
+ import { categoricalColorKeys, categoricalHues, } from '@scalewing/tokens';
2
+ import { isOneOf } from '../is-one-of.js';
3
+ /** A source as its custom property. */
4
+ function sourceProperty(source) {
5
+ return 'semantic' in source
6
+ ? `var(--sw-color-${source.semantic})`
7
+ : `var(--sw-categorical-${source.categorical})`;
8
+ }
9
+ /**
10
+ * The statement tones a range bar's fill takes (see DataBar). `danger` is
11
+ * for a shortage, never for cash leaving: an outflow is `muted`.
12
+ */
13
+ export const statementFillTones = [
14
+ 'neutral',
15
+ 'success',
16
+ 'muted',
17
+ 'strong',
18
+ 'accent',
19
+ 'danger',
20
+ ];
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 const chartFillTones = [
26
+ ...categoricalColorKeys,
27
+ ...statementFillTones.filter((tone) => tone !== 'neutral'),
28
+ ];
29
+ /** The tones a line, a marker or the boxed period is drawn in. */
30
+ export const chartStrokeTones = [
31
+ 'accent',
32
+ 'success',
33
+ 'warning',
34
+ 'danger',
35
+ 'neutral',
36
+ ];
37
+ export const chartTones = [
38
+ ...new Set([...chartFillTones, ...chartStrokeTones]),
39
+ ];
40
+ /**
41
+ * How much of a categorical colour a column's fill mixes into the surface:
42
+ * a lighter fill, so a line drawn over the columns stands apart, that
43
+ * keeps 1.5:1 on the surface in every palette and scheme.
44
+ */
45
+ export const categoricalFillShare = 0.72;
46
+ /**
47
+ * How much of the danger colour its fill mixes into the surface (a
48
+ * shortage's range bar, never cash leaving): a light red, at 1.5:1 or more
49
+ * on the surface in every palette and scheme, as every other fill.
50
+ */
51
+ export const dangerFillShare = 0.4;
52
+ /** Each fill tone's colour, mixed into the surface. */
53
+ export const chartFillMixes = {
54
+ ...Object.fromEntries(categoricalHues.map((hue) => [
55
+ hue,
56
+ { categorical: hue, share: categoricalFillShare, into: 'surface' },
57
+ ])),
58
+ neutral: { semantic: 'muted', share: 0.65, into: 'surface' },
59
+ success: { semantic: 'success', share: 0.55, into: 'surface' },
60
+ muted: { semantic: 'muted', share: 0.35, into: 'surface' },
61
+ strong: { semantic: 'text', share: 0.85, into: 'surface' },
62
+ accent: { semantic: 'accent', share: 1, into: 'surface' },
63
+ danger: { semantic: 'danger', share: dangerFillShare, into: 'surface' },
64
+ };
65
+ /** Each stroke tone's colour: a semantic colour, or muted for neutral. */
66
+ export const chartStrokeColors = {
67
+ accent: { semantic: 'accent' },
68
+ success: { semantic: 'success' },
69
+ warning: { semantic: 'warning' },
70
+ danger: { semantic: 'danger' },
71
+ neutral: { semantic: 'muted' },
72
+ };
73
+ /**
74
+ * How much of a line's stroke the area under it takes, over transparency:
75
+ * a quiet tint behind the line, decorative (the line carries the values).
76
+ */
77
+ export const chartAreaShare = 0.12;
78
+ /** How much of a hue's colour its words keep, moved toward the text colour. */
79
+ export const categoricalInkShare = 0.3;
80
+ /** How much of a semantic colour its words keep, moved toward the text colour. */
81
+ export const semanticInkShare = 0.5;
82
+ /** The tones whose words are drawn beside a mark: a series' and a stroke's. */
83
+ export const chartInkTones = [...categoricalHues, ...chartStrokeTones];
84
+ /** The words beside a mark: its tone moved toward the text colour. */
85
+ export const chartInkMixes = {
86
+ ...Object.fromEntries(categoricalHues.map((hue) => [
87
+ hue,
88
+ { categorical: hue, share: categoricalInkShare, into: 'text' },
89
+ ])),
90
+ accent: { semantic: 'accent', share: semanticInkShare, into: 'text' },
91
+ success: { semantic: 'success', share: semanticInkShare, into: 'text' },
92
+ warning: { semantic: 'warning', share: semanticInkShare, into: 'text' },
93
+ danger: { semantic: 'danger', share: semanticInkShare, into: 'text' },
94
+ neutral: { semantic: 'muted', share: 1, into: 'text' },
95
+ };
96
+ /** A mix as CSS: the colour alone when unmixed. */
97
+ export function chartColorMix(mix) {
98
+ const color = sourceProperty(mix);
99
+ if (mix.share === 1)
100
+ return color;
101
+ return `color-mix(in srgb, ${color} ${Math.round(mix.share * 100)}%, var(--sw-color-${mix.into}))`;
102
+ }
103
+ /** A fill tone's colour. */
104
+ export function chartFill(tone) {
105
+ return chartColorMix(chartFillMixes[tone]);
106
+ }
107
+ /*
108
+ * A tone class hands its roles to the marks inside it as custom
109
+ * properties, `--sw-chart-fill`, `--sw-chart-stroke` and `--sw-chart-ink`,
110
+ * so a legend swatch, a column, a line and a label each read the role it
111
+ * draws. A tone sets only the roles it has.
112
+ */
113
+ function toneDeclarations(tone) {
114
+ const declarations = [];
115
+ if (isOneOf(chartFillTones, tone))
116
+ declarations.push(`--sw-chart-fill: ${chartFill(tone)};`);
117
+ if (isOneOf(chartStrokeTones, tone))
118
+ declarations.push(`--sw-chart-stroke: ${sourceProperty(chartStrokeColors[tone])};`);
119
+ if (isOneOf(chartInkTones, tone))
120
+ declarations.push(`--sw-chart-ink: ${chartColorMix(chartInkMixes[tone])};`);
121
+ return declarations.join(' ');
122
+ }
123
+ export function cssChartToneClasses() {
124
+ return chartTones
125
+ .map((tone) => `.sw-chart-tone-${tone} { ${toneDeclarations(tone)} }`)
126
+ .join('\n');
127
+ }
128
+ export function chartToneClassCatalog() {
129
+ return chartTones.map((tone) => `sw-chart-tone-${tone}`);
130
+ }
@@ -1,5 +1,8 @@
1
1
  /**
2
- * The stroked chevron Accordion and Select share: two muted sides of a
3
- * `space-2` square, turned by each surface to point where it needs.
2
+ * A stroked chevron in `color`: two sides of a `space-2` square (its
3
+ * bottom and right borders, so forced colours keep it), turned by each
4
+ * surface to point where it needs.
4
5
  */
5
- export declare const chevronStroke = "border-bottom: 2px solid var(--sw-color-muted);\n border-right: 2px solid var(--sw-color-muted);\n box-sizing: border-box;\n height: var(--sw-space-2);\n width: var(--sw-space-2);";
6
+ export declare function chevronStrokeIn(color: string): string;
7
+ /** The muted chevron Accordion, Select and the menus share. */
8
+ export declare const chevronStroke: string;
@@ -1,9 +1,14 @@
1
1
  /**
2
- * The stroked chevron Accordion and Select share: two muted sides of a
3
- * `space-2` square, turned by each surface to point where it needs.
2
+ * A stroked chevron in `color`: two sides of a `space-2` square (its
3
+ * bottom and right borders, so forced colours keep it), turned by each
4
+ * surface to point where it needs.
4
5
  */
5
- export const chevronStroke = `border-bottom: 2px solid var(--sw-color-muted);
6
- border-right: 2px solid var(--sw-color-muted);
6
+ export function chevronStrokeIn(color) {
7
+ return `border-bottom: 2px solid ${color};
8
+ border-right: 2px solid ${color};
7
9
  box-sizing: border-box;
8
10
  height: var(--sw-space-2);
9
11
  width: var(--sw-space-2);`;
12
+ }
13
+ /** The muted chevron Accordion, Select and the menus share. */
14
+ export const chevronStroke = chevronStrokeIn('var(--sw-color-muted)');
@@ -5,5 +5,5 @@
5
5
  */
6
6
  export declare const barChartTones: readonly ["accent", "success", "warning", "danger"];
7
7
  export type BarChartTone = (typeof barChartTones)[number];
8
- export declare function cssChartClasses(): string;
9
- export declare function chartClassCatalog(): string[];
8
+ export declare function cssBarChartClasses(): string;
9
+ export declare function barChartClassCatalog(): string[];