@scalewing/react 1.38.0 → 1.40.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 (162) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +155 -1
  3. package/dist/calendar-month.d.ts +6 -0
  4. package/dist/calendar-month.js +15 -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 +6 -6
  30. package/dist/components/DateRangeButton.d.ts +65 -0
  31. package/dist/components/DateRangeButton.js +54 -0
  32. package/dist/components/LineChart.d.ts +83 -0
  33. package/dist/components/LineChart.js +77 -0
  34. package/dist/components/ListGroup.d.ts +33 -0
  35. package/dist/components/ListGroup.js +19 -0
  36. package/dist/components/ListRow.d.ts +51 -0
  37. package/dist/components/ListRow.js +59 -0
  38. package/dist/components/NavCount.d.ts +21 -0
  39. package/dist/components/NavCount.js +23 -0
  40. package/dist/components/SectionNav.js +1 -8
  41. package/dist/components/StatTile.d.ts +11 -4
  42. package/dist/components/StatTile.js +11 -4
  43. package/dist/components/{date-field → calendar}/CalendarGrid.d.ts +17 -2
  44. package/dist/components/{date-field → calendar}/CalendarGrid.js +11 -3
  45. package/dist/components/calendar/CalendarPopover.d.ts +25 -0
  46. package/dist/components/calendar/CalendarPopover.js +22 -0
  47. package/dist/components/calendar/MonthStep.d.ts +16 -0
  48. package/dist/components/calendar/MonthStep.js +17 -0
  49. package/dist/components/calendar/use-grid-focus.d.ts +18 -0
  50. package/dist/components/calendar/use-grid-focus.js +35 -0
  51. package/dist/components/calendar/wrap-tab.d.ts +7 -0
  52. package/dist/components/calendar/wrap-tab.js +29 -0
  53. package/dist/components/chart/ChartTable.d.ts +16 -0
  54. package/dist/components/chart/ChartTable.js +12 -0
  55. package/dist/components/chart/ChartValueAxis.d.ts +14 -0
  56. package/dist/components/chart/ChartValueAxis.js +11 -0
  57. package/dist/components/chart/use-chart-width.d.ts +8 -0
  58. package/dist/components/chart/use-chart-width.js +34 -0
  59. package/dist/components/column-chart/ChartCalloutList.d.ts +16 -0
  60. package/dist/components/column-chart/ChartCalloutList.js +13 -0
  61. package/dist/components/column-chart/ColumnChartDrawing.d.ts +17 -0
  62. package/dist/components/column-chart/ColumnChartDrawing.js +18 -0
  63. package/dist/components/date-field/CalendarDialog.js +9 -67
  64. package/dist/components/date-field/CalendarHeader.js +2 -11
  65. package/dist/components/date-range/DateRangePopover.d.ts +7 -0
  66. package/dist/components/date-range/DateRangePopover.js +17 -0
  67. package/dist/components/date-range/DateRangeSheet.d.ts +7 -0
  68. package/dist/components/date-range/DateRangeSheet.js +15 -0
  69. package/dist/components/date-range/RangeMonths.d.ts +28 -0
  70. package/dist/components/date-range/RangeMonths.js +50 -0
  71. package/dist/components/date-range/RangePresets.d.ts +16 -0
  72. package/dist/components/date-range/RangePresets.js +10 -0
  73. package/dist/components/date-range/RangeSummary.d.ts +15 -0
  74. package/dist/components/date-range/RangeSummary.js +11 -0
  75. package/dist/components/date-range/range-panel-props.d.ts +31 -0
  76. package/dist/components/date-range/range-panel-props.js +1 -0
  77. package/dist/components/date-range/use-range-popup.d.ts +21 -0
  78. package/dist/components/date-range/use-range-popup.js +53 -0
  79. package/dist/components/line-chart/LineChartDrawing.d.ts +17 -0
  80. package/dist/components/line-chart/LineChartDrawing.js +16 -0
  81. package/dist/components/link-press.d.ts +7 -0
  82. package/dist/components/link-press.js +13 -0
  83. package/dist/components/list/ListRowAccessoryMark.d.ts +5 -0
  84. package/dist/components/list/ListRowAccessoryMark.js +7 -0
  85. package/dist/components/list/ListRowContent.d.ts +20 -0
  86. package/dist/components/list/ListRowContent.js +10 -0
  87. package/dist/components/list/list-row-container.d.ts +5 -0
  88. package/dist/components/list/list-row-container.js +6 -0
  89. package/dist/components/use-media-query.d.ts +5 -0
  90. package/dist/components/use-media-query.js +18 -0
  91. package/dist/css/chart-marks.d.ts +10 -0
  92. package/dist/css/chart-marks.js +15 -0
  93. package/dist/css/chart-tones.d.ts +68 -0
  94. package/dist/css/chart-tones.js +130 -0
  95. package/dist/css/chevron.d.ts +6 -0
  96. package/dist/css/chevron.js +14 -0
  97. package/dist/css/{css-chart.d.ts → css-bar-chart.d.ts} +2 -2
  98. package/dist/css/{css-chart.js → css-bar-chart.js} +2 -2
  99. package/dist/css/css-button-group-attached.js +8 -4
  100. package/dist/css/css-button-group-track.js +1 -1
  101. package/dist/css/css-chart-legend.d.ts +5 -0
  102. package/dist/css/css-chart-legend.js +102 -0
  103. package/dist/css/css-chart-plot.d.ts +2 -0
  104. package/dist/css/css-chart-plot.js +170 -0
  105. package/dist/css/css-checkbox.js +2 -2
  106. package/dist/css/css-components.js +23 -2
  107. package/dist/css/css-count-pill.d.ts +2 -0
  108. package/dist/css/css-count-pill.js +54 -0
  109. package/dist/css/css-data-bar.d.ts +4 -9
  110. package/dist/css/css-data-bar.js +15 -31
  111. package/dist/css/css-date-field.js +3 -2
  112. package/dist/css/css-date-range-presets.d.ts +2 -0
  113. package/dist/css/css-date-range-presets.js +109 -0
  114. package/dist/css/css-date-range.d.ts +2 -0
  115. package/dist/css/css-date-range.js +197 -0
  116. package/dist/css/css-line-chart.d.ts +2 -0
  117. package/dist/css/css-line-chart.js +31 -0
  118. package/dist/css/css-list-row.d.ts +2 -0
  119. package/dist/css/css-list-row.js +193 -0
  120. package/dist/css/css-list.d.ts +3 -0
  121. package/dist/css/css-list.js +85 -0
  122. package/dist/css/css-scroll-region.d.ts +18 -0
  123. package/dist/css/css-scroll-region.js +41 -0
  124. package/dist/css/css-section-nav-filled.js +2 -25
  125. package/dist/css/css-stat-tile.d.ts +7 -0
  126. package/dist/css/css-stat-tile.js +16 -0
  127. package/dist/css/css-steps.js +2 -2
  128. package/dist/css/css-table-bounded-edges.js +4 -27
  129. package/dist/css/range-band.d.ts +2 -0
  130. package/dist/css/range-band.js +7 -0
  131. package/dist/css/stacking.js +2 -2
  132. package/dist/css/stylesheet.js +16 -2
  133. package/dist/date-range-draft.d.ts +26 -0
  134. package/dist/date-range-draft.js +36 -0
  135. package/dist/date-range-input.d.ts +26 -0
  136. package/dist/date-range-input.js +58 -0
  137. package/dist/date-range-labels.d.ts +22 -0
  138. package/dist/date-range-labels.js +26 -0
  139. package/dist/date-range-view.d.ts +12 -0
  140. package/dist/date-range-view.js +29 -0
  141. package/dist/date-range-words.d.ts +11 -0
  142. package/dist/date-range-words.js +40 -0
  143. package/dist/index.d.ts +9 -1
  144. package/dist/index.js +9 -1
  145. package/dist/is-one-of.d.ts +5 -0
  146. package/dist/is-one-of.js +7 -0
  147. package/dist/line-chart-input.d.ts +39 -0
  148. package/dist/line-chart-input.js +70 -0
  149. package/dist/line-chart-layout.d.ts +68 -0
  150. package/dist/line-chart-layout.js +99 -0
  151. package/dist/line-chart-markers.d.ts +29 -0
  152. package/dist/line-chart-markers.js +57 -0
  153. package/dist/line-chart-path.d.ts +18 -0
  154. package/dist/line-chart-path.js +33 -0
  155. package/dist/line-chart-points.d.ts +6 -0
  156. package/dist/line-chart-points.js +8 -0
  157. package/dist/line-chart-table.d.ts +15 -0
  158. package/dist/line-chart-table.js +29 -0
  159. package/dist/list-options.d.ts +70 -0
  160. package/dist/list-options.js +77 -0
  161. package/dist/styles.css +726 -26
  162. package/package.json +1 -1
