@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,132 @@
1
+ import { scrollShadeColor } from './css-scroll-region.js';
2
+ const region = '.sw-table-bounded';
3
+ /*
4
+ * While rows are scrolled out past the top or bottom of a bounded region
5
+ * (`sw-scroll-more-top`, `sw-scroll-more-bottom`), that edge draws the
6
+ * scroll shade, a spacing step 5 deep, in the colour of the inline edges'
7
+ * shade. A pinned row covers the region's own edge, so the shade is cast
8
+ * from the pinned row instead: the header's last row extends its rule's
9
+ * `::before` downward into a gradient, and a sticky footer's first row
10
+ * extends its 2 px rule's `::before` upward into one. Both are drawn
11
+ * inside their pinned group's layer, over the rows that scroll under
12
+ * them, and no cell moves. Only the header's last row casts it, so a
13
+ * header cell that spans rows (`rowSpan`) from an earlier row casts none
14
+ * under its column. An edge with nothing pinned on it, such as a bounded
15
+ * table without a sticky footer, shades the region's own edge with a
16
+ * background layer fixed to the box, under the rows as the inline shade
17
+ * is. Forced colours draw no shade; the system scrollbar is the cue.
18
+ */
19
+ function shadeRules() {
20
+ const shade = (direction) => `linear-gradient(to ${direction}, ${scrollShadeColor}, transparent)`;
21
+ const headCells = `${region}.sw-scroll-more-top > .sw-table-sticky > thead > tr:last-child > :is(th, td)::before`;
22
+ const footCells = `${region}.sw-scroll-more-bottom > .sw-table > .sw-table-footer-sticky > tr:first-child > :is(th, td)::before`;
23
+ const noHead = ':not(:has(> .sw-table > thead))';
24
+ const noPinnedFoot = ':not(:has(> .sw-table > .sw-table-footer-sticky))';
25
+ const ownTop = `${region}.sw-scroll-more-top${noHead}`;
26
+ const ownBottom = `${region}.sw-scroll-more-bottom${noPinnedFoot}`;
27
+ const ownBoth = `${region}.sw-scroll-more-top.sw-scroll-more-bottom${noHead}${noPinnedFoot}`;
28
+ return `${headCells} {
29
+ background: ${shade('bottom')};
30
+ border-bottom: 0;
31
+ border-top: 1px solid var(--sw-color-border);
32
+ box-sizing: border-box;
33
+ height: var(--sw-space-5);
34
+ inset-block-end: auto;
35
+ inset-block-start: calc(100% - 1px);
36
+ }
37
+
38
+ ${footCells} {
39
+ background: ${shade('top')};
40
+ border-bottom: 2px solid var(--sw-color-border);
41
+ border-top: 0;
42
+ box-sizing: border-box;
43
+ height: var(--sw-space-5);
44
+ inset-block-end: calc(100% - 1px);
45
+ inset-block-start: auto;
46
+ }
47
+
48
+ ${ownTop},
49
+ ${ownBottom} {
50
+ background-repeat: no-repeat;
51
+ background-size: 100% var(--sw-space-5);
52
+ }
53
+
54
+ ${ownTop} {
55
+ background-image: ${shade('bottom')};
56
+ background-position: top;
57
+ }
58
+
59
+ ${ownBottom} {
60
+ background-image: ${shade('top')};
61
+ background-position: bottom;
62
+ }
63
+
64
+ ${ownBoth} {
65
+ background-image: ${shade('bottom')}, ${shade('top')};
66
+ background-position: top, bottom;
67
+ }
68
+
69
+ @media (forced-colors: active) {
70
+ ${headCells},
71
+ ${footCells} { background: none; border-color: CanvasText; }
72
+ ${ownTop},
73
+ ${ownBottom},
74
+ ${ownBoth} { background-image: none; }
75
+ }`;
76
+ }
77
+ /*
78
+ * The region's focus ring is its own outline, drawn inside its edge, and
79
+ * the pinned header and sticky footer paint over it. While the region has
80
+ * keyboard focus, those pinned cells carry the ring's part they cover: an
81
+ * inset line at the top of the header's first row and the bottom of the
82
+ * footer's last row, and at the outer side of each pinned row's first and
83
+ * last cell (mirrored right to left). The parts are custom properties set
84
+ * on the cell they belong to and joined in one shadow, so a corner cell
85
+ * carries two. The side parts sit on the first and last cells, so they
86
+ * show while the region is scrolled to that inline edge, and a header
87
+ * cell spanning rows (`rowSpan`) puts a later row's first cell off the
88
+ * edge, where its side part would land mid-header. Forced colours drop
89
+ * shadows; the outline shows beside the rows there.
90
+ */
91
+ function focusRules() {
92
+ const focused = `${region}:focus-visible`;
93
+ const head = `${focused} > .sw-table-sticky > thead`;
94
+ const foot = `${focused} > .sw-table > .sw-table-footer-sticky`;
95
+ const cells = (group, row = 'tr', cell = ':is(th, td)') => `${group} > ${row} > ${cell}`;
96
+ const both = (row, cell) => `${cells(head, row, cell)},\n${cells(foot, row, cell)}`;
97
+ const ring = 'var(--sw-focus-ring-width)';
98
+ const line = (x, y) => `inset ${x} ${y} 0 0 var(--sw-color-accent)`;
99
+ const none = '0 0 transparent';
100
+ return `${both('tr', ':is(th, td)')} {
101
+ box-shadow: var(--sw-table-ring-block, ${none}), var(--sw-table-ring-start, ${none}), var(--sw-table-ring-end, ${none});
102
+ }
103
+
104
+ ${cells(head, 'tr:first-child')} {
105
+ --sw-table-ring-block: ${line('0', ring)};
106
+ }
107
+
108
+ ${cells(foot, 'tr:last-child')} {
109
+ --sw-table-ring-block: ${line('0', `calc(-1 * ${ring})`)};
110
+ }
111
+
112
+ ${both('tr', ':first-child')} {
113
+ --sw-table-ring-start: ${line(ring, '0')};
114
+ }
115
+
116
+ ${both('tr', ':last-child')} {
117
+ --sw-table-ring-end: ${line(`calc(-1 * ${ring})`, '0')};
118
+ }
119
+
120
+ ${both('tr', ':first-child:dir(rtl)')} {
121
+ --sw-table-ring-start: ${line(`calc(-1 * ${ring})`, '0')};
122
+ }
123
+
124
+ ${both('tr', ':last-child:dir(rtl)')} {
125
+ --sw-table-ring-end: ${line(ring, '0')};
126
+ }`;
127
+ }
128
+ export function cssTableBoundedEdgeClasses() {
129
+ return `${shadeRules()}
130
+
131
+ ${focusRules()}`;
132
+ }
@@ -0,0 +1,2 @@
1
+ export declare function cssTableBoundedClasses(): string;
2
+ export declare function tableBoundedClassCatalog(): string[];
@@ -0,0 +1,73 @@
1
+ import { typographyVariants } from '@scalewing/tokens';
2
+ import { cssTableBoundedEdgeClasses } from './css-table-bounded-edges.js';
3
+ import { tableCellPadding, tableRuleWidth, tableSortMinHeight, } from './css-table-metrics.js';
4
+ import { coarsePointerQuery, touchTarget } from './touch-target.js';
5
+ const body = typographyVariants.body;
6
+ const caption = typographyVariants.caption;
7
+ /** A header row whose line is `line` tall: that line and its padding twice. */
8
+ function headBlock(line) {
9
+ return `calc(${line} + 2 * var(--sw-table-cell-pad))`;
10
+ }
11
+ /*
12
+ * A bounded table (`Table maxHeight`, `sw-table-bounded` on its region)
13
+ * is as tall as its row count (`--sw-table-max-rows`) of single-line body
14
+ * rows, plus one header row when it has a `thead` and one footer row when
15
+ * it has a `tfoot`, from the measures its cells are drawn with
16
+ * (`css-table-metrics.ts`): a body row is the body line, the cell padding
17
+ * twice and its rule; a header row is the caption line and the padding
18
+ * twice (a pinned header cell draws its rule inside itself), or the sort
19
+ * button's height where a header sorts (the touch target on a coarse
20
+ * pointer). Compact density swaps the padding. Rows of two lines or with
21
+ * controls, a second header or footer row, and a horizontal scrollbar
22
+ * that takes room (classic scrollbars) leave fewer rows showing.
23
+ *
24
+ * The region already scrolls (`overflow: auto`), so the rows past that
25
+ * height scroll inside it, and the pinned header group (`thead`, top 0)
26
+ * and a sticky footer (bottom 0) pin to the region's own edges: it is
27
+ * their nearest scroll box. A bounded region is never marked
28
+ * `sw-scroll-fits`, so it never stops being a scroll box for the page
29
+ * pinning in `css-table-page-pin.ts`. Printed, every row shows.
30
+ */
31
+ function heightRules() {
32
+ const sorting = '.sw-table-bounded:has(> .sw-table > thead .sw-table-sort)';
33
+ return `.sw-table-bounded {
34
+ --sw-table-cell-pad: ${tableCellPadding.comfortable.block};
35
+ --sw-table-head-block: 0px;
36
+ --sw-table-foot-rows: 0;
37
+ max-height: calc((var(--sw-table-max-rows) + var(--sw-table-foot-rows)) * (${body.lineHeight}px + 2 * var(--sw-table-cell-pad) + ${tableRuleWidth}) + var(--sw-table-head-block));
38
+ }
39
+
40
+ .sw-table-bounded:has(> .sw-table-compact) {
41
+ --sw-table-cell-pad: ${tableCellPadding.compact.block};
42
+ }
43
+
44
+ .sw-table-bounded:has(> .sw-table > thead) {
45
+ --sw-table-head-block: ${headBlock(`${caption.lineHeight}px`)};
46
+ }
47
+
48
+ ${sorting} {
49
+ --sw-table-head-block: ${headBlock(`max(${caption.lineHeight}px, ${tableSortMinHeight})`)};
50
+ }
51
+
52
+ @media ${coarsePointerQuery} {
53
+ ${sorting} {
54
+ --sw-table-head-block: ${headBlock(`max(${caption.lineHeight}px, ${touchTarget})`)};
55
+ }
56
+ }
57
+
58
+ .sw-table-bounded:has(> .sw-table > tfoot) {
59
+ --sw-table-foot-rows: 1;
60
+ }
61
+
62
+ @media print {
63
+ .sw-table-bounded { max-height: none; }
64
+ }`;
65
+ }
66
+ export function cssTableBoundedClasses() {
67
+ return `${heightRules()}
68
+
69
+ ${cssTableBoundedEdgeClasses()}`;
70
+ }
71
+ export function tableBoundedClassCatalog() {
72
+ return ['sw-table-bounded'];
73
+ }
@@ -1,8 +1,11 @@
1
1
  /** The colours a cell's text can take: a signed figure or a quiet value. */
