@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
@@ -1,43 +1,12 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useId, useLayoutEffect, useRef, useState, } from 'react';
3
- import { monthOf, } from '../../calendar-month.js';
4
- import { clampDateOnly, daysInMonth, formatDateOnly, isOutsideDateRange, parseDateOnly, } from '../../date-only.js';
2
+ import { useId, useRef, useState } from 'react';
3
+ import { monthOf, sameDayIn } from '../../calendar-month.js';
4
+ import { clampDateOnly, isOutsideDateRange, } from '../../date-only.js';
5
5
  import { Button } from '../Button.js';
6
- import { CalendarGrid } from './CalendarGrid.js';
6
+ import { CalendarGrid } from '../calendar/CalendarGrid.js';
7
7
  import { CalendarHeader } from './CalendarHeader.js';
8
- import { useAnchoredPopover } from '../use-anchored-popover.js';
9
- /** The dialog's tab stops: enabled buttons and the grid's roving day. */
10
- function isTabStop(node) {
11
- if (node instanceof HTMLButtonElement)
12
- return !node.disabled;
13
- return node.getAttribute('tabindex') === '0';
14
- }
15
- /** Keeps Tab and Shift+Tab inside the dialog, as a modal dialog does. */
16
- function wrapTab(event) {
17
- // One universal selector keeps document order in every DOM implementation.
18
- const items = [
19
- ...event.currentTarget.querySelectorAll('*'),
20
- ].filter(isTabStop);
21
- const first = items[0];
22
- const last = items[items.length - 1];
23
- if (!first || !last)
24
- return;
25
- if (event.shiftKey && document.activeElement === first) {
26
- event.preventDefault();
27
- last.focus();
28
- }
29
- else if (!event.shiftKey && document.activeElement === last) {
30
- event.preventDefault();
31
- first.focus();
32
- }
33
- }
34
- function sameDayIn(month, focused) {
35
- const day = parseDateOnly(focused)?.day ?? 1;
36
- return formatDateOnly({
37
- ...month,
38
- day: Math.min(day, daysInMonth(month.year, month.month)),
39
- });
40
- }
8
+ import { CalendarPopover } from '../calendar/CalendarPopover.js';
9
+ import { useGridFocus } from '../calendar/use-grid-focus.js';
41
10
  /**
42
11
  * The calendar popover: a labelled modal dialog anchored under the field,
43
12
  * following the WAI-ARIA date picker dialog pattern. It opens on the
@@ -57,37 +26,10 @@ export function CalendarDialog({ anchorRef, buttonRef, id, labelId, labels, loca
57
26
  // Clamped on every render, so new bounds while open move it inside.
58
27
  const focusDate = clampDateOnly(rawFocusDate, min, max);
59
28
  const month = monthOf(focusDate);
60
- // Opening and grid keys move DOM focus to the focus date; header controls
61
- // and presses change the date without taking focus from where it is.
62
- const focusGrid = useRef(true);
63
- useAnchoredPopover(dialogRef, anchorRef, buttonRef, () => onClose(false));
64
- // Whether a day holds DOM focus. Removing that day fires no blur, so this
65
- // stays true when a new value or bounds move the grid out from under it.
66
- const gridFocused = useRef(false);
67
- useLayoutEffect(() => {
68
- if (!focusGrid.current && !gridFocused.current)
69
- return;
70
- focusGrid.current = false;
71
- dialogRef.current
72
- ?.querySelector(`[role="grid"] [data-date="${focusDate}"]`)
73
- ?.focus({ preventScroll: true });
74
- }, [focusDate]);
75
- function onKeyDown(event) {
76
- if (event.key === 'Escape') {
77
- event.preventDefault();
78
- event.stopPropagation();
79
- onClose(true);
80
- }
81
- else if (event.key === 'Tab')
82
- wrapTab(event);
83
- }
29
+ const { requestFocus, focusHandlers } = useGridFocus(dialogRef, focusDate, true);
84
30
  const todayUnavailable = isOutsideDateRange(today, min, max);
85
- return (_jsxs("div", { "aria-labelledby": labelId, "aria-modal": "true", className: "sw-date-field-calendar", id: id, onBlur: () => {
86
- gridFocused.current = false;
87
- }, onFocus: (event) => {
88
- gridFocused.current = event.target.closest('[role="grid"]') !== null;
89
- }, onKeyDown: onKeyDown, ref: dialogRef, role: "dialog", children: [_jsx(CalendarHeader, { labels: labels, locale: locale, max: max, min: min, month: month, onMonthChange: (next) => setFocusDate(sameDayIn(next, focusDate)), titleId: titleId, todayYear: monthOf(today).year }), _jsx(CalendarGrid, { focusDate: focusDate, locale: locale, max: max, min: min, month: month, onFocusDate: setFocusDate, onMoveFocus: (next) => {
90
- focusGrid.current = true;
31
+ return (_jsxs(CalendarPopover, { anchorRef: anchorRef, "aria-labelledby": labelId, buttonRef: buttonRef, focusHandlers: focusHandlers, id: id, onClose: onClose, popoverRef: dialogRef, children: [_jsx(CalendarHeader, { labels: labels, locale: locale, max: max, min: min, month: month, onMonthChange: (next) => setFocusDate(sameDayIn(next, focusDate)), titleId: titleId, todayYear: monthOf(today).year }), _jsx(CalendarGrid, { focusDate: focusDate, locale: locale, max: max, min: min, month: month, onFocusDate: setFocusDate, onMoveFocus: (next) => {
32
+ requestFocus();
91
33
  setFocusDate(next);
92
34
  }, onSelect: onSelect, range: range, selected: value, titleId: titleId, today: today, weekStartsOn: weekStartsOn }), _jsxs("div", { className: "sw-date-field-footer", children: [required ? null : (_jsx(Button, { disabled: value === '', onPress: () => onSelect(''), size: "sm", variant: "ghost", children: labels.clear })), _jsx(Button, { disabled: todayUnavailable, onPress: () => onSelect(today), size: "sm", variant: "ghost", children: labels.today })] })] }));
93
35
  }
@@ -1,18 +1,9 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { monthNames, monthTitle } from '../../calendar-labels.js';
3
- import { monthInRange, monthsInRange, shiftMonth, } from '../../calendar-month.js';
3
+ import { monthsInRange } from '../../calendar-month.js';
4
4
  import { calendarYearRange, yearsIn } from '../../calendar-years.js';
5
5
  import { Select } from '../Select.js';
6
- import { DateFieldGlyph } from './DateFieldGlyph.js';
7
- import { tabStop } from '../../tab-stop.js';
8
- function MonthStep({ delta, label, max, min, month, onMonthChange, }) {
9
- const target = shiftMonth(month, delta);
10
- const unavailable = target.year < 1 || target.year > 9999 || !monthInRange(target, min, max);
11
- return (_jsx("button", { "aria-disabled": unavailable || undefined, "aria-label": label, className: "sw-date-field-step", onClick: () => {
12
- if (!unavailable)
13
- onMonthChange(target);
14
- }, tabIndex: tabStop, type: "button", children: _jsx(DateFieldGlyph, { name: delta < 0 ? 'previous' : 'next' }) }));
15
- }
6
+ import { MonthStep } from '../calendar/MonthStep.js';
16
7
  /**
17
8
  * Month stepping plus month and year selectors for jumping far, both cut to
18
9
  * `min` and `max`, over a visually hidden title that announces the shown
@@ -0,0 +1,7 @@
1
+ import { type RangePanelProps } from './range-panel-props.js';
2
+ /**
3
+ * DateRangeButton from `md` up: a popover beside the button with the
4
+ * presets, two months, the range being picked, Cancel and Apply. Escape
5
+ * and a press outside cancel.
6
+ */
7
+ export declare function DateRangePopover(props: RangePanelProps): import("react").JSX.Element;
@@ -0,0 +1,17 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useRef } from 'react';
3
+ import { Button } from '../Button.js';
4
+ import { CalendarPopover } from '../calendar/CalendarPopover.js';
5
+ import { RangeMonths } from './RangeMonths.js';
6
+ import { RangePresets } from './RangePresets.js';
7
+ import { RangeSummary } from './RangeSummary.js';
8
+ /**
9
+ * DateRangeButton from `md` up: a popover beside the button with the
10
+ * presets, two months, the range being picked, Cancel and Apply. Escape
11
+ * and a press outside cancel.
12
+ */
13
+ export function DateRangePopover(props) {
14
+ const popoverRef = useRef(null);
15
+ const { labels, presets } = props;
16
+ return (_jsxs(CalendarPopover, { anchorRef: props.buttonRef, "aria-label": props.label, buttonRef: props.buttonRef, className: "sw-date-range-popover", id: props.id, onClose: props.onCancel, popoverRef: popoverRef, children: [_jsxs("div", { className: "sw-date-range-body", children: [presets.length > 0 ? (_jsx(RangePresets, { label: labels.presets, layout: "list", onChoose: props.onChoosePreset, presets: presets, pressedId: props.pressedPresetId })) : null, _jsx(RangeMonths, { count: 2, draft: props.draft, focusOnMount: true, labels: labels, latest: props.latest, locale: props.locale, max: props.max, min: props.min, onPress: props.onPress, today: props.today, weekStartsOn: props.weekStartsOn })] }), _jsxs("div", { className: "sw-date-range-footer", children: [_jsx(RangeSummary, { count: props.count, hint: labels.hint, line: props.line }), _jsxs("div", { className: "sw-date-range-actions", children: [_jsx(Button, { onPress: () => props.onCancel(true), size: "sm", variant: "neutral", children: labels.cancel }), _jsx(Button, { onPress: props.onApply, size: "sm", children: labels.apply })] })] })] }));
17
+ }
@@ -0,0 +1,7 @@
1
+ import { type RangePanelProps } from './range-panel-props.js';
2
+ /**
3
+ * DateRangeButton below `md`: a bottom sheet titled by its label, the
4
+ * range being picked under the title, the presets in a row, one month,
5
+ * and the hint over Cancel and Apply. Closing it any other way cancels.
6
+ */
7
+ export declare function DateRangeSheet(props: RangePanelProps): import("react").JSX.Element;
@@ -0,0 +1,15 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Button } from '../Button.js';
3
+ import { Dialog } from '../Dialog.js';
4
+ import { RangeMonths } from './RangeMonths.js';
5
+ import { RangePresets } from './RangePresets.js';
6
+ import { RangeSummary } from './RangeSummary.js';
7
+ /**
8
+ * DateRangeButton below `md`: a bottom sheet titled by its label, the
9
+ * range being picked under the title, the presets in a row, one month,
10
+ * and the hint over Cancel and Apply. Closing it any other way cancels.
11
+ */
12
+ export function DateRangeSheet(props) {
13
+ const { labels, presets } = props;
14
+ return (_jsxs(Dialog, { className: "sw-date-range-sheet", closeLabel: labels.close, description: `${props.line} · ${props.count}`, footer: _jsxs("div", { className: "sw-date-range-sheet-footer", children: [_jsx("span", { className: "sw-date-range-hint", children: labels.hint }), _jsxs("div", { className: "sw-date-range-actions", children: [_jsx(Button, { onPress: () => props.onCancel(true), variant: "neutral", children: labels.cancel }), _jsx(Button, { onPress: props.onApply, children: labels.apply })] })] }), id: props.id, onClose: () => props.onCancel(true), open: true, sheetBelow: "md", title: props.label, children: [_jsx(RangeSummary, { count: props.count, line: props.line, visuallyHidden: true }), presets.length > 0 ? (_jsx(RangePresets, { label: labels.presets, layout: "row", onChoose: props.onChoosePreset, presets: presets, pressedId: props.pressedPresetId })) : null, _jsx(RangeMonths, { count: 1, draft: props.draft, focusOnMount: false, labels: labels, latest: props.latest, locale: props.locale, max: props.max, min: props.min, onPress: props.onPress, today: props.today, weekStartsOn: props.weekStartsOn })] }));
15
+ }
@@ -0,0 +1,28 @@
1
+ import { type WeekStart } from '../../calendar-month.js';
2
+ import { type RangeDraft } from '../../date-range-draft.js';
3
+ import { type RangeMonthCount } from '../../date-range-view.js';
4
+ export type RangeMonthsProps = {
5
+ count: RangeMonthCount;
6
+ draft: RangeDraft;
7
+ onPress: (day: string) => void;
8
+ /** The last day a press may pick (`max`, or `maxDays` from the start). */
9
+ latest: string | undefined;
10
+ min?: string;
11
+ max?: string;
12
+ locale: string;
13
+ today: string;
14
+ weekStartsOn: WeekStart;
15
+ labels: {
16
+ previousMonth: string;
17
+ nextMonth: string;
18
+ };
19
+ /** Take DOM focus on the focus day when shown, as a popover does. */
20
+ focusOnMount: boolean;
21
+ };
22
+ /**
23
+ * One month, or two side by side, of the range being picked: each a
24
+ * labelled grid with one roving day across them, the previous step before
25
+ * the first month and the next step after the last. It opens on the
26
+ * range's end, and the months follow the focus day.
27
+ */
28
+ export declare function RangeMonths({ count, draft, onPress, latest, min, max, locale, today, weekStartsOn, labels, focusOnMount, }: RangeMonthsProps): import("react").JSX.Element;
@@ -0,0 +1,50 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useId, useRef, useState } from 'react';
3
+ import { monthTitle } from '../../calendar-labels.js';
4
+ import { firstOfMonth, lastOfMonth, monthsBetween, sameDayIn, shiftMonth, } from '../../calendar-month.js';
5
+ import { firstShownMonth, shownMonths, } from '../../date-range-view.js';
6
+ import { clampDateOnly } from '../../date-only.js';
7
+ import { CalendarGrid } from '../calendar/CalendarGrid.js';
8
+ import { MonthStep } from '../calendar/MonthStep.js';
9
+ import { useGridFocus } from '../calendar/use-grid-focus.js';
10
+ /** `focusDate` moved into the shown months, after a month step. */
11
+ function keepInView(focusDate, first, count) {
12
+ const months = shownMonths(first, count);
13
+ const last = months[months.length - 1] ?? first;
14
+ if (focusDate < firstOfMonth(first))
15
+ return sameDayIn(first, focusDate);
16
+ if (focusDate > lastOfMonth(last))
17
+ return sameDayIn(last, focusDate);
18
+ return focusDate;
19
+ }
20
+ /**
21
+ * One month, or two side by side, of the range being picked: each a
22
+ * labelled grid with one roving day across them, the previous step before
23
+ * the first month and the next step after the last. It opens on the
24
+ * range's end, and the months follow the focus day.
25
+ */
26
+ export function RangeMonths({ count, draft, onPress, latest, min, max, locale, today, weekStartsOn, labels, focusOnMount, }) {
27
+ const id = useId();
28
+ const containerRef = useRef(null);
29
+ const [rawFocusDate, setFocusDate] = useState(draft.range.end);
30
+ // Clamped on every render, so new bounds while open move it inside.
31
+ const focusDate = clampDateOnly(rawFocusDate, min, max);
32
+ const [first, setFirst] = useState(() => firstShownMonth(focusDate, count, undefined, min));
33
+ const shown = firstShownMonth(focusDate, count, first, min);
34
+ if (monthsBetween(first, shown) !== 0)
35
+ setFirst(shown);
36
+ const { requestFocus, focusHandlers } = useGridFocus(containerRef, focusDate, focusOnMount);
37
+ function stepBy(delta) {
38
+ const nextFirst = shiftMonth(shown, delta);
39
+ setFirst(nextFirst);
40
+ setFocusDate(clampDateOnly(keepInView(focusDate, nextFirst, count), min, max));
41
+ }
42
+ const months = shownMonths(shown, count);
43
+ return (_jsxs("div", { className: "sw-date-range-months", ref: containerRef, ...focusHandlers, children: [_jsx("div", { "aria-live": "polite", className: "sw-sr-only", children: months.map((month) => (_jsx("div", { children: monthTitle(month, locale) }, `${month.year}-${month.month}`))) }), months.map((month, index) => {
44
+ const titleId = `${id}-${index}`;
45
+ return (_jsxs("div", { className: "sw-date-range-month", children: [_jsxs("div", { className: "sw-date-range-month-header", children: [index === 0 ? (_jsx(MonthStep, { delta: -1, label: labels.previousMonth, max: max, min: min, month: month, onMonthChange: () => stepBy(-1) })) : null, _jsx("div", { className: "sw-date-range-month-title", id: titleId, children: monthTitle(month, locale) }), index === months.length - 1 ? (_jsx(MonthStep, { delta: 1, label: labels.nextMonth, max: max, min: min, month: month, onMonthChange: () => stepBy(1) })) : null] }), _jsx(CalendarGrid, { blankOutside: true, focusDate: focusDate, latest: latest, locale: locale, max: max, min: min, month: month, onFocusDate: setFocusDate, onMoveFocus: (next) => {
46
+ requestFocus();
47
+ setFocusDate(next);
48
+ }, onSelect: onPress, selected: draft.range, titleId: titleId, today: today, weekStartsOn: weekStartsOn })] }, titleId));
49
+ })] }));
50
+ }
@@ -0,0 +1,16 @@
1
+ import { type DateRangePreset } from '../../date-range-input.js';
2
+ export type RangePresetsProps = {
3
+ presets: readonly DateRangePreset[];
4
+ /** Names the group, such as "Quick ranges". */
5
+ label: string;
6
+ /** The preset whose range is the one shown, if any. */
7
+ pressedId: string | undefined;
8
+ onChoose: (preset: DateRangePreset) => void;
9
+ /** `list` beside the months; `row` above one month, scrolling sideways. */
10
+ layout: 'list' | 'row';
11
+ };
12
+ /**
13
+ * DateRangeButton's presets: a named group of buttons, the one matching
14
+ * the range shown `aria-pressed`. A press applies its range at once.
15
+ */
16
+ export declare function RangePresets({ presets, label, pressedId, onChoose, layout, }: RangePresetsProps): import("react").JSX.Element;
@@ -0,0 +1,10 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { cx } from '../../class-names.js';
3
+ import { tabStop } from '../../tab-stop.js';
4
+ /**
5
+ * DateRangeButton's presets: a named group of buttons, the one matching
6
+ * the range shown `aria-pressed`. A press applies its range at once.
7
+ */
8
+ export function RangePresets({ presets, label, pressedId, onChoose, layout, }) {
9
+ return (_jsx("div", { "aria-label": label, className: cx('sw-date-range-presets', layout === 'row' && 'sw-date-range-presets-row'), role: "group", children: presets.map((preset) => (_jsx("button", { "aria-pressed": preset.id === pressedId, className: "sw-date-range-preset", onClick: () => onChoose(preset), tabIndex: tabStop, type: "button", children: preset.label }, preset.id))) }));
10
+ }
@@ -0,0 +1,15 @@
1
+ export type RangeSummaryProps = {
2
+ /** The range being picked, with its weekdays. */
3
+ line: string;
4
+ /** Its length, such as "7 days". */
5
+ count: string;
6
+ /** How to pick, after the length; left out where the page shows it elsewhere. */
7
+ hint?: string;
8
+ /** Keep the words for assistive technology only, where the page shows them elsewhere. */
9
+ visuallyHidden?: boolean;
10
+ };
11
+ /**
12
+ * The range being picked, in a polite live region so each press is heard:
13
+ * the range on its own line, then its length and the hint.
14
+ */
15
+ export declare function RangeSummary({ line, count, hint, visuallyHidden, }: RangeSummaryProps): import("react").JSX.Element;
@@ -0,0 +1,11 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ /**
3
+ * The range being picked, in a polite live region so each press is heard:
4
+ * the range on its own line, then its length and the hint.
5
+ */
6
+ export function RangeSummary({ line, count, hint, visuallyHidden = false, }) {
7
+ const status = (_jsxs("div", { "aria-atomic": "true", className: "sw-date-range-status", role: "status", children: [_jsx("span", { className: "sw-date-range-line", children: line }), ' ', _jsx("span", { className: "sw-date-range-count", children: count })] }));
8
+ if (visuallyHidden)
9
+ return _jsx("div", { className: "sw-sr-only", children: status });
10
+ return (_jsxs("div", { className: "sw-date-range-summary", children: [status, hint === undefined ? null : (_jsxs(_Fragment, { children: [_jsx("span", { "aria-hidden": "true", className: "sw-date-range-separator", children: ' · ' }), _jsx("span", { className: "sw-date-range-hint", children: hint })] }))] }));
11
+ }
@@ -0,0 +1,31 @@
1
+ import { type RefObject } from 'react';
2
+ import { type WeekStart } from '../../calendar-month.js';
3
+ import { type RangeDraft } from '../../date-range-draft.js';
4
+ import { type DateRangePreset } from '../../date-range-input.js';
5
+ import { type DateRangeButtonLabels } from '../../date-range-labels.js';
6
+ /** What both of DateRangeButton's presentations, popover and sheet, draw. */
7
+ export type RangePanelProps = {
8
+ id: string;
9
+ /** Names the popover or titles the sheet. */
10
+ label: string;
11
+ labels: DateRangeButtonLabels;
12
+ draft: RangeDraft;
13
+ onPress: (day: string) => void;
14
+ presets: readonly DateRangePreset[];
15
+ pressedPresetId: string | undefined;
16
+ onChoosePreset: (preset: DateRangePreset) => void;
17
+ /** Applies the draft. */
18
+ onApply: () => void;
19
+ /** Keeps the value; `true` returns focus to the button. */
20
+ onCancel: (restoreFocus: boolean) => void;
21
+ latest: string | undefined;
22
+ min?: string;
23
+ max?: string;
24
+ locale: string;
25
+ today: string;
26
+ weekStartsOn: WeekStart;
27
+ buttonRef: RefObject<HTMLButtonElement | null>;
28
+ /** The draft's words: the range with weekdays, and its length. */
29
+ line: string;
30
+ count: string;
31
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,21 @@
1
+ import { type RefObject } from 'react';
2
+ import { type RangeDraft } from '../../date-range-draft.js';
3
+ import { type DateOnlyRange } from '../../date-only.js';
4
+ export type RangePopup = {
5
+ buttonRef: RefObject<HTMLButtonElement | null>;
6
+ id: string;
7
+ shown: boolean;
8
+ toggle: () => void;
9
+ draft: RangeDraft;
10
+ press: (day: string) => void;
11
+ /** Closes and gives `range` to `onChange` when it is a new range. */
12
+ apply: (range: DateOnlyRange) => void;
13
+ /** Closes and keeps the value; `true` returns focus to the button. */
14
+ cancel: (restoreFocus: boolean) => void;
15
+ };
16
+ /**
17
+ * DateRangeButton's open state and the range being picked. Opening starts
18
+ * from the value; a new value while open starts over from it. Disabling
19
+ * closes it for good.
20
+ */
21
+ export declare function useRangePopup(value: DateOnlyRange, disabled: boolean, onChange: (value: DateOnlyRange) => void): RangePopup;
@@ -0,0 +1,53 @@
1
+ import { useId, useLayoutEffect, useRef, useState, } from 'react';
2
+ import { pressDay, sameRange, settledDraft, } from '../../date-range-draft.js';
3
+ /**
4
+ * DateRangeButton's open state and the range being picked. Opening starts
5
+ * from the value; a new value while open starts over from it. Disabling
6
+ * closes it for good.
7
+ */
8
+ export function useRangePopup(value, disabled, onChange) {
9
+ const buttonRef = useRef(null);
10
+ const id = useId();
11
+ const [open, setOpen] = useState(false);
12
+ const [draft, setDraft] = useState(() => settledDraft(value));
13
+ const [openedWith, setOpenedWith] = useState(value);
14
+ if (!sameRange(openedWith, value)) {
15
+ setOpenedWith(value);
16
+ setDraft(settledDraft(value));
17
+ }
18
+ if (disabled && open)
19
+ setOpen(false);
20
+ const shown = open && !disabled;
21
+ // Focus returns once the calendar is gone: while the sheet is still a
22
+ // modal dialog, the page behind it, the button included, is inert.
23
+ const restoreFocus = useRef(false);
24
+ useLayoutEffect(() => {
25
+ if (shown || !restoreFocus.current)
26
+ return;
27
+ restoreFocus.current = false;
28
+ buttonRef.current?.focus();
29
+ }, [shown]);
30
+ function cancel(restore) {
31
+ restoreFocus.current = restore;
32
+ setOpen(false);
33
+ }
34
+ return {
35
+ buttonRef,
36
+ id,
37
+ shown,
38
+ toggle: () => {
39
+ if (shown)
40
+ return cancel(true);
41
+ setDraft(settledDraft(value));
42
+ setOpen(true);
43
+ },
44
+ draft,
45
+ press: (day) => setDraft((current) => pressDay(current, day)),
46
+ apply: (range) => {
47
+ cancel(true);
48
+ if (!sameRange(range, value))
49
+ onChange(range);
50
+ },
51
+ cancel,
52
+ };
53
+ }
@@ -0,0 +1,17 @@
1
+ import { type ChartStrokeTone } from '../../css/chart-tones.js';
2
+ import { type LineChartLayout } from '../../line-chart-layout.js';
3
+ export type LineChartDrawingProps = {
4
+ layout: LineChartLayout;
5
+ tone: ChartStrokeTone;
6
+ /** Each marker's tone, in marker order. */
7
+ markerTones: readonly ChartStrokeTone[];
8
+ };
9
+ /**
10
+ * A line chart's picture, hidden from assistive technology (the table
11
+ * beside it carries every figure): the grid and value axis, the boxed
12
+ * period, the area and line in the line's tone with a dot and words at its
13
+ * last value, the date labels, and each marker's triangle and words in its
14
+ * own tone. It is laid out in its own pixels, left to right whatever the
15
+ * writing direction, as a time axis reads.
16
+ */
17
+ export declare function LineChartDrawing({ layout, markerTones, tone, }: LineChartDrawingProps): import("react").JSX.Element;
@@ -0,0 +1,16 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { chartBoxRadius, chartDotRadius } from '../../chart-metrics.js';
3
+ import { svgPoints } from '../../line-chart-path.js';
4
+ import { ChartValueAxis } from '../chart/ChartValueAxis.js';
5
+ /**
6
+ * A line chart's picture, hidden from assistive technology (the table
7
+ * beside it carries every figure): the grid and value axis, the boxed
8
+ * period, the area and line in the line's tone with a dot and words at its
9
+ * last value, the date labels, and each marker's triangle and words in its
10
+ * own tone. It is laid out in its own pixels, left to right whatever the
11
+ * writing direction, as a time axis reads.
12
+ */
13
+ export function LineChartDrawing({ layout, markerTones, tone, }) {
14
+ const { last, plot } = layout;
15
+ return (_jsxs("svg", { "aria-hidden": "true", className: "sw-chart-svg", direction: "ltr", focusable: "false", height: layout.height, viewBox: `0 0 ${layout.width} ${layout.height}`, width: layout.width, children: [_jsx(ChartValueAxis, { grid: true, plot: plot, side: "start", ticks: layout.ticks }), layout.highlight ? (_jsx("rect", { className: "sw-chart-highlight sw-chart-tone-accent", height: layout.highlight.height, rx: chartBoxRadius, width: layout.highlight.width, x: layout.highlight.x, y: layout.highlight.y })) : null, _jsxs("g", { className: `sw-chart-tone-${tone}`, children: [layout.area.length > 0 ? (_jsx("polygon", { className: "sw-chart-area", points: svgPoints(layout.area) })) : null, _jsx("polyline", { className: "sw-chart-line", points: svgPoints(layout.path) }), last && (layout.end || layout.path.length === 1) ? (_jsx("circle", { className: "sw-chart-dot", cx: last.x, cy: last.y, r: chartDotRadius })) : null, layout.end ? (_jsx("text", { className: "sw-chart-end-label", dominantBaseline: "central", x: layout.end.x, y: layout.end.y, children: layout.end.text })) : null] }), layout.markers.map((marker) => (_jsxs("g", { className: `sw-chart-tone-${markerTones[marker.order]}`, children: [_jsx("polygon", { className: "sw-chart-marker", points: marker.triangle }), marker.labelShown ? (_jsx("text", { className: "sw-chart-marker-label", textAnchor: "middle", x: marker.labelX, y: marker.labelY, children: marker.label })) : null] }, marker.order))), layout.dateLabels.map((label) => (_jsx("text", { className: "sw-chart-category", textAnchor: "middle", x: label.x, y: label.y, children: label.text }, label.day)))] }));
16
+ }
@@ -0,0 +1,7 @@
1
+ import { type MouseEvent } from 'react';
2
+ /**
3
+ * Whether a press on a link is a plain one a client router may take: the
4
+ * primary button, no modifier key (which opens a tab or a window) and not
5
+ * already handled. Shared by SectionNav and ListRow.
6
+ */
7
+ export declare function plainPress(event: MouseEvent<HTMLAnchorElement>): boolean;
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Whether a press on a link is a plain one a client router may take: the
3
+ * primary button, no modifier key (which opens a tab or a window) and not
4
+ * already handled. Shared by SectionNav and ListRow.
5
+ */
6
+ export function plainPress(event) {
7
+ return (event.button === 0 &&
8
+ !event.defaultPrevented &&
9
+ !event.metaKey &&
10
+ !event.ctrlKey &&
11
+ !event.shiftKey &&
12
+ !event.altKey);
13
+ }
@@ -0,0 +1,5 @@
1
+ import { type ListRowAccessory } from '../../list-options.js';
2
+ /** A row's chevron or check: control chrome drawn by the generated CSS. */
3
+ export declare function ListRowAccessoryMark({ kind }: {
4
+ kind: ListRowAccessory;
5
+ }): import("react").JSX.Element | null;
@@ -0,0 +1,7 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ /** A row's chevron or check: control chrome drawn by the generated CSS. */
3
+ export function ListRowAccessoryMark({ kind }) {
4
+ if (kind === 'none')
5
+ return null;
6
+ return (_jsx("span", { "aria-hidden": "true", className: kind === 'check' ? 'sw-list-row-check' : 'sw-list-row-chevron' }));
7
+ }
@@ -0,0 +1,20 @@
1
+ import { type ReactNode } from 'react';
2
+ import { type ListRowAccessory, type ListRowTitleAppearance, type ListRowTone, type ListRowValueTone } from '../../list-options.js';
3
+ export type ListRowContentProps = {
4
+ /** Prefixes the ids that name and describe a pressable row. */
5
+ id: string;
6
+ title: string;
7
+ detail?: string;
8
+ value?: string;
9
+ valueDetail?: string;
10
+ valueTone?: ListRowValueTone;
11
+ leading?: ReactNode;
12
+ tone: ListRowTone;
13
+ titleAppearance: ListRowTitleAppearance;
14
+ accessory: ListRowAccessory;
15
+ };
16
+ /**
17
+ * A list row's parts in reading order: the hidden leading mark, the title
18
+ * over its detail, the value over its detail, then the chevron or check.
19
+ */
20
+ export declare function ListRowContent({ id, title, detail, value, valueDetail, valueTone, leading, tone, titleAppearance, accessory, }: ListRowContentProps): import("react").JSX.Element;
@@ -0,0 +1,10 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { cx } from '../../class-names.js';
3
+ import { ListRowAccessoryMark } from './ListRowAccessoryMark.js';
4
+ /**
5
+ * A list row's parts in reading order: the hidden leading mark, the title
6
+ * over its detail, the value over its detail, then the chevron or check.
7
+ */
8
+ export function ListRowContent({ id, title, detail, value, valueDetail, valueTone, leading, tone, titleAppearance, accessory, }) {
9
+ return (_jsxs(_Fragment, { children: [leading === undefined ? null : (_jsx("span", { "aria-hidden": "true", className: "sw-list-row-leading", children: leading })), _jsxs("span", { className: "sw-list-row-label", children: [_jsx("span", { className: cx('sw-list-row-title', titleAppearance === 'plain' && 'sw-list-row-title-plain', tone === 'danger' && 'sw-list-row-title-danger'), id: `${id}-title`, children: title }), detail ? (_jsx("span", { className: "sw-list-row-detail", id: `${id}-detail`, children: detail })) : null] }), value === undefined ? null : (_jsxs("span", { className: "sw-list-row-end", children: [_jsx("span", { className: cx('sw-list-row-value', valueTone && `sw-list-row-value-${valueTone}`), id: `${id}-value`, children: value }), valueDetail ? (_jsx("span", { className: "sw-list-row-value-detail", id: `${id}-value-detail`, children: valueDetail })) : null] })), _jsx(ListRowAccessoryMark, { kind: accessory })] }));
10
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * The element a ListRow renders as: an `li` in a ListGroup's list, a `div`
3
+ * anywhere else, such as a ListGroup's `footer`.
4
+ */
5
+ export declare const ListRowContainer: import("react").Context<"div" | "li">;
@@ -0,0 +1,6 @@
1
+ import { createContext } from 'react';
2
+ /**
3
+ * The element a ListRow renders as: an `li` in a ListGroup's list, a `div`
4
+ * anywhere else, such as a ListGroup's `footer`.
5
+ */
6
+ export const ListRowContainer = createContext('div');
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Whether a media query matches now, following its changes. Where there is
3
+ * no `matchMedia` (a server render, jsdom) it reads as `fallback`.
4
+ */
5
+ export declare function useMediaQuery(query: string, fallback: boolean): boolean;
@@ -0,0 +1,18 @@
1
+ import { useCallback, useSyncExternalStore } from 'react';
2
+ function canMatch() {
3
+ return (typeof window !== 'undefined' && typeof window.matchMedia === 'function');
4
+ }
5
+ /**
6
+ * Whether a media query matches now, following its changes. Where there is
7
+ * no `matchMedia` (a server render, jsdom) it reads as `fallback`.
8
+ */
9
+ export function useMediaQuery(query, fallback) {
10
+ const subscribe = useCallback((onChange) => {
11
+ if (!canMatch())
12
+ return () => undefined;
13
+ const list = window.matchMedia(query);
14
+ list.addEventListener('change', onChange);
15
+ return () => list.removeEventListener('change', onChange);
16
+ }, [query]);
17
+ return useSyncExternalStore(subscribe, () => (canMatch() ? window.matchMedia(query).matches : fallback), () => fallback);
18
+ }
@@ -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;