@@ -0,0 +1,26 @@
1
+ import { type DateOnlyRange } from './date-only.js';
2
+ /** A named range a person can choose in one press, such as "Last 7 days". */
3
+ export type DateRangePreset = {
4
+ /** Unique among the presets; never shown. */
5
+ id: string;
6
+ label: string;
7
+ range: DateOnlyRange;
8
+ };
9
+ export type DateRangeInput = {
10
+ label: string;
11
+ value: DateOnlyRange;
12
+ presets: readonly DateRangePreset[];
13
+ min?: string;
14
+ max?: string;
15
+ maxDays?: number;
16
+ today?: string;
17
+ weekStartsOn: number;
18
+ };
19
+ /**
20
+ * Fails closed on DateRangeButton props it cannot draw: empty words, a
21
+ * malformed or reversed value, bounds or today, a `maxDays` that is not a
22
+ * whole number from 1, and presets that repeat an id or that a press could
23
+ * not pick (outside the bounds or longer than `maxDays`). A value outside
24
+ * the bounds stays the value, as on CalendarButton.
25
+ */
26
+ export declare function assertDateRangeInput(input: DateRangeInput): void;
@@ -0,0 +1,58 @@
1
+ import { assertWeekStart } from './calendar-month.js';
2
+ import { rangeDayCount } from './date-range-draft.js';
3
+ import { assertDateOnly, assertDateRange, isOutsideDateRange, } from './date-only.js';
4
+ function isBlank(text) {
5
+ return typeof text !== 'string' || text.trim() === '';
6
+ }
7
+ function assertValue(value) {
8
+ if (typeof value !== 'object' || value === null)
9
+ throw new RangeError('value must be a { start, end } range');
10
+ assertDateOnly('value.start', value.start);
11
+ assertDateOnly('value.end', value.end);
12
+ if (value.start > value.end)
13
+ throw new RangeError('value.start must not be after value.end');
14
+ }
15
+ function assertBounds(min, max) {
16
+ if (min !== undefined)
17
+ assertDateOnly('min', min);
18
+ if (max !== undefined)
19
+ assertDateOnly('max', max);
20
+ if (min !== undefined && max !== undefined && min > max)
21
+ throw new RangeError('min must not be after max');
22
+ }
23
+ function assertPreset(preset, { min, max, maxDays }) {
24
+ if (isBlank(preset.id) || isBlank(preset.label))
25
+ throw new RangeError('presets must have non-empty ids and labels');
26
+ assertDateRange(preset.range);
27
+ const { start, end } = preset.range;
28
+ if (isOutsideDateRange(start, min, max) || isOutsideDateRange(end, min, max))
29
+ throw new RangeError(`preset ${preset.id} must lie inside min and max`);
30
+ if (maxDays !== undefined && rangeDayCount(preset.range) > maxDays)
31
+ throw new RangeError(`preset ${preset.id} must not exceed maxDays`);
32
+ }
33
+ /**
34
+ * Fails closed on DateRangeButton props it cannot draw: empty words, a
35
+ * malformed or reversed value, bounds or today, a `maxDays` that is not a
36
+ * whole number from 1, and presets that repeat an id or that a press could
37
+ * not pick (outside the bounds or longer than `maxDays`). A value outside
38
+ * the bounds stays the value, as on CalendarButton.
39
+ */
40
+ export function assertDateRangeInput(input) {
41
+ if (isBlank(input.label))
42
+ throw new RangeError('label must be non-empty text');
43
+ assertValue(input.value);
44
+ assertBounds(input.min, input.max);
45
+ if (input.today !== undefined)
46
+ assertDateOnly('today', input.today);
47
+ assertWeekStart(input.weekStartsOn);
48
+ const { maxDays } = input;
49
+ if (maxDays !== undefined && (!Number.isInteger(maxDays) || maxDays < 1))
50
+ throw new RangeError('maxDays must be a whole number from 1');
51
+ const ids = new Set();
52
+ for (const preset of input.presets) {
53
+ assertPreset(preset, input);
54
+ if (ids.has(preset.id))
55
+ throw new RangeError(`presets must have unique ids: ${preset.id}`);
56
+ ids.add(preset.id);
57
+ }
58
+ }
@@ -0,0 +1,22 @@
1
+ import { type CalendarButtonLabels } from './calendar-button-labels.js';
2
+ /**
3
+ * DateRangeButton's own words. Products pass their translations; month and
4
+ * weekday names, and the dates themselves, come from `Intl` instead.
5
+ */
6
+ export type DateRangeButtonLabels = Pick<CalendarButtonLabels, 'previousMonth' | 'nextMonth' | 'nameSeparator'> & {
7
+ /** Names the list of presets, such as "Quick ranges". */
8
+ presets: string;
9
+ /** Applies the chosen range and closes the calendar, such as "Apply". */
10
+ apply: string;
11
+ /** Closes the calendar and keeps the range it opened with. */
12
+ cancel: string;
13
+ /** The bottom sheet's close button. */
14
+ close: string;
15
+ /** How to pick, shown under the calendar, such as "Choose a first and a last day." */
16
+ hint: string;
17
+ /** The length of the chosen range, such as "7 days". */
18
+ dayCount: (days: number) => string;
19
+ };
20
+ export declare const defaultDateRangeButtonLabels: DateRangeButtonLabels;
21
+ /** Consumer words over the English defaults; empty words fail closed. */
22
+ export declare function resolveDateRangeButtonLabels(labels?: Partial<DateRangeButtonLabels>): DateRangeButtonLabels;
@@ -0,0 +1,26 @@
1
+ import { defaultNameSeparator, } from './calendar-button-labels.js';
2
+ export const defaultDateRangeButtonLabels = {
3
+ previousMonth: 'Previous month',
4
+ nextMonth: 'Next month',
5
+ nameSeparator: defaultNameSeparator,
6
+ presets: 'Presets',
7
+ apply: 'Apply',
8
+ cancel: 'Cancel',
9
+ close: 'Close',
10
+ hint: 'Choose a first and a last day.',
11
+ dayCount: (days) => (days === 1 ? '1 day' : `${days} days`),
12
+ };
13
+ /** Consumer words over the English defaults; empty words fail closed. */
14
+ export function resolveDateRangeButtonLabels(labels = {}) {
15
+ const resolved = { ...defaultDateRangeButtonLabels, ...labels };
16
+ const { dayCount, nameSeparator, ...words } = resolved;
17
+ for (const [key, text] of Object.entries(words))
18
+ if (typeof text !== 'string' || text.trim() === '')
19
+ throw new RangeError(`labels.${key} must be non-empty text`);
20
+ // Only an empty string is refused: a lone space is a valid separator.
21
+ if (typeof nameSeparator !== 'string' || nameSeparator === '')
22
+ throw new RangeError('labels.nameSeparator must be non-empty text');
23
+ if (typeof dayCount !== 'function')
24
+ throw new RangeError('labels.dayCount must be a function');
25
+ return resolved;
26
+ }
@@ -0,0 +1,12 @@
1
+ import { type CalendarMonth } from './calendar-month.js';
2
+ /** How many months a range calendar shows: two side by side, or one. */
3
+ export type RangeMonthCount = 1 | 2;
4
+ /**
5
+ * The first month a range calendar shows so that `focusDate` is in view.
6
+ * A view that already holds it stays; a focus date before it becomes the
7
+ * first month, and one after it the last. With no view yet, the focus
8
+ * date's month is the last one shown, but never a month before `min`'s.
9
+ */
10
+ export declare function firstShownMonth(focusDate: string, count: RangeMonthCount, shown: CalendarMonth | undefined, min: string | undefined): CalendarMonth;
11
+ /** The months shown from `first`, in order. */
12
+ export declare function shownMonths(first: CalendarMonth, count: RangeMonthCount): CalendarMonth[];
@@ -0,0 +1,29 @@
1
+ import { monthOf, monthsBetween, shiftMonth, } from './calendar-month.js';
2
+ /**
3
+ * The first month a range calendar shows so that `focusDate` is in view.
4
+ * A view that already holds it stays; a focus date before it becomes the
5
+ * first month, and one after it the last. With no view yet, the focus
6
+ * date's month is the last one shown, but never a month before `min`'s.
7
+ */
8
+ export function firstShownMonth(focusDate, count, shown, min) {
9
+ const focus = monthOf(focusDate);
10
+ if (shown !== undefined) {
11
+ const offset = monthsBetween(shown, focus);
12
+ if (offset >= 0 && offset < count)
13
+ return shown;
14
+ if (offset < 0)
15
+ return focus;
16
+ return shiftMonth(focus, 1 - count);
17
+ }
18
+ const first = shiftMonth(focus, 1 - count);
19
+ // No month before 0001 exists: the first year's January leads.
20
+ if (first.year < 1)
21
+ return focus;
22
+ if (min !== undefined && monthsBetween(first, monthOf(min)) > 0)
23
+ return monthOf(min);
24
+ return first;
25
+ }
26
+ /** The months shown from `first`, in order. */
27
+ export function shownMonths(first, count) {
28
+ return Array.from({ length: count }, (_, index) => shiftMonth(first, index));
29
+ }
@@ -0,0 +1,11 @@
1
+ import { type DateOnlyRange } from './date-only.js';
2
+ /**
3
+ * A range as the button shows it, the locale sharing what both ends have,
4
+ * such as "Oct 6 – 12, 2026", or "Oct 6, 2026" for one day.
5
+ */
6
+ export declare function formatRangeShort(range: DateOnlyRange, locale: string): string;
7
+ /**
8
+ * A range with its weekdays, as the calendar states the one being picked,
9
+ * such as "Tue, Oct 6 – Mon, Oct 12, 2026".
10
+ */
11
+ export declare function formatRangeLong(range: DateOnlyRange, locale: string): string;
@@ -0,0 +1,40 @@
1
+ import { dateFormatter, formatterDate } from './date-formatters.js';
2
+ import { parseDateOnly } from './date-only.js';
3
+ const shortRange = {
4
+ month: 'short',
5
+ day: 'numeric',
6
+ year: 'numeric',
7
+ };
8
+ const longRange = {
9
+ weekday: 'short',
10
+ month: 'short',
11
+ day: 'numeric',
12
+ year: 'numeric',
13
+ };
14
+ function formatRange(range, locale, options) {
15
+ const start = parseDateOnly(range.start);
16
+ const end = parseDateOnly(range.end);
17
+ if (!start || !end)
18
+ return '';
19
+ const format = dateFormatter(locale, options);
20
+ // Browsers before formatRange (Safari 14.0) write both ends in full.
21
+ if (typeof format.formatRange !== 'function')
22
+ return range.start === range.end
23
+ ? format.format(formatterDate(start))
24
+ : `${format.format(formatterDate(start))} – ${format.format(formatterDate(end))}`;
25
+ return format.formatRange(formatterDate(start), formatterDate(end));
26
+ }
27
+ /**
28
+ * A range as the button shows it, the locale sharing what both ends have,
29
+ * such as "Oct 6 – 12, 2026", or "Oct 6, 2026" for one day.
30
+ */
31
+ export function formatRangeShort(range, locale) {
32
+ return formatRange(range, locale, shortRange);
33
+ }
34
+ /**
35
+ * A range with its weekdays, as the calendar states the one being picked,
36
+ * such as "Tue, Oct 6 – Mon, Oct 12, 2026".
37
+ */
38
+ export function formatRangeLong(range, locale) {
39
+ return formatRange(range, locale, longRange);
40
+ }
package/dist/index.d.ts CHANGED
@@ -5,6 +5,10 @@ export type { AccordionProps, AccordionSize } from './components/Accordion.js';
5
5
  export { Switch, type SwitchProps } from './components/Switch.js';