2
2
  export declare const tableCellTones: readonly ["success", "danger", "muted", "accent"];
3
3
  export type TableCellTone = (typeof tableCellTones)[number];
4
- /** A row's place in the table: a row of its own, or one under the row above. */
5
- export declare const tableRowVariants: readonly ["default", "sub"];
4
+ /**
5
+ * A row's place in the table: a row of its own, one under the row above,
6
+ * or a total of the rows above it, mid-table.
7
+ */
8
+ export declare const tableRowVariants: readonly ["default", "sub", "total"];
6
9
  export type TableRowVariant = (typeof tableRowVariants)[number];
7
10
  export declare function assertTableCellTone(tone: unknown): void;
8
11
  export declare function assertTableRowVariant(variant: unknown): void;
@@ -1,7 +1,10 @@
1
1
  /** The colours a cell's text can take: a signed figure or a quiet value. */
2
2
  export const tableCellTones = ['success', 'danger', 'muted', 'accent'];
3
- /** A row's place in the table: a row of its own, or one under the row above. */
4
- export const tableRowVariants = ['default', 'sub'];
3
+ /**
4
+ * A row's place in the table: a row of its own, one under the row above,
5
+ * or a total of the rows above it, mid-table.
6
+ */
7
+ export const tableRowVariants = ['default', 'sub', 'total'];
5
8
  export function assertTableCellTone(tone) {
6
9
  if (tone !== undefined && !tableCellTones.includes(tone))
7
10
  throw new RangeError(`Unknown TableCell tone: ${String(tone)}`);
@@ -0,0 +1,14 @@
1
+ export declare const tableCellPadding: {
2
+ readonly comfortable: {
3
+ readonly block: "var(--sw-space-2)";
4
+ readonly inline: "var(--sw-space-3)";
5
+ };
6
+ readonly compact: {
7
+ readonly block: "var(--sw-space-1)";
8
+ readonly inline: "var(--sw-space-2)";
9
+ };
10
+ };
11
+ /** The rule under every row (a collapsed cell border). */
12
+ export declare const tableRuleWidth = "1px";
13
+ /** The least height of a sortable header's button on a fine pointer. */
14
+ export declare const tableSortMinHeight = "var(--sw-control-xs-min-height)";
@@ -0,0 +1,13 @@
1
+ /*
2
+ * The measures a table's cells are drawn with, shared by the cell rules
3
+ * (`css-table.ts`, `css-table-sort.ts`) and the bounded table's height
4
+ * (`css-table-bounded.ts`), so a change to one moves the other with it.
5
+ */
6
+ export const tableCellPadding = {
7
+ comfortable: { block: 'var(--sw-space-2)', inline: 'var(--sw-space-3)' },
8
+ compact: { block: 'var(--sw-space-1)', inline: 'var(--sw-space-2)' },
9
+ };
10
+ /** The rule under every row (a collapsed cell border). */
11
+ export const tableRuleWidth = '1px';
12
+ /** The least height of a sortable header's button on a fine pointer. */
13
+ export const tableSortMinHeight = 'var(--sw-control-xs-min-height)';
@@ -1,4 +1,5 @@
1
1
  import { chevronStroke } from './chevron.js';
2
+ import { tableSortMinHeight } from './css-table-metrics.js';
2
3
  import { coarsePointerQuery, touchTarget } from './touch-target.js';
3
4
  /*
4
5
  * A sortable header's button keeps the header's own text style (caption,
@@ -63,7 +64,7 @@ export function cssTableSortClasses() {
63
64
  letter-spacing: inherit;
64
65
  /* A step of room for the focus ring, taken back so the name stays on the cell's edge. */
65
66
  margin-inline: calc(-1 * var(--sw-space-1));
66
- min-height: var(--sw-control-xs-min-height);
67
+ min-height: ${tableSortMinHeight};
67
68
  padding: 0 var(--sw-space-1);
68
69
  text-align: inherit;
69
70
  }
