@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.
- package/CHANGELOG.md +18 -0
- package/README.md +155 -1
- package/dist/calendar-month.d.ts +6 -0
- package/dist/calendar-month.js +15 -0
- package/dist/chart-axis.d.ts +61 -0
- package/dist/chart-axis.js +83 -0
- package/dist/chart-dates.d.ts +6 -0
- package/dist/chart-dates.js +15 -0
- package/dist/chart-metrics.d.ts +40 -0
- package/dist/chart-metrics.js +48 -0
- package/dist/chart-scale.d.ts +48 -0
- package/dist/chart-scale.js +116 -0
- package/dist/chart-table-rows.d.ts +8 -0
- package/dist/chart-table-rows.js +1 -0
- package/dist/column-chart-bars.d.ts +38 -0
- package/dist/column-chart-bars.js +82 -0
- package/dist/column-chart-callouts.d.ts +48 -0
- package/dist/column-chart-callouts.js +54 -0
- package/dist/column-chart-input.d.ts +40 -0
- package/dist/column-chart-input.js +60 -0
- package/dist/column-chart-layout.d.ts +63 -0
- package/dist/column-chart-layout.js +97 -0
- package/dist/components/BarChart.d.ts +1 -1
- package/dist/components/BarChart.js +1 -1
- package/dist/components/ChartLegend.d.ts +38 -0
- package/dist/components/ChartLegend.js +32 -0
- package/dist/components/ColumnChart.d.ts +72 -0
- package/dist/components/ColumnChart.js +71 -0
- package/dist/components/DataBar.d.ts +6 -6
- package/dist/components/DateRangeButton.d.ts +65 -0
- package/dist/components/DateRangeButton.js +54 -0
- package/dist/components/LineChart.d.ts +83 -0
- package/dist/components/LineChart.js +77 -0
- package/dist/components/ListGroup.d.ts +33 -0
- package/dist/components/ListGroup.js +19 -0
- package/dist/components/ListRow.d.ts +51 -0
- package/dist/components/ListRow.js +59 -0
- package/dist/components/NavCount.d.ts +21 -0
- package/dist/components/NavCount.js +23 -0
- package/dist/components/SectionNav.js +1 -8
- package/dist/components/StatTile.d.ts +11 -4
- package/dist/components/StatTile.js +11 -4
- package/dist/components/{date-field → calendar}/CalendarGrid.d.ts +17 -2
- package/dist/components/{date-field → calendar}/CalendarGrid.js +11 -3
- package/dist/components/calendar/CalendarPopover.d.ts +25 -0
- package/dist/components/calendar/CalendarPopover.js +22 -0
- package/dist/components/calendar/MonthStep.d.ts +16 -0
- package/dist/components/calendar/MonthStep.js +17 -0
- package/dist/components/calendar/use-grid-focus.d.ts +18 -0
- package/dist/components/calendar/use-grid-focus.js +35 -0
- package/dist/components/calendar/wrap-tab.d.ts +7 -0
- package/dist/components/calendar/wrap-tab.js +29 -0
- package/dist/components/chart/ChartTable.d.ts +16 -0
- package/dist/components/chart/ChartTable.js +12 -0
- package/dist/components/chart/ChartValueAxis.d.ts +14 -0
- package/dist/components/chart/ChartValueAxis.js +11 -0
- package/dist/components/chart/use-chart-width.d.ts +8 -0
- package/dist/components/chart/use-chart-width.js +34 -0
- package/dist/components/column-chart/ChartCalloutList.d.ts +16 -0
- package/dist/components/column-chart/ChartCalloutList.js +13 -0
- package/dist/components/column-chart/ColumnChartDrawing.d.ts +17 -0
- package/dist/components/column-chart/ColumnChartDrawing.js +18 -0
- package/dist/components/date-field/CalendarDialog.js +9 -67
- package/dist/components/date-field/CalendarHeader.js +2 -11
- package/dist/components/date-range/DateRangePopover.d.ts +7 -0
- package/dist/components/date-range/DateRangePopover.js +17 -0
- package/dist/components/date-range/DateRangeSheet.d.ts +7 -0
- package/dist/components/date-range/DateRangeSheet.js +15 -0
- package/dist/components/date-range/RangeMonths.d.ts +28 -0
- package/dist/components/date-range/RangeMonths.js +50 -0
- package/dist/components/date-range/RangePresets.d.ts +16 -0
- package/dist/components/date-range/RangePresets.js +10 -0
- package/dist/components/date-range/RangeSummary.d.ts +15 -0
- package/dist/components/date-range/RangeSummary.js +11 -0
- package/dist/components/date-range/range-panel-props.d.ts +31 -0
- package/dist/components/date-range/range-panel-props.js +1 -0
- package/dist/components/date-range/use-range-popup.d.ts +21 -0
- package/dist/components/date-range/use-range-popup.js +53 -0
- package/dist/components/line-chart/LineChartDrawing.d.ts +17 -0
- package/dist/components/line-chart/LineChartDrawing.js +16 -0
- package/dist/components/link-press.d.ts +7 -0
- package/dist/components/link-press.js +13 -0
- package/dist/components/list/ListRowAccessoryMark.d.ts +5 -0
- package/dist/components/list/ListRowAccessoryMark.js +7 -0
- package/dist/components/list/ListRowContent.d.ts +20 -0
- package/dist/components/list/ListRowContent.js +10 -0
- package/dist/components/list/list-row-container.d.ts +5 -0
- package/dist/components/list/list-row-container.js +6 -0
- package/dist/components/use-media-query.d.ts +5 -0
- package/dist/components/use-media-query.js +18 -0
- package/dist/css/chart-marks.d.ts +10 -0
- package/dist/css/chart-marks.js +15 -0
- package/dist/css/chart-tones.d.ts +68 -0
- package/dist/css/chart-tones.js +130 -0
- package/dist/css/chevron.d.ts +6 -0
- package/dist/css/chevron.js +14 -0
- package/dist/css/{css-chart.d.ts → css-bar-chart.d.ts} +2 -2
- package/dist/css/{css-chart.js → css-bar-chart.js} +2 -2
- package/dist/css/css-button-group-attached.js +8 -4
- package/dist/css/css-button-group-track.js +1 -1
- package/dist/css/css-chart-legend.d.ts +5 -0
- package/dist/css/css-chart-legend.js +102 -0
- package/dist/css/css-chart-plot.d.ts +2 -0
- package/dist/css/css-chart-plot.js +170 -0
- package/dist/css/css-checkbox.js +2 -2
- package/dist/css/css-components.js +23 -2
- package/dist/css/css-count-pill.d.ts +2 -0
- package/dist/css/css-count-pill.js +54 -0
- package/dist/css/css-data-bar.d.ts +4 -9
- package/dist/css/css-data-bar.js +15 -31
- package/dist/css/css-date-field.js +3 -2
- package/dist/css/css-date-range-presets.d.ts +2 -0
- package/dist/css/css-date-range-presets.js +109 -0
- package/dist/css/css-date-range.d.ts +2 -0
- package/dist/css/css-date-range.js +197 -0
- package/dist/css/css-line-chart.d.ts +2 -0
- package/dist/css/css-line-chart.js +31 -0
- package/dist/css/css-list-row.d.ts +2 -0
- package/dist/css/css-list-row.js +193 -0
- package/dist/css/css-list.d.ts +3 -0
- package/dist/css/css-list.js +85 -0
- package/dist/css/css-scroll-region.d.ts +18 -0
- package/dist/css/css-scroll-region.js +41 -0
- package/dist/css/css-section-nav-filled.js +2 -25
- package/dist/css/css-stat-tile.d.ts +7 -0
- package/dist/css/css-stat-tile.js +16 -0
- package/dist/css/css-steps.js +2 -2
- package/dist/css/css-table-bounded-edges.js +4 -27
- package/dist/css/range-band.d.ts +2 -0
- package/dist/css/range-band.js +7 -0
- package/dist/css/stacking.js +2 -2
- package/dist/css/stylesheet.js +16 -2
- package/dist/date-range-draft.d.ts +26 -0
- package/dist/date-range-draft.js +36 -0
- package/dist/date-range-input.d.ts +26 -0
- package/dist/date-range-input.js +58 -0
- package/dist/date-range-labels.d.ts +22 -0
- package/dist/date-range-labels.js +26 -0
- package/dist/date-range-view.d.ts +12 -0
- package/dist/date-range-view.js +29 -0
- package/dist/date-range-words.d.ts +11 -0
- package/dist/date-range-words.js +40 -0
- package/dist/index.d.ts +9 -1
- package/dist/index.js +9 -1
- package/dist/is-one-of.d.ts +5 -0
- package/dist/is-one-of.js +7 -0
- package/dist/line-chart-input.d.ts +39 -0
- package/dist/line-chart-input.js +70 -0
- package/dist/line-chart-layout.d.ts +68 -0
- package/dist/line-chart-layout.js +99 -0
- package/dist/line-chart-markers.d.ts +29 -0
- package/dist/line-chart-markers.js +57 -0
- package/dist/line-chart-path.d.ts +18 -0
- package/dist/line-chart-path.js +33 -0
- package/dist/line-chart-points.d.ts +6 -0
- package/dist/line-chart-points.js +8 -0
- package/dist/line-chart-table.d.ts +15 -0
- package/dist/line-chart-table.js +29 -0
- package/dist/list-options.d.ts +70 -0
- package/dist/list-options.js +77 -0
- package/dist/styles.css +726 -26
- 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,
|
|
3
|
-
import { monthOf, } from '../../calendar-month.js';
|
|
4
|
-
import { clampDateOnly,
|
|
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 '
|
|
6
|
+
import { CalendarGrid } from '../calendar/CalendarGrid.js';
|
|
7
7
|
import { CalendarHeader } from './CalendarHeader.js';
|
|
8
|
-
import {
|
|
9
|
-
|
|
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
|
-
|
|
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(
|
|
86
|
-
|
|
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 {
|
|
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 {
|
|
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,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,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;
|