6
6
  export { DateField, type DateFieldLabels, type DateFieldProps, type WeekStart, } from './components/DateField.js';
7
7
  export { CalendarButton, type CalendarButtonLabels, type CalendarButtonProps, type CalendarButtonRange, } from './components/CalendarButton.js';
8
+ export { DateRangeButton, type DateRangeButtonLabels, type DateRangeButtonProps, type DateRangeButtonValue, type DateRangePreset, } from './components/DateRangeButton.js';
9
+ export { ListGroup, type ListGroupProps } from './components/ListGroup.js';
10
+ export { ListRow, type ListRowProps } from './components/ListRow.js';
11
+ export { listGroupVariants, listRowTitleAppearances, listRowTones, listRowValueTones, type ListGroupMaxHeight, type ListGroupVariant, type ListRowTitleAppearance, type ListRowTone, type ListRowValueTone, } from './list-options.js';
8
12
  export { Checkbox, type CheckboxProps } from './components/Checkbox.js';
9
13
  export { Spinner, type SpinnerProps, type SpinnerSize, } from './components/Spinner.js';
10
14
  export { GlyphTile, glyphTileSizes, type GlyphTileHue, type GlyphTileProps, type GlyphTileSize, } from './components/GlyphTile.js';
@@ -16,6 +20,9 @@ export { RadioGroup, type RadioGroupOption, type RadioGroupProps, } from './comp
16
20
  export { AppHeader, appHeaderAppearances, type AppHeaderAppearance, type AppHeaderProps, } from './components/AppHeader.js';