@@ -0,0 +1,2 @@
1
+ export declare function cssTableTotalRowClasses(): string;
2
+ export declare function tableTotalRowClassCatalog(): string[];
@@ -0,0 +1,29 @@
1
+ /*
2
+ * A total row mid-table (`TableRow variant="total"`), such as a subtotal
3
+ * the rows above add up to before the table goes on: its cells at weight
4
+ * 600 under the footer's heavier rule, 2 px in the border colour across
5
+ * its top, as a `TableFooter` total is drawn. The rule is the cells' own
6
+ * top border, which the collapsed-border model resolves over the row
7
+ * above's 1 px one, so forced colours keep it (in `CanvasText`) and no
8
+ * pseudo-element is taken from a selected row's bar or a sub row's guide.
9
+ * The row takes no fill: on the subtle fill, accent text (a link or an
10
+ * accent-toned figure) falls under 4.5:1 in several palettes, as the
11
+ * selected row's note in `css-table.ts` found. A total row belongs in the
12
+ * body: a pinned header cell draws its rules on its pseudo-elements
13
+ * instead, and a `TableFooter` already draws the total at the table's
14
+ * end. A cell's `tone` keeps its colour; a struck total row mutes and
15
+ * strikes it as any struck row.
16
+ */
17
+ export function cssTableTotalRowClasses() {
18
+ return `.sw-table .sw-table-row-total > :is(th, td) {
19
+ border-top: 2px solid var(--sw-color-border);
20
+ font-weight: 600;
21
+ }
22
+
23
+ @media (forced-colors: active) {
24
+ .sw-table .sw-table-row-total > :is(th, td) { border-top-color: CanvasText; }
25
+ }`;
26
+ }
27
+ export function tableTotalRowClassCatalog() {
28
+ return ['sw-table-row-total'];
29
+ }
@@ -1,13 +1,18 @@
1
1
  import { typographyVariants } from '@scalewing/tokens';
