@scalewing/react 1.39.0 → 1.41.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 +33 -0
- package/README.md +255 -5
- package/dist/calendar-labels.d.ts +2 -0
- package/dist/calendar-labels.js +7 -0
- package/dist/calendar-month.d.ts +6 -0
- package/dist/calendar-month.js +15 -0
- package/dist/components/BarChart.d.ts +20 -4
- package/dist/components/BarChart.js +13 -6
- package/dist/components/ButtonGroup.d.ts +8 -1
- package/dist/components/ButtonGroup.js +11 -2
- package/dist/components/DateField.d.ts +14 -2
- package/dist/components/DateField.js +14 -11
- package/dist/components/DateRangeButton.d.ts +65 -0
- package/dist/components/DateRangeButton.js +54 -0
- package/dist/components/DenominationGrid.d.ts +3 -2
- package/dist/components/DenominationGrid.js +21 -3
- package/dist/components/Dialog.d.ts +29 -13
- package/dist/components/Dialog.js +22 -20
- package/dist/components/Field.d.ts +13 -2
- package/dist/components/Field.js +14 -38
- package/dist/components/FilterChips.d.ts +7 -37
- package/dist/components/FilterChips.js +32 -24
- package/dist/components/ListGroup.d.ts +33 -0
- package/dist/components/ListGroup.js +19 -0
- package/dist/components/ListRow.d.ts +60 -0
- package/dist/components/ListRow.js +60 -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/SegmentedControl.d.ts +17 -1
- package/dist/components/SegmentedControl.js +11 -3
- package/dist/components/StatTile.d.ts +11 -4
- package/dist/components/StatTile.js +11 -4
- package/dist/components/Switch.d.ts +16 -1
- package/dist/components/Switch.js +17 -6
- package/dist/components/action-bar/ActionBarSummary.d.ts +10 -2
- package/dist/components/action-bar/ActionBarSummary.js +11 -2
- 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/date-field/CalendarDialog.js +9 -67
- package/dist/components/date-field/CalendarEntryButton.d.ts +24 -0
- package/dist/components/date-field/CalendarEntryButton.js +16 -0
- package/dist/components/date-field/CalendarHeader.js +2 -11
- package/dist/components/date-field/TypedEntry.d.ts +26 -0
- package/dist/components/date-field/TypedEntry.js +13 -0
- 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/denomination-grid/CountCell.d.ts +16 -0
- package/dist/components/denomination-grid/CountCell.js +14 -0
- package/dist/components/denomination-grid/DenominationStrip.d.ts +4 -1
- package/dist/components/denomination-grid/DenominationStrip.js +2 -2
- package/dist/components/denomination-grid/EntryCell.d.ts +3 -1
- package/dist/components/denomination-grid/EntryCell.js +2 -2
- package/dist/components/denomination-grid/StripRow.js +1 -10
- package/dist/components/denomination-grid/TransposedStrip.d.ts +17 -0
- package/dist/components/denomination-grid/TransposedStrip.js +43 -0
- package/dist/components/denomination-grid/types.d.ts +31 -0
- package/dist/components/denomination-grid/types.js +7 -1
- package/dist/components/denomination-grid/validate-options.d.ts +7 -0
- package/dist/components/denomination-grid/validate-options.js +27 -0
- package/dist/components/dialog/DialogFooter.d.ts +6 -5
- package/dist/components/dialog/DialogFooter.js +6 -6
- package/dist/components/dialog/validate.d.ts +24 -0
- package/dist/components/dialog/validate.js +23 -0
- package/dist/components/field/field-checks.d.ts +34 -0
- package/dist/components/field/field-checks.js +42 -0
- package/dist/components/filter-chips/FilterChip.d.ts +16 -0
- package/dist/components/filter-chips/FilterChip.js +20 -0
- package/dist/components/filter-chips/options.d.ts +75 -0
- package/dist/components/filter-chips/options.js +11 -0
- package/dist/components/filter-chips/validate.d.ts +9 -0
- package/dist/components/filter-chips/validate.js +48 -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 +22 -0
- package/dist/components/list/ListRowContent.js +11 -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-tones.d.ts +2 -0
- package/dist/css/chart-tones.js +5 -1
- package/dist/css/chevron.d.ts +6 -0
- package/dist/css/chevron.js +14 -0
- package/dist/css/css-bar-chart.d.ts +17 -2
- package/dist/css/css-bar-chart.js +38 -7
- package/dist/css/css-button-group-attached.d.ts +3 -0
- package/dist/css/css-button-group-attached.js +26 -5
- package/dist/css/css-button-group-track.js +1 -1
- package/dist/css/css-canvas-grouped.d.ts +17 -0
- package/dist/css/css-canvas-grouped.js +40 -0
- package/dist/css/css-checkbox.js +2 -2
- package/dist/css/css-components.js +15 -0
- package/dist/css/css-count-pill.d.ts +2 -0
- package/dist/css/css-count-pill.js +54 -0
- package/dist/css/css-date-field-calendar-entry.d.ts +11 -0
- package/dist/css/css-date-field-calendar-entry.js +50 -0
- package/dist/css/css-date-field.js +7 -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-denomination-grid.js +4 -0
- package/dist/css/css-denomination-transposed.d.ts +4 -0
- package/dist/css/css-denomination-transposed.js +178 -0
- package/dist/css/css-dialog-canvas.d.ts +3 -0
- package/dist/css/css-dialog-canvas.js +20 -0
- package/dist/css/css-dialog-frame.js +19 -2
- package/dist/css/css-dialog-grabber.d.ts +4 -0
- package/dist/css/css-dialog-grabber.js +56 -0
- package/dist/css/css-dialog.js +3 -2
- package/dist/css/css-document.js +3 -0
- package/dist/css/css-field-width.d.ts +12 -0
- package/dist/css/css-field-width.js +49 -0
- package/dist/css/css-field.js +4 -0
- package/dist/css/css-filter-chips-layout.d.ts +2 -0
- package/dist/css/css-filter-chips-layout.js +42 -0
- package/dist/css/css-filter-chips.js +4 -0
- package/dist/css/css-list-row.d.ts +8 -0
- package/dist/css/css-list-row.js +209 -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-segmented-sizing.d.ts +13 -0
- package/dist/css/css-segmented-sizing.js +66 -0
- package/dist/css/css-segmented.js +4 -0
- 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-switch.d.ts +6 -0
- package/dist/css/css-switch.js +39 -0
- 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.d.ts +1 -0
- package/dist/css/stacking.js +6 -3
- package/dist/css/stylesheet.js +10 -0
- package/dist/date-field-labels.d.ts +5 -0
- package/dist/date-field-labels.js +1 -0
- 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 +16 -9
- package/dist/index.js +14 -7
- package/dist/list-options.d.ts +71 -0
- package/dist/list-options.js +84 -0
- package/dist/styles.css +863 -41
- package/dist/theme/ThemeProvider.d.ts +11 -1
- package/dist/theme/ThemeProvider.js +4 -2
- package/package.json +1 -1
|
@@ -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,16 @@
|
|
|
1
|
+
import { type DenominationGridRow } from './types.js';
|
|
2
|
+
type CountCellProps = {
|
|
3
|
+
row: DenominationGridRow;
|
|
4
|
+
index: number;
|
|
5
|
+
zeroLabel: string;
|
|
6
|
+
/** More classes for the cell, such as the transposed layout's row look. */
|
|
7
|
+
className?: string;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* One count, read-only: the zero label, a signed delta, and the cell's tone.
|
|
11
|
+
* A signed row's positive count is success; a negative count is danger
|
|
12
|
+
* unless its row's `negativeAppearance` is `plain`. The value has its own
|
|
13
|
+
* box, which the `pill` tone appearance fills.
|
|
14
|
+
*/
|
|
15
|
+
export declare function CountCell({ row, index, zeroLabel, className, }: CountCellProps): import("react").JSX.Element;
|
|
16
|
+
export {};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { cx } from '../../class-names.js';
|
|
3
|
+
import { denominationCellToneClassNames } from '../../css/css-denomination-cell-tones.js';
|
|
4
|
+
import { denominationCellView } from '../../denomination-cells.js';
|
|
5
|
+
/**
|
|
6
|
+
* One count, read-only: the zero label, a signed delta, and the cell's tone.
|
|
7
|
+
* A signed row's positive count is success; a negative count is danger
|
|
8
|
+
* unless its row's `negativeAppearance` is `plain`. The value has its own
|
|
9
|
+
* box, which the `pill` tone appearance fills.
|
|
10
|
+
*/
|
|
11
|
+
export function CountCell({ row, index, zeroLabel, className, }) {
|
|
12
|
+
const view = denominationCellView(row.cells[index] ?? null, row.signed ?? false, zeroLabel);
|
|
13
|
+
return (_jsx("td", { className: cx('sw-denomination-cell', `sw-denomination-cell-${view.state}`, row.signed && 'sw-denomination-cell-signed', row.negativeAppearance === 'plain' && 'sw-denomination-cell-plain', ...denominationCellToneClassNames(row.cellTones?.[index]), className), children: _jsx("span", { className: "sw-denomination-cell-value", children: view.text }) }));
|
|
14
|
+
}
|
|
@@ -1,9 +1,12 @@
|
|
|
1
|
+
import { type CSSProperties } from 'react';
|
|
1
2
|
import { type DenominationGridProps } from './types.js';
|
|
2
3
|
type StripProps = Required<Pick<DenominationGridProps, 'label' | 'columns' | 'rows' | 'zeroLabel'>> & Pick<DenominationGridProps, 'totalLabel' | 'labelWidth'> & {
|
|
3
4
|
/** The grid's appearance classes (labels, totals, cell tones), if any. */
|
|
4
5
|
className?: string;
|
|
5
6
|
/** The `table` frame: a hairline around the region and a heading-row fill. */
|
|
6
7
|
framed: boolean;
|
|
8
|
+
/** The entry counts' room (`countDigits`), if set. */
|
|
9
|
+
countStyle?: CSSProperties;
|
|
7
10
|
};
|
|
8
|
-
export declare function DenominationStrip({ className, framed, label, columns, rows, zeroLabel, totalLabel, labelWidth, }: StripProps): import("react").JSX.Element;
|
|
11
|
+
export declare function DenominationStrip({ className, countStyle, framed, label, columns, rows, zeroLabel, totalLabel, labelWidth, }: StripProps): import("react").JSX.Element;
|
|
9
12
|
export {};
|
|
@@ -18,7 +18,7 @@ function TotalHead({ totalLabel }) {
|
|
|
18
18
|
return _jsx("td", { className: "sw-denomination-corner sw-denomination-total-head" });
|
|
19
19
|
return (_jsx("th", { className: "sw-denomination-head sw-denomination-total-head", scope: "col", children: _jsx("span", { className: "sw-denomination-total-label", children: totalLabel }) }));
|
|
20
20
|
}
|
|
21
|
-
export function DenominationStrip({ className, framed, label, columns, rows, zeroLabel, totalLabel, labelWidth, }) {
|
|
21
|
+
export function DenominationStrip({ className, countStyle, framed, label, columns, rows, zeroLabel, totalLabel, labelWidth, }) {
|
|
22
22
|
const idPrefix = useId();
|
|
23
23
|
const hasTotals = rows.some((row) => row.total !== undefined);
|
|
24
24
|
// An aligned strip sizes its columns from the region's width; it needs
|
|
@@ -29,5 +29,5 @@ export function DenominationStrip({ className, framed, label, columns, rows, zer
|
|
|
29
29
|
'--sw-denomination-columns': columns.length,
|
|
30
30
|
'--sw-denomination-total': hasTotals ? 1 : 0,
|
|
31
31
|
};
|
|
32
|
-
return (_jsx(ScrollRegion, { "aria-label": label, className: cx('sw-denomination-scroll', aligned && 'sw-denomination-scroll-aligned', framed && denominationScrollFramedClass), children: _jsxs("table", { className: cx('sw-denomination-grid sw-denomination-strip', labelWidth && 'sw-denomination-strip-aligned', rows.some((row) => row.entry) && 'sw-denomination-strip-entry', labelWidth && denominationLabelWidthClass(labelWidth), framed && denominationFramedClass, className), style: aligned, children: [_jsx("caption", { className: "sw-sr-only", children: label }), _jsx("thead", { children: _jsxs("tr", { children: [_jsx("td", { className: "sw-denomination-corner" }), columns.map((column) => (_jsx("th", { className: "sw-denomination-head", scope: "col", children: _jsx(ColumnHeadText, { column: column }) }, column.key))), hasTotals ? _jsx(TotalHead, { totalLabel: totalLabel }) : null] }) }), _jsx("tbody", { children: rows.map((row, rowIndex) => (_jsx(StripRow, { columns: columns, hasTotals: hasTotals, idPrefix: idPrefix, row: row, rowIndex: rowIndex, zeroLabel: zeroLabel }, row.id))) })] }) }));
|
|
32
|
+
return (_jsx(ScrollRegion, { "aria-label": label, className: cx('sw-denomination-scroll', aligned && 'sw-denomination-scroll-aligned', framed && denominationScrollFramedClass), children: _jsxs("table", { className: cx('sw-denomination-grid sw-denomination-strip', labelWidth && 'sw-denomination-strip-aligned', rows.some((row) => row.entry) && 'sw-denomination-strip-entry', labelWidth && denominationLabelWidthClass(labelWidth), framed && denominationFramedClass, className), style: aligned || countStyle ? { ...aligned, ...countStyle } : undefined, children: [_jsx("caption", { className: "sw-sr-only", children: label }), _jsx("thead", { children: _jsxs("tr", { children: [_jsx("td", { className: "sw-denomination-corner" }), columns.map((column) => (_jsx("th", { className: "sw-denomination-head", scope: "col", children: _jsx(ColumnHeadText, { column: column }) }, column.key))), hasTotals ? _jsx(TotalHead, { totalLabel: totalLabel }) : null] }) }), _jsx("tbody", { children: rows.map((row, rowIndex) => (_jsx(StripRow, { columns: columns, hasTotals: hasTotals, idPrefix: idPrefix, row: row, rowIndex: rowIndex, zeroLabel: zeroLabel }, row.id))) })] }) }));
|
|
33
33
|
}
|
|
@@ -6,6 +6,8 @@ type EntryCellProps = {
|
|
|
6
6
|
index: number;
|
|
7
7
|
/** The value cell under this count, which describes it, if any. */
|
|
8
8
|
describedBy?: string;
|
|
9
|
+
/** More classes for the cell, such as the transposed layout's row look. */
|
|
10
|
+
className?: string;
|
|
9
11
|
};
|
|
10
12
|
/**
|
|
11
13
|
* One editable count of a strip row: a full-width `Stepper` named by the
|
|
@@ -13,5 +15,5 @@ type EntryCellProps = {
|
|
|
13
15
|
* message from the entry, and the cell's tone on its border (an accent one
|
|
14
16
|
* is the Stepper's changed look).
|
|
15
17
|
*/
|
|
16
|
-
export declare function EntryCell({ row, entry, column, index, describedBy, }: EntryCellProps): import("react").JSX.Element;
|
|
18
|
+
export declare function EntryCell({ row, entry, column, index, describedBy, className, }: EntryCellProps): import("react").JSX.Element;
|
|
17
19
|
export {};
|
|
@@ -9,8 +9,8 @@ import { columnName } from './ColumnHeadText.js';
|
|
|
9
9
|
* message from the entry, and the cell's tone on its border (an accent one
|
|
10
10
|
* is the Stepper's changed look).
|
|
11
11
|
*/
|
|
12
|
-
export function EntryCell({ row, entry, column, index, describedBy, }) {
|
|
12
|
+
export function EntryCell({ row, entry, column, index, describedBy, className, }) {
|
|
13
13
|
const max = entry.max?.[index] ?? undefined;
|
|
14
14
|
const tone = row.cellTones?.[index];
|
|
15
|
-
return (_jsx("td", { className: cx('sw-denomination-entry', ...denominationCellToneClassNames(tone)), children: _jsx(Stepper, { "aria-describedby": describedBy, changed: tone === 'accent', decrementLabel: entry.decrementLabel, disabled: row.disabled, error: entry.errors?.[index] ?? undefined, incrementLabel: entry.incrementLabel, label: `${row.label} ${columnName(column)}`, labelVisuallyHidden: true, limitAppearance: entry.limitAppearance, max: max, onChange: (value) => entry.onChange(column.key, value), placeholder: entry.placeholder, value: row.cells[index] ?? null, width: "full" }) }));
|
|
15
|
+
return (_jsx("td", { className: cx('sw-denomination-entry', ...denominationCellToneClassNames(tone), className), children: _jsx(Stepper, { "aria-describedby": describedBy, changed: tone === 'accent', decrementLabel: entry.decrementLabel, disabled: row.disabled, error: entry.errors?.[index] ?? undefined, incrementLabel: entry.incrementLabel, label: `${row.label} ${columnName(column)}`, labelVisuallyHidden: true, limitAppearance: entry.limitAppearance, max: max, onChange: (value) => entry.onChange(column.key, value), placeholder: entry.placeholder, value: row.cells[index] ?? null, width: "full" }) }));
|
|
16
16
|
}
|
|
@@ -1,19 +1,10 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { cx } from '../../class-names.js';
|
|
3
|
-
import {
|
|
4
|
-
import { denominationCellView } from '../../denomination-cells.js';
|
|
3
|
+
import { CountCell } from './CountCell.js';
|
|
5
4
|
import { EntryCell } from './EntryCell.js';
|
|
6
5
|
import { RowLabelLine } from './RowLabelLine.js';
|
|
7
6
|
import { StripTotalCell, StripTotalInline } from './StripTotal.js';
|
|
8
7
|
import { hasValue, StripValueRow, valueCellId } from './StripValueRow.js';
|
|
9
|
-
/**
|
|
10
|
-
* One count, read-only: the zero label, a signed delta, and the cell's tone.
|
|
11
|
-
* The value has its own box, which the `pill` tone appearance fills.
|
|
12
|
-
*/
|
|
13
|
-
function CountCell({ row, index, zeroLabel, }) {
|
|
14
|
-
const view = denominationCellView(row.cells[index] ?? null, row.signed ?? false, zeroLabel);
|
|
15
|
-
return (_jsx("td", { className: cx('sw-denomination-cell', `sw-denomination-cell-${view.state}`, ...denominationCellToneClassNames(row.cellTones?.[index])), children: _jsx("span", { className: "sw-denomination-cell-value", children: view.text }) }));
|
|
16
|
-
}
|
|
17
8
|
/**
|
|
18
9
|
* One strip row: its pinned label (with the phone total under it), a count
|
|
19
10
|
* per column, read-only or entered, and its total. With a `valueRow`, the
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type CSSProperties } from 'react';
|
|
2
|
+
import { type DenominationGridProps } from './types.js';
|
|
3
|
+
type TransposedProps = Required<Pick<DenominationGridProps, 'label' | 'columns' | 'rows' | 'zeroLabel'>> & Pick<DenominationGridProps, 'totalLabel'> & {
|
|
4
|
+
/** The grid's appearance classes (cell tones), if any. */
|
|
5
|
+
className?: string;
|
|
6
|
+
/** The entry counts' room, as the strip's. */
|
|
7
|
+
style?: CSSProperties;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* A strip drawn for a phone: a row per denomination (its label and
|
|
11
|
+
* caption), a column per grid row (its counts, read-only or entered, then
|
|
12
|
+
* its value line's values), and the totals in a last row named by
|
|
13
|
+
* `totalLabel`. Names, entry, tones and the zero label are the strip's;
|
|
14
|
+
* the denomination column stays in view while the columns scroll under it.
|
|
15
|
+
*/
|
|
16
|
+
export declare function TransposedStrip({ className, style, label, columns, rows, zeroLabel, totalLabel, }: TransposedProps): import("react").JSX.Element;
|
|
17
|
+
export {};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Fragment, useId } from 'react';
|
|
3
|
+
import { cx } from '../../class-names.js';
|
|
4
|
+
import { ScrollRegion } from '../ScrollRegion.js';
|
|
5
|
+
import { ColumnHeadText } from './ColumnHeadText.js';
|
|
6
|
+
import { CountCell } from './CountCell.js';
|
|
7
|
+
import { EntryCell } from './EntryCell.js';
|
|
8
|
+
import { RowLabelLine } from './RowLabelLine.js';
|
|
9
|
+
import { hasValue, valueCellId } from './StripValueRow.js';
|
|
10
|
+
/** The look a grid row lends each of its cells in a column. */
|
|
11
|
+
function columnClass(row) {
|
|
12
|
+
return (cx(row.emphasis === 'quiet' && 'sw-denomination-transposed-quiet', row.disabled && 'sw-denomination-transposed-disabled') || undefined);
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* A grid row's heads: its label (glyph, words, caption), then its value
|
|
16
|
+
* line's. A disabled row says so on its heads (a column header may be
|
|
17
|
+
* `aria-disabled`), as the strip's row does, since a column has no element
|
|
18
|
+
* of its own.
|
|
19
|
+
*/
|
|
20
|
+
function RowHeads({ row }) {
|
|
21
|
+
return (_jsxs(_Fragment, { children: [_jsx("th", { "aria-disabled": row.disabled || undefined, className: cx('sw-denomination-head sw-denomination-transposed-head', columnClass(row)), scope: "col", children: _jsx(RowLabelLine, { row: row }) }), row.valueRow ? (_jsx("th", { "aria-disabled": row.disabled || undefined, "aria-label": `${row.label} ${row.valueRow.label}`, className: cx('sw-denomination-head sw-denomination-transposed-head', columnClass(row)), scope: "col", children: row.valueRow.label })) : null] }));
|
|
22
|
+
}
|
|
23
|
+
/** A grid row's total: under its value line when it has one, else under its counts. */
|
|
24
|
+
function RowTotal({ row }) {
|
|
25
|
+
const total = (_jsx("td", { className: cx('sw-denomination-transposed-total', `sw-denomination-row-${row.tone ?? 'neutral'}`, columnClass(row)), children: row.total ?? '' }));
|
|
26
|
+
return row.valueRow ? (_jsxs(_Fragment, { children: [_jsx("td", { className: columnClass(row) }), total] })) : (total);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* A strip drawn for a phone: a row per denomination (its label and
|
|
30
|
+
* caption), a column per grid row (its counts, read-only or entered, then
|
|
31
|
+
* its value line's values), and the totals in a last row named by
|
|
32
|
+
* `totalLabel`. Names, entry, tones and the zero label are the strip's;
|
|
33
|
+
* the denomination column stays in view while the columns scroll under it.
|
|
34
|
+
*/
|
|
35
|
+
export function TransposedStrip({ className, style, label, columns, rows, zeroLabel, totalLabel, }) {
|
|
36
|
+
const idPrefix = useId();
|
|
37
|
+
const hasTotals = rows.some((row) => row.total !== undefined);
|
|
38
|
+
return (_jsx(ScrollRegion, { "aria-label": label, className: "sw-denomination-scroll sw-denomination-scroll-transposed", children: _jsxs("table", { className: cx('sw-denomination-grid sw-denomination-transposed', rows.some((row) => row.entry) && 'sw-denomination-transposed-entry', className), style: style, children: [_jsx("caption", { className: "sw-sr-only", children: label }), _jsx("thead", { children: _jsxs("tr", { children: [_jsx("td", { className: "sw-denomination-corner" }), rows.map((row) => (_jsx(RowHeads, { row: row }, row.id)))] }) }), _jsx("tbody", { children: columns.map((column, index) => (_jsxs("tr", { className: "sw-denomination-row", children: [_jsx("th", { className: "sw-denomination-transposed-label", scope: "row", children: _jsx(ColumnHeadText, { column: column }) }), rows.map((row, rowIndex) => {
|
|
39
|
+
const value = row.valueRow?.cells[index];
|
|
40
|
+
const valueId = valueCellId(idPrefix, rowIndex, index);
|
|
41
|
+
return (_jsxs(Fragment, { children: [row.entry ? (_jsx(EntryCell, { className: columnClass(row), column: column, describedBy: hasValue(value) ? valueId : undefined, entry: row.entry, index: index, row: row })) : (_jsx(CountCell, { className: columnClass(row), index: index, row: row, zeroLabel: zeroLabel })), row.valueRow ? (_jsx("td", { className: cx('sw-denomination-transposed-value', columnClass(row)), id: valueId, children: hasValue(value) ? value : '' })) : null] }, row.id));
|
|
42
|
+
})] }, column.key))) }), hasTotals ? (_jsx("tfoot", { children: _jsxs("tr", { className: "sw-denomination-row", children: [totalLabel === undefined ? (_jsx("td", { className: "sw-denomination-corner" })) : (_jsx("th", { className: "sw-denomination-transposed-label", scope: "row", children: totalLabel })), rows.map((row) => (_jsx(RowTotal, { row: row }, row.id)))] }) })) : null] }) }));
|
|
43
|
+
}
|
|
@@ -8,6 +8,15 @@ import { type StepperLimitAppearance } from '../Stepper.js';
|
|
|
8
8
|
export type { DenominationCellToneAppearance, DenominationGridFrame, DenominationLabelWidth, DenominationTotalEmphasis, };
|
|
9
9
|
export type DenominationGridTone = BadgeTone;
|
|
10
10
|
export type DenominationGridLayout = 'strip' | 'tiles';
|
|
11
|
+
/** How a negative count reads: in the danger colour, or plain text. */
|
|
12
|
+
export declare const denominationNegativeAppearances: readonly ["danger", "plain"];
|
|
13
|
+
export type DenominationGridNegativeAppearance = (typeof denominationNegativeAppearances)[number];
|
|
14
|
+
/**
|
|
15
|
+
* The widths below which a strip is drawn transposed: the denominations as
|
|
16
|
+
* rows, the grid's rows as columns.
|
|
17
|
+
*/
|
|
18
|
+
export declare const denominationTransposeBreakpoints: readonly ["md", "lg"];
|
|
19
|
+
export type DenominationTransposeBreakpoint = (typeof denominationTransposeBreakpoints)[number];
|
|
11
20
|
/**
|
|
12
21
|
* What a named tiles row is to assistive technology: a `region` (a landmark,
|
|
13
22
|
* the default) or a `group` (named, but not in the landmark list).
|
|
@@ -77,6 +86,13 @@ export type DenominationGridRow = {
|
|
|
77
86
|
cellTones?: readonly (DenominationGridTone | null)[];
|
|
78
87
|
/** A muted line under the label, such as "from the customer". */
|
|
79
88
|
caption?: string;
|
|
89
|
+
/**
|
|
90
|
+
* How a negative count reads: `danger` (the default) in the danger
|
|
91
|
+
* colour; `plain` in the text colour, with its minus sign, for counts
|
|
92
|
+
* that go out by design, such as bills paid out (cash out is never an
|
|
93
|
+
* error, so never red).
|
|
94
|
+
*/
|
|
95
|
+
negativeAppearance?: DenominationGridNegativeAppearance;
|
|
80
96
|
/** The `strip` layout only. */
|
|
81
97
|
emphasis?: DenominationGridRowEmphasis;
|
|
82
98
|
/**
|
|
@@ -164,4 +180,19 @@ export type DenominationGridProps = {
|
|
|
164
180
|
* changed count in accent. Entry counts and tiles keep their tone border.
|
|
165
181
|
*/
|
|
166
182
|
cellToneAppearance?: DenominationCellToneAppearance;
|
|
183
|
+
/**
|
|
184
|
+
* Below this breakpoint (`md` or `lg`) the strip is drawn transposed,
|
|
185
|
+
* for a phone: one row per denomination (its label and caption) and one
|
|
186
|
+
* column per grid row (its label over its counts, entry steppers or
|
|
187
|
+
* value line), the totals in a last row. Its names, entry, cell tones
|
|
188
|
+
* and zero label are the strip's. The `strip` layout only.
|
|
189
|
+
*/
|
|
190
|
+
transposeBelow?: DenominationTransposeBreakpoint;
|
|
191
|
+
/**
|
|
192
|
+
* The digits an entry count holds without clipping, from 3 (the
|
|
193
|
+
* default room) to 6: a column of steppers keeps that many digits'
|
|
194
|
+
* width before the strip scrolls, such as 4 for a count in the
|
|
195
|
+
* thousands in a grid of seven columns. Entry rows only.
|
|
196
|
+
*/
|
|
197
|
+
countDigits?: number;
|
|
167
198
|
};
|
|
@@ -1 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
/** How a negative count reads: in the danger colour, or plain text. */
|
|
2
|
+
export const denominationNegativeAppearances = ['danger', 'plain'];
|
|
3
|
+
/**
|
|
4
|
+
* The widths below which a strip is drawn transposed: the denominations as
|
|
5
|
+
* rows, the grid's rows as columns.
|
|
6
|
+
*/
|
|
7
|
+
export const denominationTransposeBreakpoints = ['md', 'lg'];
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type DenominationGridLayout, type DenominationGridRow } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* The grid's later options: a known negative appearance per row, a known
|
|
4
|
+
* transpose breakpoint on a strip only, and a whole count of digits (3 to
|
|
5
|
+
* 6) for a grid with entry rows.
|
|
6
|
+
*/
|
|
7
|
+
export declare function assertDenominationOptions(layout: DenominationGridLayout, rows: readonly DenominationGridRow[], transposeBelow: unknown, countDigits: unknown): void;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { denominationNegativeAppearances, denominationTransposeBreakpoints, } from './types.js';
|
|
2
|
+
/** The digits an entry count can be given room for. */
|
|
3
|
+
const countDigitsRange = { min: 3, max: 6 };
|
|
4
|
+
/**
|
|
5
|
+
* The grid's later options: a known negative appearance per row, a known
|
|
6
|
+
* transpose breakpoint on a strip only, and a whole count of digits (3 to
|
|
7
|
+
* 6) for a grid with entry rows.
|
|
8
|
+
*/
|
|
9
|
+
export function assertDenominationOptions(layout, rows, transposeBelow, countDigits) {
|
|
10
|
+
if (rows.some((row) => row.negativeAppearance !== undefined &&
|
|
11
|
+
!denominationNegativeAppearances.includes(row.negativeAppearance)))
|
|
12
|
+
throw new RangeError("negativeAppearance must be 'danger' or 'plain'");
|
|
13
|
+
if (transposeBelow !== undefined) {
|
|
14
|
+
if (!denominationTransposeBreakpoints.includes(transposeBelow))
|
|
15
|
+
throw new RangeError("transposeBelow must be 'md' or 'lg'");
|
|
16
|
+
if (layout !== 'strip')
|
|
17
|
+
throw new RangeError('transposeBelow needs the strip layout');
|
|
18
|
+
}
|
|
19
|
+
if (countDigits !== undefined) {
|
|
20
|
+
if (!Number.isInteger(countDigits) ||
|
|
21
|
+
countDigits < countDigitsRange.min ||
|
|
22
|
+
countDigits > countDigitsRange.max)
|
|
23
|
+
throw new RangeError('countDigits must be a whole number from 3 to 6');
|
|
24
|
+
if (!rows.some((row) => row.entry))
|
|
25
|
+
throw new RangeError('countDigits needs entry rows');
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -2,13 +2,14 @@ import { type ReactNode } from 'react';
|
|
|
2
2
|
import { type ActionBarSummary } from '../action-bar/ActionBarSummary.js';
|
|
3
3
|
type DialogFooterProps = {
|
|
4
4
|
summary?: ActionBarSummary;
|
|
5
|
+
status?: ReactNode;
|
|
5
6
|
children?: ReactNode;
|
|
6
7
|
};
|
|
7
8
|
/**
|
|
8
|
-
* The dialog's pinned footer: the summary figure
|
|
9
|
-
* as in ActionBar) at the start, the actions at the
|
|
10
|
-
* the dialog has a footer
|
|
11
|
-
* region, so a new figure is announced.
|
|
9
|
+
* The dialog's pinned footer: the summary figure and the status line (in
|
|
10
|
+
* one polite live region, as in ActionBar) at the start, the actions at the
|
|
11
|
+
* end. Rendered only when the dialog has a footer, a summary or a status,
|
|
12
|
+
* and then always with its live region, so a new figure is announced.
|
|
12
13
|
*/
|
|
13
|
-
export declare function DialogFooter({ children, summary }: DialogFooterProps): import("react").JSX.Element;
|
|
14
|
+
export declare function DialogFooter({ children, status, summary }: DialogFooterProps): import("react").JSX.Element;
|
|
14
15
|
export {};
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { ActionBarSummaryLine, } from '../action-bar/ActionBarSummary.js';
|
|
3
3
|
/**
|
|
4
|
-
* The dialog's pinned footer: the summary figure
|
|
5
|
-
* as in ActionBar) at the start, the actions at the
|
|
6
|
-
* the dialog has a footer
|
|
7
|
-
* region, so a new figure is announced.
|
|
4
|
+
* The dialog's pinned footer: the summary figure and the status line (in
|
|
5
|
+
* one polite live region, as in ActionBar) at the start, the actions at the
|
|
6
|
+
* end. Rendered only when the dialog has a footer, a summary or a status,
|
|
7
|
+
* and then always with its live region, so a new figure is announced.
|
|
8
8
|
*/
|
|
9
|
-
export function DialogFooter({ children, summary }) {
|
|
10
|
-
return (_jsxs("div", { className: "sw-dialog-footer", children: [
|
|
9
|
+
export function DialogFooter({ children, status, summary }) {
|
|
10
|
+
return (_jsxs("div", { className: "sw-dialog-footer", children: [_jsxs("div", { className: "sw-dialog-footer-status", role: "status", children: [summary ? _jsx(ActionBarSummaryLine, { summary: summary }) : null, status !== undefined ? (_jsx("span", { className: "sw-dialog-footer-note", children: status })) : null] }), children ? (_jsx("div", { className: "sw-dialog-footer-actions", children: children })) : null] }));
|
|
11
11
|
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { type Breakpoint } from '../../css/breakpoints.js';
|
|
2
|
+
import { type DialogAppearance } from '../../css/css-dialog-banded.js';
|
|
3
|
+
import { type ThemeCanvas } from '../../css/css-canvas-grouped.js';
|
|
4
|
+
import { type ActionBarSummary } from '../action-bar/ActionBarSummary.js';
|
|
5
|
+
export declare const dialogSizes: readonly ["md", "lg", "xl"];
|
|
6
|
+
/**
|
|
7
|
+
* `md` is the reading width (32rem) for a message or a short form; `lg`
|
|
8
|
+
* (56rem) holds a row of six fields or a data grid without folding it;
|
|
9
|
+
* `xl` (65rem) holds a grid of six count columns with its labels and total.
|
|
10
|
+
*/
|
|
11
|
+
export type DialogSize = (typeof dialogSizes)[number];
|
|
12
|
+
/** `alertdialog` for a dialog that interrupts to warn, such as an idle sign-out. */
|
|
13
|
+
export type DialogRole = 'dialog' | 'alertdialog';
|
|
14
|
+
export type DialogChecks = {
|
|
15
|
+
size: DialogSize;
|
|
16
|
+
role: DialogRole | undefined;
|
|
17
|
+
description: string | undefined;
|
|
18
|
+
summary: ActionBarSummary | undefined;
|
|
19
|
+
appearance: DialogAppearance;
|
|
20
|
+
canvas: ThemeCanvas;
|
|
21
|
+
sheetBelow: Breakpoint | undefined;
|
|
22
|
+
sheetGrabber: boolean;
|
|
23
|
+
};
|
|
24
|
+
export declare function assertDialogProps({ appearance, canvas, description, role, sheetBelow, sheetGrabber, size, summary, }: DialogChecks): void;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { dialogAppearances, } from '../../css/css-dialog-banded.js';
|
|
2
|
+
import { themeCanvases, } from '../../css/css-canvas-grouped.js';
|
|
3
|
+
import { assertActionBarSummary, } from '../action-bar/ActionBarSummary.js';
|
|
4
|
+
export const dialogSizes = ['md', 'lg', 'xl'];
|
|
5
|
+
const dialogRoles = ['dialog', 'alertdialog'];
|
|
6
|
+
function oneOf(values, value, prop) {
|
|
7
|
+
if (!values.includes(value))
|
|
8
|
+
throw new RangeError(`Unknown Dialog ${prop}: ${String(value)}`);
|
|
9
|
+
}
|
|
10
|
+
export function assertDialogProps({ appearance, canvas, description, role, sheetBelow, sheetGrabber, size, summary, }) {
|
|
11
|
+
oneOf(dialogSizes, size, 'size');
|
|
12
|
+
oneOf(dialogAppearances, appearance, 'appearance');
|
|
13
|
+
oneOf(themeCanvases, canvas, 'canvas');
|
|
14
|
+
if (role !== undefined)
|
|
15
|
+
oneOf(dialogRoles, role, 'role');
|
|
16
|
+
if (description !== undefined &&
|
|
17
|
+
(typeof description !== 'string' || !description.trim()))
|
|
18
|
+
throw new RangeError('Dialog description must not be empty');
|
|
19
|
+
if (sheetGrabber && !sheetBelow)
|
|
20
|
+
throw new RangeError('Dialog sheetGrabber needs sheetBelow');
|
|
21
|
+
if (summary)
|
|
22
|
+
assertActionBarSummary(summary);
|
|
23
|
+
}
|