17
21
  export { Badge, type BadgeProps, type BadgeSize, type BadgeTone, badgeSizes, badgeTones, } from './components/Badge.js';
18
22
  export { BarChart, type BarChartItem, type BarChartProps, type BarChartTone, barChartTones, } from './components/BarChart.js';
23
+ export { ChartLegend, type ChartFillTone, type ChartLegendItem, type ChartLegendProps, type ChartLegendSwatch, type ChartStrokeTone, type ChartTone, chartFillTones, chartLegendSwatches, chartStrokeTones, } from './components/ChartLegend.js';
24
+ export { ColumnChart, type ColumnChartCallout, type ColumnChartLine, type ColumnChartMode, type ColumnChartProps, type ColumnChartSeries, columnChartModes, } from './components/ColumnChart.js';
25
+ export { LineChart, type LineChartCurve, type LineChartMarker, type LineChartPeriod, type LineChartPoint, type LineChartProps, lineChartCurves, } from './components/LineChart.js';
19
26
  export { DataBar, type DataBarProps, type DataBarTone, dataBarTones, } from './components/DataBar.js';
20
27
  export { Pagination, type PaginationLabels, type PaginationProps, } from './components/Pagination.js';
21
28
  export { Button, type ButtonDisabledAppearance, type ButtonProps, type ButtonSize, type ButtonVariant, } from './components/Button.js';