2
2
  import { scrollRegionRules } from './css-scroll-region.js';
3
+ import { tableCellPadding, tableRuleWidth } from './css-table-metrics.js';
4
+ import { cssTableBoundedClasses, tableBoundedClassCatalog, } from './css-table-bounded.js';
3
5
  import { cssTableLayoutClasses, tableLayoutClassCatalog, } from './css-table-layout.js';
4
6
  import { cssTableLedgerClasses, tableLedgerClassCatalog, } from './css-table-ledger.js';
5
7
  import { cssTableFooterClasses, tableFooterClassCatalog, } from './css-table-footer.js';
6
8
  import { cssTablePagePinClasses, tablePagePinClassCatalog, } from './css-table-page-pin.js';
9
+ import { cssTableTotalRowClasses, tableTotalRowClassCatalog, } from './css-table-total-row.js';
7
10
  import { cssTablePressClasses, tablePressClassCatalog, } from './css-table-press.js';
8
11
  import { cssTableStickyHeaderClasses, tableStickyHeaderClassCatalog, } from './css-table-sticky-header.js';
9
12
  import { cssTableSortClasses, tableSortClassCatalog, } from './css-table-sort.js';
10
13
  const caption = typographyVariants.caption;
14
+ const comfortable = tableCellPadding.comfortable;
15
+ const compact = tableCellPadding.compact;
11
16
  /*
12
17
  * A selected row is marked by an accent bar at its inline start and nothing
13
18
  * else: no fill, so text, muted text and accent text (ghost buttons, links)
@@ -45,8 +50,8 @@ export function cssTableClasses() {
45
50
 
46
51
  .sw-table th,
47
52
  .sw-table td {
48
- border-bottom: 1px solid var(--sw-color-border);
49
- padding: var(--sw-space-2) var(--sw-space-3);
53
+ border-bottom: ${tableRuleWidth} solid var(--sw-color-border);
54
+ padding: ${comfortable.block} ${comfortable.inline};
50
55
  text-align: start;
51
56
  vertical-align: middle;
52
57
  }
@@ -79,7 +84,7 @@ ${cssTableStickyHeaderClasses()}
79
84
 
80
85
  .sw-table-compact th,
81
86
  .sw-table-compact td {
82
- padding: var(--sw-space-1) var(--sw-space-2);
87
+ padding: ${compact.block} ${compact.inline};
83
88
  }
84
89
 
85
90
  ${cssTableLayoutClasses()}
@@ -88,10 +93,14 @@ ${cssTableSortClasses()}
88
93
 
89
94
  ${cssTableLedgerClasses()}
90
95
 
96
+ ${cssTableTotalRowClasses()}
97
+
91
98
  ${cssTableFooterClasses()}
92
99
 
93
100
  ${cssTablePagePinClasses()}
94
101
 
102
+ ${cssTableBoundedClasses()}
103
+
95
104
  ${cssTablePressClasses()}
96
105
 
97
106
  ${selectedRowRules()}`;
@@ -109,8 +118,10 @@ export function tableClassCatalog() {
109
118
  ...tableLayoutClassCatalog(),
110
119
  ...tableSortClassCatalog(),
111
120
  ...tableLedgerClassCatalog(),
121
+ ...tableTotalRowClassCatalog(),
112
122
  ...tableFooterClassCatalog(),
113
123
  ...tablePagePinClassCatalog(),
124
+ ...tableBoundedClassCatalog(),
114
125
  ...tablePressClassCatalog(),
115
126
  ];
116
127
  }
@@ -1,4 +1,5 @@
1
1
  import { cardVariants, darkTheme, lightTheme, typographyVariants, } from '@scalewing/tokens';
2
+ import { chartToneClassCatalog } from './chart-tones.js';
2
3
  import { actionBarClassCatalog } from './css-action-bar.js';
3
4
  import { actionMenuClassCatalog } from './css-action-menu.js';
4
5
  import { accordionClassCatalog } from './css-accordion.js';
@@ -30,7 +31,12 @@ import { boxBorderClassCatalog } from './css-box-border.js';
30
31
  import { boxLinkClassCatalog } from './css-box-link.js';
31
32
  import { cssComponentClasses } from './css-components.js';
32
33
  import { buttonClassCatalog } from './css-button.js';
33
- import { chartClassCatalog } from './css-chart.js';
34
+ import { barChartClassCatalog } from './css-bar-chart.js';
35
+ import { chartLegendClassCatalog } from './css-chart-legend.js';
36
+ import { chartPlotClassCatalog } from './css-chart-plot.js';
37
+ import { lineChartClassCatalog } from './css-line-chart.js';
38
+ import { dataBarClassCatalog } from './css-data-bar.js';
39
+ import { paginationClassCatalog } from './css-pagination.js';
34
40
  import { dataClassCatalog } from './css-data.js';
35
41
  import { segmentedClassCatalog } from './css-segmented.js';
36
42
  import { tableClassCatalog } from './css-table.js';
@@ -68,7 +74,13 @@ export function utilityClassCatalog() {
68
74
  ...segmentedClassCatalog(),
69
75
  ...tableClassCatalog(),
70
76
  ...scrollRegionClassCatalog(),
71
- ...chartClassCatalog(),
77
+ ...barChartClassCatalog(),
78
+ ...dataBarClassCatalog(),
79
+ ...chartToneClassCatalog(),
80
+ ...chartLegendClassCatalog(),
81
+ ...chartPlotClassCatalog(),
82
+ ...lineChartClassCatalog(),
83
+ ...paginationClassCatalog(),
72
84
  ...chromeClassCatalog(),
73
85
  ...sectionNavClassCatalog(),
74
86
  ...dialogClassCatalog(),
@@ -0,0 +1,24 @@
1
+ /** Where a range bar sits on its track, as shares of the track (0 to 1). */
2
+ export type DataBarGeometry = {
3
+ /** The bar's inline start, from the track's start. */
4
+ start: number;
5
+ /** The bar's length. */
6
+ size: number;
7
+ /** Where zero falls, when the domain runs from below zero to above it. */
8
+ zero: number | null;
9
+ /**
10
+ * Which end of the bar meets the zero line on such a domain: `start`
11
+ * for a bar from zero toward the domain's end (a positive value), `end`
12
+ * for one from below zero up to it, `null` when neither end is zero.
13
+ */
14
+ zeroEnd: 'start' | 'end' | null;
15
+ };
16
+ /**
17
+ * Places the range from `from` to `to` on a track whose domain runs from
18
+ * `min` to `max`: the bar starts at the smaller end and runs to the larger,
19
+ * each clamped to the domain, so a value just past it from rounding stays
20
+ * on the track. A domain from below zero to above it has a zero line, and
21
+ * the bar says which of its ends meets it.
22
+ * Non-finite numbers, or a `min` not under `max`, throw a `RangeError`.
23
+ */
24
+ export declare function dataBarGeometry(from: number, to: number, min: number, max: number): DataBarGeometry;
@@ -0,0 +1,41 @@
1
+ function assertFinite(name, value) {
2
+ if (typeof value !== 'number' || !Number.isFinite(value))
3
+ throw new RangeError(`DataBar ${name} must be a finite number: ${String(value)}`);
4
+ }
5
+ function share(value, min, span) {
6
+ return (Math.min(Math.max(value, min), min + span) - min) / span;
7
+ }
8
+ /**
9
+ * Places the range from `from` to `to` on a track whose domain runs from
10
+ * `min` to `max`: the bar starts at the smaller end and runs to the larger,
11
+ * each clamped to the domain, so a value just past it from rounding stays
12
+ * on the track. A domain from below zero to above it has a zero line, and
13
+ * the bar says which of its ends meets it.
14
+ * Non-finite numbers, or a `min` not under `max`, throw a `RangeError`.
15
+ */
16
+ export function dataBarGeometry(from, to, min, max) {
17
+ assertFinite('from', from);
18
+ assertFinite('to', to);
19
+ assertFinite('min', min);
20
+ assertFinite('max', max);
21
+ if (min >= max)
22
+ throw new RangeError(`DataBar min must be under max: min ${min}, max ${max}`);
23
+ const span = max - min;
24
+ const low = Math.min(from, to);
25
+ const high = Math.max(from, to);
26
+ const start = share(low, min, span);
27
+ const end = share(high, min, span);
28
+ const diverging = min < 0 && max > 0;
29
+ return {
30
+ start,
31
+ size: end - start,
32
+ zero: diverging ? share(0, min, span) : null,
33
+ zeroEnd: !diverging || low === high
34
+ ? null
35
+ : low === 0
36
+ ? 'start'
37
+ : high === 0
38
+ ? 'end'
39
+ : null,
40
+ };
41
+ }
package/dist/index.d.ts CHANGED
@@ -16,6 +16,11 @@ export { RadioGroup, type RadioGroupOption, type RadioGroupProps, } from './comp
16
16
  export { AppHeader, appHeaderAppearances, type AppHeaderAppearance, type AppHeaderProps, } from './components/AppHeader.js';
17
17
  export { Badge, type BadgeProps, type BadgeSize, type BadgeTone, badgeSizes, badgeTones, } from './components/Badge.js';
18
18
  export { BarChart, type BarChartItem, type BarChartProps, type BarChartTone, barChartTones, } from './components/BarChart.js';
19
+ export { ChartLegend, type ChartFillTone, type ChartLegendItem, type ChartLegendProps, type ChartLegendSwatch, type ChartStrokeTone, type ChartTone, chartFillTones, chartLegendSwatches, chartStrokeTones, } from './components/ChartLegend.js';
20
+ export { ColumnChart, type ColumnChartCallout, type ColumnChartLine, type ColumnChartMode, type ColumnChartProps, type ColumnChartSeries, columnChartModes, } from './components/ColumnChart.js';
21
+ export { LineChart, type LineChartCurve, type LineChartMarker, type LineChartPeriod, type LineChartPoint, type LineChartProps, lineChartCurves, } from './components/LineChart.js';
22
+ export { DataBar, type DataBarProps, type DataBarTone, dataBarTones, } from './components/DataBar.js';
23
+ export { Pagination, type PaginationLabels, type PaginationProps, } from './components/Pagination.js';
19
24
  export { Button, type ButtonDisabledAppearance, type ButtonProps, type ButtonSize, type ButtonVariant, } from './components/Button.js';