@@ -25,7 +32,7 @@ export { boxLinkAppearances } from './css/css-box-link.js';
25
32
  export { ButtonGroup, buttonGroupAppearances, type ButtonGroupAppearance, type ButtonGroupJustify, type ButtonGroupProps, } from './components/ButtonGroup.js';
26
33
  export { Card, type CardProps, type CardVariant } from './components/Card.js';
27
34
  export { Dialog, dialogAppearances, dialogSizes, type DialogAppearance, type DialogProps, type DialogRole, type DialogSize, type DialogTitleLevel, } from './components/Dialog.js';
28
- export { StatTile, type StatTileEmphasis, type StatTileProps, type StatTileTone, statTileTones, } from './components/StatTile.js';
35
+ export { StatTile, type StatTileAppearance, statTileAppearances, type StatTileEmphasis, type StatTileProps, type StatTileTone, statTileTones, } from './components/StatTile.js';
29
36
  export { TabPanel, type TabItem, type TabPanelProps, Tabs, type TabsProps, tabId, tabPanelId, } from './components/Tabs.js';
30
37
  export { Toast, toastPlacements, toastTones, type ToastAction, type ToastPlacement, } from './components/Toast.js';
31
38
  export type { ToastProps, ToastTone } from './components/Toast.js';
@@ -37,6 +44,7 @@ export { Inline, type InlineProps } from './components/Inline.js';
37
44
  export { Split } from './components/Split.js';