20
25
  export { buttonDisabledAppearances, webButtonVariants, } from './css/css-button.js';
21
26
  export { Box, type BoxBorder, type BoxElement, type BoxLink, type BoxProps, } from './components/Box.js';
@@ -42,7 +47,7 @@ export { Select } from './components/Select.js';
42
47
  export type { SelectAction, SelectOption, SelectProps, SelectWidth, } from './components/Select.js';
43
48
  export { MultiSelect, type MultiSelectAlign, type MultiSelectOption, type MultiSelectProps, } from './components/MultiSelect.js';
44
49
  export { Stack, type Align, type Justify, type StackProps, } from './components/Stack.js';
45
- export { Table, TableBody, TableCell, TableFooter, TableHeader, TableRow, tableCellTones, tableRowVariants, type TableBodyProps, type TableCellProps, type TableCellTone, type TableColumnWidth, type TableDensity, type TableFooterProps, type TableHeaderProps, type TableLayout, type TableProps, type TableRowProps, type TableRowVariant, type TableSort, type TableStickyHeader, type TableVerticalAlign, } from './components/Table.js';
50
+ export { Table, TableBody, TableCell, TableFooter, TableHeader, TableRow, tableCellTones, tableRowVariants, type TableBodyProps, type TableCellProps, type TableCellTone, type TableColumnWidth, type TableDensity, type TableFooterProps, type TableHeaderProps, type TableLayout, type TableMaxHeight, type TableProps, type TableRowProps, type TableRowVariant, type TableSort, type TableStickyHeader, type TableVerticalAlign, } from './components/Table.js';
46
51
  export { Text, type TextAlign, type TextElement, type TextProps, } from './components/Text.js';