38
45
  export type { SplitProps } from './components/Split.js';
39
46
  export { Nav, navAppearances, type NavAppearance, type NavProps, } from './components/Nav.js';
47
+ export { NavCount, type NavCountProps } from './components/NavCount.js';
40
48
  export { SectionNav, sectionNavAppearances, type SectionNavAppearance, type SectionNavItem, type SectionNavProps, } from './components/SectionNav.js';
41
49
  export { SegmentedControl, type SegmentedControlProps, type SegmentedControlVariant, type SegmentedItem, } from './components/SegmentedControl.js';
42
50
  export { Combobox, comboboxSizes, type ComboboxAction, type ComboboxBadge, type ComboboxGroup, type ComboboxOption, type ComboboxProps, type ComboboxSize, } from './components/Combobox.js';
package/dist/index.js CHANGED
@@ -4,6 +4,10 @@ export { Accordion } from './components/Accordion.js';
4
4
  export { Switch } from './components/Switch.js';
5
5
  export { DateField, } from './components/DateField.js';
6
6
  export { CalendarButton, } from './components/CalendarButton.js';
7
+ export { DateRangeButton, } from './components/DateRangeButton.js';
8
+ export { ListGroup } from './components/ListGroup.js';
9
+ export { ListRow } from './components/ListRow.js';
10
+ export { listGroupVariants, listRowTitleAppearances, listRowTones, listRowValueTones, } from './list-options.js';
7
11
  export { Checkbox } from './components/Checkbox.js';
8
12
  export { Spinner, } from './components/Spinner.js';
9
13
  export { GlyphTile, glyphTileSizes, } from './components/GlyphTile.js';
@@ -15,6 +19,9 @@ export { RadioGroup, } from './components/RadioGroup.js';
15
19
  export { AppHeader, appHeaderAppearances, } from './components/AppHeader.js';
16
20
  export { Badge, badgeSizes, badgeTones, } from './components/Badge.js';
17
21
  export { BarChart, barChartTones, } from './components/BarChart.js';
22
+ export { ChartLegend, chartFillTones, chartLegendSwatches, chartStrokeTones, } from './components/ChartLegend.js';
23
+ export { ColumnChart, columnChartModes, } from './components/ColumnChart.js';
24
+ export { LineChart, lineChartCurves, } from './components/LineChart.js';
18
25
  export { DataBar, dataBarTones, } from './components/DataBar.js';
19
26
  export { Pagination, } from './components/Pagination.js';
20
27
  export { Button, } from './components/Button.js';
@@ -24,7 +31,7 @@ export { boxLinkAppearances } from './css/css-box-link.js';
24
31
  export { ButtonGroup, buttonGroupAppearances, } from './components/ButtonGroup.js';
25
32
  export { Card } from './components/Card.js';
26
33
  export { Dialog, dialogAppearances, dialogSizes, } from './components/Dialog.js';
27
- export { StatTile, statTileTones, } from './components/StatTile.js';
34
+ export { StatTile, statTileAppearances, statTileTones, } from './components/StatTile.js';
28
35
  export { TabPanel, Tabs, tabId, tabPanelId, } from './components/Tabs.js';
29
36
  export { Toast, toastPlacements, toastTones, } from './components/Toast.js';
30
37
  export { Field, fieldVariants, } from './components/Field.js';
@@ -34,6 +41,7 @@ export { Stepper, stepperLimitAppearances, } from './components/Stepper.js';
34
41
  export { Inline } from './components/Inline.js';
35
42
  export { Split } from './components/Split.js';
36
43
  export { Nav, navAppearances, } from './components/Nav.js';
44
+ export { NavCount } from './components/NavCount.js';
37
45
  export { SectionNav, sectionNavAppearances, } from './components/SectionNav.js';
38
46
  export { SegmentedControl, } from './components/SegmentedControl.js';
39
47
  export { Combobox, comboboxSizes, } from './components/Combobox.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
+ }
@@ -0,0 +1,68 @@
1
+ import { type ChartPlot, type ChartTick } from './chart-axis.js';
2
+ import { type PlacedMarker } from './line-chart-markers.js';
3
+ import { type ChartPoint, type LineChartCurve } from './line-chart-path.js';
4
+ export type LineChartLayoutInput = {
5
+ width: number;
6
+ /** The points as day counts, in date order. */
7
+ points: readonly {
8
+ day: number;
9
+ y: number;
10
+ }[];
11
+ curve: LineChartCurve;
12
+ area: boolean;
13
+ /** Each marker's day (one of the points') and words. */
14
+ markers: readonly {
15
+ day: number;
16
+ label: string;
17
+ }[];
18
+ /** The days to label on the time axis, and their words. */
19
+ ticks: readonly {
20
+ day: number;
21
+ text: string;
22
+ }[];
23
+ /** The boxed period's first and last days. */
24
+ highlight: {
25
+ from: number;
26
+ to: number;
27
+ } | null;
28
+ /** The words at the line's last value, or none. */
29
+ endLabel: string | null;
30
+ formatTick: (value: number) => string;
31
+ };
32
+ export type LineChartLayout = {
33
+ width: number;
34
+ height: number;
35
+ compact: boolean;
36
+ plot: ChartPlot;
37
+ ticks: ChartTick[];
38
+ path: ChartPoint[];
39
+ area: ChartPoint[];
40
+ last: ChartPoint | null;
41
+ end: {
42
+ x: number;
43
+ y: number;
44
+ text: string;
45
+ } | null;
46
+ dateLabels: {
47
+ day: number;
48
+ x: number;
49
+ y: number;
50
+ text: string;
51
+ }[];
52
+ highlight: {
53
+ x: number;
54
+ y: number;
55
+ width: number;
56
+ height: number;
57
+ } | null;
58
+ markers: PlacedMarker[];
59
+ };
60
+ /**
61
+ * Lays a line chart out at a width: its value axis on the left (through
62
+ * zero), the line through its points by date (straight or in steps), the
63
+ * area under it, its last value beside the plot's end, the time axis's
64
+ * labels (fewer when they would meet), the boxed period, and each marker's
65
+ * triangle and words. A compact chart (under `chartCompactWidth`) has a
66
+ * shorter plot.
67
+ */
68
+ export declare function lineChartLayout(input: LineChartLayoutInput): LineChartLayout;
@@ -0,0 +1,99 @@
1
+ import { endGutter, labelGutter, labelInside, periodBox, valueTicks, valueY, } from './chart-axis.js';
2
+ import { chartCompactWidth, chartGap, chartPlotHeight, chartTextWidth, chartType, } from './chart-metrics.js';
3
+ import { chartScale, extentThroughZero } from './chart-scale.js';
4
+ import { placeMarkers } from './line-chart-markers.js';
5
+ import { closingIndexes } from './line-chart-points.js';
6
+ import { areaPath, linePath, } from './line-chart-path.js';
7
+ /**
8
+ * The time axis's labels: each asked-for day at its place, kept inside the
9
+ * chart, and thinned from the last back so no two meet.
10
+ */
11
+ function dateLabels(ticks, x, width, y) {
12
+ const kept = [];
13
+ const sorted = [...ticks].sort((a, b) => b.day - a.day);
14
+ for (const tick of sorted) {
15
+ const centre = labelInside(x(tick.day), tick.text, width);
16
+ const half = chartTextWidth(tick.text) / 2;
17
+ const next = kept[0];
18
+ if (next &&
19
+ centre + half + chartGap * 2 > next.x - chartTextWidth(next.text) / 2)
20
+ continue;
21
+ kept.unshift({ day: tick.day, x: centre, y, text: tick.text });
22
+ }
23
+ return kept;
24
+ }
25
+ /**
26
+ * Lays a line chart out at a width: its value axis on the left (through
27
+ * zero), the line through its points by date (straight or in steps), the
28
+ * area under it, its last value beside the plot's end, the time axis's
29
+ * labels (fewer when they would meet), the boxed period, and each marker's
30
+ * triangle and words. A compact chart (under `chartCompactWidth`) has a
31
+ * shorter plot.
32
+ */
33
+ export function lineChartLayout(input) {
34
+ const { width, points } = input;
35
+ const compact = width < chartCompactWidth;
36
+ const scale = chartScale(extentThroughZero(points.map((p) => p.y)));
37
+ const left = labelGutter(scale.ticks.map(input.formatTick));
38
+ const right = Math.max(left + 1, width -
39
+ endGutter({ width, left, axisWords: [], endLabel: input.endLabel })
40
+ .gutter);
41
+ const top = chartGap + chartType.lineHeight / 2;
42
+ const plot = {
43
+ left,
44
+ right,
45
+ top,
46
+ bottom: top + chartPlotHeight(compact),
47
+ };
48
+ const first = points[0]?.day ?? 0;
49
+ const span = (points.at(-1)?.day ?? first) - first;
50
+ const dayWidth = span > 0 ? (right - left) / span : right - left;
51
+ const x = (day) => span > 0 ? left + (day - first) * dayWidth : (left + right) / 2;
52
+ const placed = points.map((p) => ({
53
+ x: x(p.day),
54
+ y: valueY(p.y, scale, plot),
55
+ }));
56
+ const path = linePath(placed, input.curve);
57
+ const last = placed.at(-1) ?? null;
58
+ // A marker sits on its day's last point, the day's closing value.
59
+ const closing = closingIndexes(points.map((p) => p.day));
60
+ const markerPoints = input.markers.map((marker) => ({
61
+ x: x(marker.day),
62
+ y: placed[closing.get(marker.day) ?? -1]?.y ?? plot.bottom,
63
+ label: marker.label,
64
+ }));
65
+ return {
66
+ width,
67
+ height: plot.bottom + chartGap + chartType.lineHeight,
68
+ compact,
69
+ plot,
70
+ ticks: valueTicks(scale, plot, input.formatTick),
71
+ path,
72
+ area: input.area ? areaPath(path, valueY(0, scale, plot)) : [],
73
+ last,
74
+ end: last && input.endLabel
75
+ ? { x: right + chartGap, y: last.y, text: input.endLabel }
76
+ : null,
77
+ dateLabels: dateLabels(input.ticks, x, width, plot.bottom + chartGap + chartType.fontSize),
78
+ highlight: points.length > 0
79
+ ? highlightBox(input.highlight, first, span, x, dayWidth, plot)
80
+ : null,
81
+ markers: placeMarkers(markerPoints, plot),
82
+ };
83
+ }
84
+ /**
85
+ * The boxed period: its days, half a day past each end, clipped to the
86
+ * plot. On a chart of one date it is the whole plot when the period holds
87
+ * that date and nothing otherwise.
88
+ */
89
+ function highlightBox(period, first, span, x, dayWidth, plot) {
90
+ if (!period)
91
+ return null;
92
+ if (span === 0 && (period.from > first || period.to < first))
93
+ return null;
94
+ const from = Math.max(x(period.from) - dayWidth / 2, plot.left);
95
+ const to = Math.min(x(period.to) + dayWidth / 2, plot.right);
96
+ if (to < from)
97
+ return null;
98
+ return periodBox(from, to - from, plot, 0);
99
+ }
@@ -0,0 +1,29 @@
1
+ import { type ChartPlot } from './chart-axis.js';
2
+ /**
3
+ * A marker on a line: a downward triangle at its date with its words,
4
+ * below the line where they fit inside the plot, above it otherwise. Its
5
+ * words are left out where they would meet an earlier marker's.
6
+ */
7
+ export type PlacedMarker = {
8
+ /** Its place in the chart's `markers`. */
9
+ order: number;
10
+ x: number;
11
+ /** The triangle's corners, as SVG points. */
12
+ triangle: string;
13
+ /** Its words' centre, moved in so they stay over the plot. */
14
+ labelX: number;
15
+ labelY: number;
16
+ label: string;
17
+ labelShown: boolean;
18
+ };
19
+ /**
20
+ * Places each marker at the line's height on its date: the triangle a
21
+ * small inset under the line, its words under it (moved in to stay over
22
+ * the plot), or both above the line when the words would pass the plot's
23
+ * bottom.
24
+ */
25
+ export declare function placeMarkers(markers: readonly {
26
+ x: number;
27
+ y: number;
28
+ label: string;
29
+ }[], plot: ChartPlot): PlacedMarker[];