47
52
  export { ThemeProvider, useTheme, type ThemeProviderProps, } from './theme/ThemeProvider.js';
48
53
  export { utilityClassCatalog } from './css/stylesheet.js';
package/dist/index.js CHANGED
@@ -15,6 +15,11 @@ export { RadioGroup, } from './components/RadioGroup.js';
15
15
  export { AppHeader, appHeaderAppearances, } from './components/AppHeader.js';
16
16
  export { Badge, badgeSizes, badgeTones, } from './components/Badge.js';
17
17
  export { BarChart, barChartTones, } from './components/BarChart.js';
18
+ export { ChartLegend, chartFillTones, chartLegendSwatches, chartStrokeTones, } from './components/ChartLegend.js';
19
+ export { ColumnChart, columnChartModes, } from './components/ColumnChart.js';
20
+ export { LineChart, lineChartCurves, } from './components/LineChart.js';
21
+ export { DataBar, dataBarTones, } from './components/DataBar.js';
22
+ export { Pagination, } from './components/Pagination.js';
18
23
  export { Button, } from './components/Button.js';
19
24
  export { buttonDisabledAppearances, webButtonVariants, } from './css/css-button.js';
20
25
  export { Box, } from './components/Box.js';
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Whether a value is one of a list of names, narrowing it to the list's
3
+ * type: a component's check of a prop against its allowed values.
4
+ */
5
+ export declare function isOneOf<Name extends string>(names: readonly Name[], value: unknown): value is Name;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Whether a value is one of a list of names, narrowing it to the list's
3
+ * type: a component's check of a prop against its allowed values.
4
+ */
5
+ export function isOneOf(names, value) {
6
+ return names.includes(value);
7
+ }
@@ -0,0 +1,39 @@
1
+ import { type ChartStrokeTone } from './css/chart-tones.js';
2
+ import { type LineChartCurve } from './line-chart-path.js';
3
+ /** A value on a date (`YYYY-MM-DD`). */
4
+ export type LineChartPoint = {
5
+ x: string;
6
+ y: number;
7
+ };
8
+ /** A downward triangle on the line at one of its points' dates, with words. */
9
+ export type LineChartMarker = {
10
+ x: string;
11
+ label: string;
12
+ tone: ChartStrokeTone;
13
+ };
14
+ /** A period to box, from its first date to its last. */
15
+ export type LineChartPeriod = {
16
+ from: string;
17
+ to: string;
18
+ };
19
+ export type LineChartInput = {
20
+ label: string;
21
+ valueLabel?: string;
22
+ points: readonly LineChartPoint[];
23
+ curve: LineChartCurve;
24
+ tone: ChartStrokeTone;
25
+ markers: readonly LineChartMarker[];
26
+ markersLabel?: string;
27
+ ticks?: readonly string[];
28
+ highlight?: LineChartPeriod;
29
+ };
30
+ /** A date's day count, or a refusal naming what it was. */
31
+ export declare function lineChartDay(what: string, value: string): number;
32
+ /**
33
+ * Refuses what a line chart cannot draw, with a `RangeError`: empty words,
34
+ * a date that is not `YYYY-MM-DD`, points out of date order, a value that
35
+ * is not finite, an unknown curve or tone, a marker off the points' dates
36
+ * or markers without `markersLabel`, a tick outside the points' dates, and
37
+ * a period that ends before it starts.
38
+ */
39
+ export declare function assertLineChartInput(input: LineChartInput): void;
@@ -0,0 +1,70 @@
1
+ import { dayNumber } from './chart-dates.js';
2
+ import { chartStrokeTones } from './css/chart-tones.js';
3
+ import { isOneOf } from './is-one-of.js';
4
+ import { lineChartCurves } from './line-chart-path.js';
5
+ function refuse(message) {
6
+ throw new RangeError(`LineChart ${message}`);
7
+ }
8
+ function assertWords(what, value) {
9
+ if (typeof value !== 'string' || value.trim() === '')
10
+ refuse(`${what} must not be empty`);
11
+ }
12
+ /** A date's day count, or a refusal naming what it was. */
13
+ export function lineChartDay(what, value) {
14
+ const day = typeof value === 'string' ? dayNumber(value) : null;
15
+ if (day === null)
16
+ refuse(`${what} must be a YYYY-MM-DD date: ${String(value)}`);
17
+ return day;
18
+ }
19
+ function assertTone(what, tone) {
20
+ if (!isOneOf(chartStrokeTones, tone))
21
+ refuse(`${what} must be one of ${chartStrokeTones.join(', ')}; received ${String(tone)}`);
22
+ }
23
+ /**
24
+ * Refuses what a line chart cannot draw, with a `RangeError`: empty words,
25
+ * a date that is not `YYYY-MM-DD`, points out of date order, a value that
26
+ * is not finite, an unknown curve or tone, a marker off the points' dates
27
+ * or markers without `markersLabel`, a tick outside the points' dates, and
28
+ * a period that ends before it starts.
29
+ */
30
+ export function assertLineChartInput(input) {
31
+ assertWords('label', input.label);
32
+ if (input.valueLabel !== undefined)
33
+ assertWords('valueLabel', input.valueLabel);
34
+ if (!isOneOf(lineChartCurves, input.curve))
35
+ refuse(`curve must be one of ${lineChartCurves.join(', ')}; received ${String(input.curve)}`);
36
+ assertTone('tone', input.tone);
37
+ let previous = -Infinity;
38
+ for (const point of input.points) {
39
+ const day = lineChartDay('point x', point.x);
40
+ if (day < previous)
41
+ refuse(`points must be in date order: ${point.x} comes after a later date`);
42
+ previous = day;
43
+ if (typeof point.y !== 'number' || !Number.isFinite(point.y))
44
+ refuse(`point y must be a finite number: ${String(point.y)}`);
45
+ }
46
+ const dates = new Set(input.points.map((point) => point.x));
47
+ if (input.markers.length > 0)
48
+ assertWords('markersLabel', input.markersLabel);
49
+ for (const marker of input.markers) {
50
+ if (!dates.has(marker.x))
51
+ refuse(`marker x must be one of the points' dates: ${String(marker.x)}`);
52
+ assertWords('marker label', marker.label);
53
+ assertTone('marker tone', marker.tone);
54
+ }
55
+ const first = input.points[0]?.x;
56
+ const last = input.points.at(-1)?.x;
57
+ for (const tick of input.ticks ?? []) {
58
+ const day = lineChartDay('tick', tick);
59
+ if (first === undefined ||
60
+ day < lineChartDay('point x', first) ||
61
+ day > lineChartDay('point x', last))
62
+ refuse(`tick must fall within the points' dates: ${tick}`);
63
+ }
64
+ if (input.highlight) {
65
+ const from = lineChartDay('highlight from', input.highlight.from);
66
+ const to = lineChartDay('highlight to', input.highlight.to);
67
+ if (to < from)
68
+ refuse('highlight must not end before it starts');
69
+ }
70
+ }