@scalewing/react 1.40.0 → 1.42.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 +38 -0
- package/README.md +274 -7
- package/dist/calendar-labels.d.ts +2 -0
- package/dist/calendar-labels.js +7 -0
- package/dist/components/Article.d.ts +60 -0
- package/dist/components/Article.js +42 -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/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/DocumentViewer.d.ts +41 -0
- package/dist/components/DocumentViewer.js +72 -0
- package/dist/components/Field.d.ts +13 -2
- package/dist/components/Field.js +14 -38
- package/dist/components/FileField.d.ts +44 -0
- package/dist/components/FileField.js +66 -0
- package/dist/components/FilterChips.d.ts +7 -37
- package/dist/components/FilterChips.js +32 -24
- package/dist/components/ListRow.d.ts +33 -3
- package/dist/components/ListRow.js +18 -17
- package/dist/components/MarkdownView.d.ts +39 -0
- package/dist/components/MarkdownView.js +30 -0
- package/dist/components/NavCount.d.ts +15 -2
- package/dist/components/NavCount.js +16 -6
- package/dist/components/SegmentedControl.d.ts +17 -1
- package/dist/components/SegmentedControl.js +11 -3
- 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/chart/use-chart-width.js +2 -24
- package/dist/components/date-field/CalendarEntryButton.d.ts +24 -0
- package/dist/components/date-field/CalendarEntryButton.js +16 -0
- package/dist/components/date-field/TypedEntry.d.ts +26 -0
- package/dist/components/date-field/TypedEntry.js +13 -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/document-viewer/document-viewer-labels.d.ts +23 -0
- package/dist/components/document-viewer/document-viewer-labels.js +23 -0
- package/dist/components/document-viewer/page-box.d.ts +8 -0
- package/dist/components/document-viewer/page-box.js +12 -0
- package/dist/components/document-viewer/pdf-document.d.ts +32 -0
- package/dist/components/document-viewer/pdf-document.js +66 -0
- package/dist/components/document-viewer/pdf-options.d.ts +36 -0
- package/dist/components/document-viewer/pdf-options.js +46 -0
- package/dist/components/document-viewer/render-pdf-page.d.ts +20 -0
- package/dist/components/document-viewer/render-pdf-page.js +38 -0
- package/dist/components/document-viewer/scroll-chain.d.ts +6 -0
- package/dist/components/document-viewer/scroll-chain.js +23 -0
- package/dist/components/document-viewer/use-error-report.d.ts +5 -0
- package/dist/components/document-viewer/use-error-report.js +13 -0
- package/dist/components/document-viewer/use-page-render.d.ts +21 -0
- package/dist/components/document-viewer/use-page-render.js +70 -0
- package/dist/components/document-viewer/use-pdf-document.d.ts +22 -0
- package/dist/components/document-viewer/use-pdf-document.js +47 -0
- package/dist/components/document-viewer/use-pinch-zoom.d.ts +12 -0
- package/dist/components/document-viewer/use-pinch-zoom.js +67 -0
- package/dist/components/document-viewer/use-zoom.d.ts +23 -0
- package/dist/components/document-viewer/use-zoom.js +70 -0
- package/dist/components/document-viewer/zoom.d.ts +29 -0
- package/dist/components/document-viewer/zoom.js +37 -0
- package/dist/components/field/field-checks.d.ts +34 -0
- package/dist/components/field/field-checks.js +42 -0
- package/dist/components/file-field/file-field-checks.d.ts +13 -0
- package/dist/components/file-field/file-field-checks.js +21 -0
- package/dist/components/file-field/use-file-drop.d.ts +34 -0
- package/dist/components/file-field/use-file-drop.js +78 -0
- package/dist/components/file-field/use-swap-focus.d.ts +15 -0
- package/dist/components/file-field/use-swap-focus.js +40 -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/list/ListRowContent.d.ts +8 -2
- package/dist/components/list/ListRowContent.js +6 -3
- package/dist/components/list/ListRowItem.d.ts +10 -0
- package/dist/components/list/ListRowItem.js +17 -0
- package/dist/components/list/row-naming.d.ts +17 -0
- package/dist/components/list/row-naming.js +19 -0
- package/dist/components/list/row-slot.d.ts +8 -0
- package/dist/components/list/row-slot.js +12 -0
- package/dist/components/markdown/markdown-limits.d.ts +14 -0
- package/dist/components/markdown/markdown-limits.js +14 -0
- package/dist/components/markdown/markdown-model.d.ts +94 -0
- package/dist/components/markdown/markdown-model.js +8 -0
- package/dist/components/markdown/markdown-options.d.ts +18 -0
- package/dist/components/markdown/markdown-options.js +10 -0
- package/dist/components/markdown/markdown-rules.d.ts +28 -0
- package/dist/components/markdown/markdown-rules.js +81 -0
- package/dist/components/markdown/markdown-text.d.ts +17 -0
- package/dist/components/markdown/markdown-text.js +86 -0
- package/dist/components/markdown/parse-markdown.d.ts +4 -0
- package/dist/components/markdown/parse-markdown.js +21 -0
- package/dist/components/markdown/read-blocks.d.ts +8 -0
- package/dist/components/markdown/read-blocks.js +168 -0
- package/dist/components/markdown/read-inlines.d.ts +12 -0
- package/dist/components/markdown/read-inlines.js +126 -0
- package/dist/components/markdown/read-markdown.d.ts +9 -0
- package/dist/components/markdown/read-markdown.js +27 -0
- package/dist/components/markdown/reading-context.d.ts +18 -0
- package/dist/components/markdown/reading-context.js +35 -0
- package/dist/components/markdown/render-markdown.d.ts +10 -0
- package/dist/components/markdown/render-markdown.js +52 -0
- package/dist/components/use-element-width.d.ts +11 -0
- package/dist/components/use-element-width.js +38 -0
- package/dist/css/chart-tones.d.ts +2 -0
- package/dist/css/chart-tones.js +5 -1
- package/dist/css/css-article.d.ts +2 -0
- package/dist/css/css-article.js +110 -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 +18 -1
- package/dist/css/css-canvas-grouped.d.ts +17 -0
- package/dist/css/css-canvas-grouped.js +40 -0
- package/dist/css/css-components.js +18 -0
- package/dist/css/css-count-pill.js +14 -1
- 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 +4 -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-viewer.d.ts +2 -0
- package/dist/css/css-document-viewer.js +186 -0
- 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-file-field.d.ts +2 -0
- package/dist/css/css-file-field.js +180 -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-trailing.d.ts +2 -0
- package/dist/css/css-list-row-trailing.js +91 -0
- package/dist/css/css-list-row.d.ts +6 -0
- package/dist/css/css-list-row.js +19 -3
- package/dist/css/css-list.js +5 -1
- package/dist/css/css-markdown.d.ts +2 -0
- package/dist/css/css-markdown.js +144 -0
- 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-switch.d.ts +6 -0
- package/dist/css/css-switch.js +39 -0
- package/dist/css/css-variables.js +1 -0
- package/dist/css/stacking.d.ts +1 -0
- package/dist/css/stacking.js +4 -1
- package/dist/css/stylesheet.js +12 -0
- package/dist/date-field-labels.d.ts +5 -0
- package/dist/date-field-labels.js +1 -0
- package/dist/document-viewer-worker.d.ts +9 -0
- package/dist/document-viewer-worker.js +9 -0
- package/dist/index.d.ts +15 -9
- package/dist/index.js +13 -7
- package/dist/list-options.d.ts +11 -7
- package/dist/list-options.js +20 -8
- package/dist/styles.css +889 -44
- package/dist/theme/ThemeProvider.d.ts +11 -1
- package/dist/theme/ThemeProvider.js +4 -2
- package/package.json +10 -2
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
/** The pill's colours: `danger` for work waiting, `accent` for news. */
|
|
2
|
+
export declare const navCountTones: readonly ["danger", "accent"];
|
|
3
|
+
export type NavCountTone = (typeof navCountTones)[number];
|
|
1
4
|
export type NavCountProps = {
|
|
2
5
|
/**
|
|
3
6
|
* How many things wait behind the destination, such as tasks to do: a
|
|
@@ -11,11 +14,21 @@ export type NavCountProps = {
|
|
|
11
14
|
* pill then hidden from assistive technology.
|
|
12
15
|
*/
|
|
13
16
|
countLabel?: string;
|
|
17
|
+
/**
|
|
18
|
+
* The pill's colour. `danger` (the default) is work waiting there, such
|
|
19
|
+
* as tasks to do; `accent` is news, such as what was added since the
|
|
20
|
+
* person last looked, where nothing is overdue. Give an accent count a
|
|
21
|
+
* `countLabel` ("3 new"), so its words, not its colour, say it is news.
|
|
22
|
+
* Place either tone on a neutral or ghost destination: a filled one of
|
|
23
|
+
* the same colour would hide it.
|
|
24
|
+
*/
|
|
25
|
+
tone?: NavCountTone;
|
|
14
26
|
};
|
|
15
27
|
/**
|
|
16
28
|
* A count after a Nav destination's words, placed inside its link or
|
|
17
29
|
* Button: `<a href="/tasks">Tasks<NavCount count={8} /></a>`. It is the
|
|
18
30
|
* pill SectionNav's item count draws, and it is part of the destination's
|
|
19
|
-
* accessible name, as there.
|
|
31
|
+
* accessible name, as there. `tone="accent"` draws it in the accent colour,
|
|
32
|
+
* for news rather than work.
|
|
20
33
|
*/
|
|
21
|
-
export declare function NavCount({ count, countLabel }: NavCountProps): import("react").JSX.Element | null;
|
|
34
|
+
export declare function NavCount({ count, countLabel, tone, }: NavCountProps): import("react").JSX.Element | null;
|
|
@@ -1,23 +1,33 @@
|
|
|
1
1
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
import { cx } from '../class-names.js';
|
|
3
|
+
import { isOneOf } from '../is-one-of.js';
|
|
4
|
+
/** The pill's colours: `danger` for work waiting, `accent` for news. */
|
|
5
|
+
export const navCountTones = ['danger', 'accent'];
|
|
6
|
+
/**
|
|
7
|
+
* Fails closed on a count that is not a whole number, an empty label or an
|
|
8
|
+
* unknown tone.
|
|
9
|
+
*/
|
|
10
|
+
function assertNavCount({ count, countLabel, tone }) {
|
|
4
11
|
if (!Number.isInteger(count) || count < 0)
|
|
5
12
|
throw new RangeError(`NavCount count must be a whole number from 0: ${String(count)}`);
|
|
6
13
|
if (countLabel !== undefined &&
|
|
7
14
|
(typeof countLabel !== 'string' || !countLabel.trim()))
|
|
8
15
|
throw new RangeError('NavCount countLabel must be non-empty text');
|
|
16
|
+
if (tone !== undefined && !isOneOf(navCountTones, tone))
|
|
17
|
+
throw new RangeError(`Unknown NavCount tone: ${String(tone)}`);
|
|
9
18
|
}
|
|
10
19
|
/**
|
|
11
20
|
* A count after a Nav destination's words, placed inside its link or
|
|
12
21
|
* Button: `<a href="/tasks">Tasks<NavCount count={8} /></a>`. It is the
|
|
13
22
|
* pill SectionNav's item count draws, and it is part of the destination's
|
|
14
|
-
* accessible name, as there.
|
|
23
|
+
* accessible name, as there. `tone="accent"` draws it in the accent colour,
|
|
24
|
+
* for news rather than work.
|
|
15
25
|
*/
|
|
16
|
-
export function NavCount({ count, countLabel }) {
|
|
17
|
-
assertNavCount({ count, countLabel });
|
|
26
|
+
export function NavCount({ count, countLabel, tone = 'danger', }) {
|
|
27
|
+
assertNavCount({ count, countLabel, tone });
|
|
18
28
|
if (count === 0)
|
|
19
29
|
return null;
|
|
20
30
|
// The space is its own text node, so every browser and the name's
|
|
21
31
|
// computation keep it between the words and the count.
|
|
22
|
-
return (_jsxs(_Fragment, { children: [' ', countLabel ? _jsx("span", { className: "sw-sr-only", children: countLabel }) : null, _jsx("span", { "aria-hidden": countLabel ? true : undefined, className:
|
|
32
|
+
return (_jsxs(_Fragment, { children: [' ', countLabel ? _jsx("span", { className: "sw-sr-only", children: countLabel }) : null, _jsx("span", { "aria-hidden": countLabel ? true : undefined, className: cx('sw-nav-count', tone === 'accent' && 'sw-nav-count-accent'), children: count })] }));
|
|
23
33
|
}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { type ReactNode } from 'react';
|
|
2
2
|
import { type AccessibleName } from '../accessible-name.js';
|
|
3
|
+
import { type Breakpoint } from '../css/breakpoints.js';
|
|
4
|
+
import { type SegmentedSize, segmentedSizes } from '../css/css-segmented-sizing.js';
|
|
5
|
+
export { segmentedSizes, type SegmentedSize };
|
|
3
6
|
export type SegmentedItem = {
|
|
4
7
|
id: string;
|
|
5
8
|
label: ReactNode;
|
|
@@ -46,6 +49,19 @@ export type SegmentedControlProps = AccessibleName & SegmentedChange & {
|
|
|
46
49
|
error?: string;
|
|
47
50
|
/** Sets `aria-required` on the group; the visible mark belongs to the element that labels it. */
|
|
48
51
|
required?: boolean;
|
|
52
|
+
/**
|
|
53
|
+
* Below this breakpoint the control fills its column and its segments
|
|
54
|
+
* share the width equally, whatever the variant, such as a period
|
|
55
|
+
* switch across a phone. From it up it keeps its own width.
|
|
56
|
+
*/
|
|
57
|
+
fullWidthBelow?: Breakpoint;
|
|
58
|
+
/**
|
|
59
|
+
* `sm` pads every segment a step less and keeps each label on one line
|
|
60
|
+
* (ending in an ellipsis past its share; its name keeps the whole
|
|
61
|
+
* label), so three or four segments with counts fit a phone's card.
|
|
62
|
+
* Keep the labels short enough to fit: the hidden part cannot be read.
|
|
63
|
+
* `md` (the default) is the variant's own padding.
|
|
64
|
+
*/
|
|
65
|
+
size?: SegmentedSize;
|
|
49
66
|
};
|
|
50
67
|
export declare const SegmentedControl: import("react").ForwardRefExoticComponent<SegmentedControlProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
51
|
-
export {};
|
|
@@ -2,11 +2,19 @@
|
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { forwardRef, useId } from 'react';
|
|
4
4
|
import { cx } from '../class-names.js';
|
|
5
|
+
import { breakpoints } from '../css/breakpoints.js';
|
|
5
6
|
import { segmentedRaisedClass } from '../css/css-segmented-raised.js';
|
|
7
|
+
import { segmentedFullWidthClass, segmentedSizes, segmentedSmClass, } from '../css/css-segmented-sizing.js';
|
|
6
8
|
import { FieldErrorRegion } from './FieldErrorRegion.js';
|
|
7
9
|
import { tabStop } from '../tab-stop.js';
|
|
8
|
-
export
|
|
10
|
+
export { segmentedSizes };
|
|
11
|
+
export const SegmentedControl = forwardRef(function SegmentedControl({ disabled = false, error, fullWidthBelow, items, onChange, required = false, size = 'md', value, variant = 'compact', ...labelProps }, ref) {
|
|
9
12
|
const errorId = useId();
|
|
13
|
+
if (fullWidthBelow !== undefined &&
|
|
14
|
+
!breakpoints.includes(fullWidthBelow))
|
|
15
|
+
throw new RangeError(`Unknown SegmentedControl fullWidthBelow: ${String(fullWidthBelow)}`);
|
|
16
|
+
if (!segmentedSizes.includes(size))
|
|
17
|
+
throw new RangeError(`Unknown SegmentedControl size: ${String(size)}`);
|
|
10
18
|
const withIcons = items.some((item) => item.icon);
|
|
11
19
|
function onKeyDown(event) {
|
|
12
20
|
if (disabled || (event.key !== 'ArrowRight' && event.key !== 'ArrowLeft')) {
|
|
@@ -25,7 +33,7 @@ export const SegmentedControl = forwardRef(function SegmentedControl({ disabled
|
|
|
25
33
|
}
|
|
26
34
|
// The wrapper is always rendered, so an error that appears later swaps
|
|
27
35
|
// text into a live region that already exists and the group keeps focus.
|
|
28
|
-
return (_jsxs("div", { className: cx('sw-segmented-field', variant === 'filled' && 'sw-segmented-field-filled'), children: [_jsx("div", { ref: ref, "aria-describedby": error ? errorId : undefined, "aria-disabled": disabled || undefined, "aria-invalid": error ? true : undefined, "aria-required": required || undefined, className: cx('sw-segmented', variant === 'filled' && 'sw-segmented-filled', variant === 'raised' && segmentedRaisedClass, withIcons && 'sw-segmented-with-icons', disabled && 'sw-segmented-disabled'), onKeyDown: onKeyDown, role: "radiogroup", ...labelProps, children: items.map((item) => {
|
|
36
|
+
return (_jsxs("div", { className: cx('sw-segmented-field', variant === 'filled' && 'sw-segmented-field-filled', fullWidthBelow && segmentedFullWidthClass(fullWidthBelow)), children: [_jsx("div", { ref: ref, "aria-describedby": error ? errorId : undefined, "aria-disabled": disabled || undefined, "aria-invalid": error ? true : undefined, "aria-required": required || undefined, className: cx('sw-segmented', variant === 'filled' && 'sw-segmented-filled', variant === 'raised' && segmentedRaisedClass, withIcons && 'sw-segmented-with-icons', disabled && 'sw-segmented-disabled', size === 'sm' && segmentedSmClass), onKeyDown: onKeyDown, role: "radiogroup", ...labelProps, children: items.map((item) => {
|
|
29
37
|
const selected = item.id === value;
|
|
30
38
|
return (_jsxs("button", { "aria-checked": selected, className: cx('sw-segmented-item', selected && 'sw-segmented-item-selected'), disabled: disabled, onClick: () => {
|
|
31
39
|
if (!selected) {
|
|
@@ -34,6 +42,6 @@ export const SegmentedControl = forwardRef(function SegmentedControl({ disabled
|
|
|
34
42
|
}, role: "radio",
|
|
35
43
|
// Every item stays a tab stop, as each already was outside
|
|
36
44
|
// Safari: the arrow keys change the value, not the focus.
|
|
37
|
-
tabIndex: tabStop, type: "button", children: [item.icon ? (_jsx("span", { "aria-hidden": "true", className: "sw-segmented-item-icon", children: item.icon })) : null, item.label] }, item.id));
|
|
45
|
+
tabIndex: tabStop, type: "button", children: [item.icon ? (_jsx("span", { "aria-hidden": "true", className: "sw-segmented-item-icon", children: item.icon })) : null, size === 'sm' ? (_jsx("span", { className: "sw-segmented-item-label", children: item.label })) : (item.label)] }, item.id));
|
|
38
46
|
}) }), _jsx(FieldErrorRegion, { className: "sw-field-error", id: errorId, message: error })] }));
|
|
39
47
|
});
|
|
@@ -1,8 +1,23 @@
|
|
|
1
|
+
import { type SwitchCheckedTone, switchCheckedTones, type SwitchLayout, switchLayouts } from '../css/css-switch.js';
|
|
2
|
+
export { switchCheckedTones, switchLayouts, type SwitchCheckedTone, type SwitchLayout, };
|
|
1
3
|
export type SwitchProps = {
|
|
2
4
|
label: string;
|
|
3
5
|
checked: boolean;
|
|
4
6
|
onCheckedChange: (checked: boolean) => void;
|
|
5
7
|
disabled?: boolean;
|
|
6
8
|
description?: string;
|
|
9
|
+
/**
|
|
10
|
+
* `leading` (the default) puts the control before its label;
|
|
11
|
+
* `trailing` puts the label and description first and the control at
|
|
12
|
+
* the row's end, the row as wide as its column, as a settings list
|
|
13
|
+
* draws it.
|
|
14
|
+
*/
|
|
15
|
+
layout?: SwitchLayout;
|
|
16
|
+
/**
|
|
17
|
+
* The track's colour while on: `accent` (the default) or `success`, for
|
|
18
|
+
* a switch whose on state is the healthy one, such as a feature turned
|
|
19
|
+
* on. The thumb's place says the state either way.
|
|
20
|
+
*/
|
|
21
|
+
checkedTone?: SwitchCheckedTone;
|
|
7
22
|
};
|
|
8
|
-
export declare function Switch({ label, checked, onCheckedChange, disabled, description, }: SwitchProps): import("react").JSX.Element;
|
|
23
|
+
export declare function Switch({ label, checked, onCheckedChange, disabled, description, layout, checkedTone, }: SwitchProps): import("react").JSX.Element;
|
|
@@ -1,12 +1,23 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
3
|
import { useId } from 'react';
|
|
4
|
+
import { cx } from '../class-names.js';
|
|
5
|
+
import { switchCheckedTones, switchLayouts, } from '../css/css-switch.js';
|
|
4
6
|
import { tabStop } from '../tab-stop.js';
|
|
5
|
-
export
|
|
7
|
+
export { switchCheckedTones, switchLayouts, };
|
|
8
|
+
function assertOneOf(values, value, prop) {
|
|
9
|
+
if (!values.includes(value))
|
|
10
|
+
throw new RangeError(`Unknown Switch ${prop}: ${String(value)}`);
|
|
11
|
+
}
|
|
12
|
+
export function Switch({ label, checked, onCheckedChange, disabled = false, description, layout = 'leading', checkedTone = 'accent', }) {
|
|
13
|
+
assertOneOf(switchLayouts, layout, 'layout');
|
|
14
|
+
assertOneOf(switchCheckedTones, checkedTone, 'checkedTone');
|
|
6
15
|
const labelId = useId();
|
|
7
16
|
const descriptionId = useId();
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
17
|
+
const control = (_jsxs("span", { className: "sw-switch-control", children: [_jsx("input", { "aria-checked": checked, "aria-describedby": description ? descriptionId : undefined, "aria-labelledby": labelId, checked: checked, className: "sw-switch-input", disabled: disabled, onChange: (event) => {
|
|
18
|
+
if (!disabled)
|
|
19
|
+
onCheckedChange(event.currentTarget.checked);
|
|
20
|
+
}, role: "switch", tabIndex: tabStop, type: "checkbox" }), _jsx("span", { "aria-hidden": "true", className: "sw-switch-track", children: _jsx("span", { className: "sw-switch-thumb" }) })] }));
|
|
21
|
+
const copy = (_jsxs("span", { className: "sw-switch-copy", children: [_jsx("span", { className: "sw-switch-label", id: labelId, children: label }), description ? (_jsx("span", { className: "sw-switch-description", id: descriptionId, children: description })) : null] }));
|
|
22
|
+
return (_jsx("label", { className: cx('sw-switch', layout === 'trailing' && 'sw-switch-trailing', checkedTone === 'success' && 'sw-switch-success'), children: layout === 'trailing' ? (_jsxs(_Fragment, { children: [copy, control] })) : (_jsxs(_Fragment, { children: [control, copy] })) }));
|
|
12
23
|
}
|
|
@@ -5,18 +5,26 @@ export type ActionBarSummaryTone = (typeof actionBarSummaryTones)[number];
|
|
|
5
5
|
export type ActionBarSummary = {
|
|
6
6
|
/** What the figure is, such as "Counted". */
|
|
7
7
|
label: string;
|
|
8
|
-
/**
|
|
8
|
+
/**
|
|
9
|
+
* The figure, formatted by the consumer, such as "$33,790.00". An empty
|
|
10
|
+
* string (or only spaces) means there is no figure yet, such as before
|
|
11
|
+
* anything is counted: the summary line is left out and the live region
|
|
12
|
+
* stays, so the first figure is announced when it arrives.
|
|
13
|
+
*/
|
|
9
14
|
value: string;
|
|
10
15
|
/** A short line after the figure, such as "short $4,000.00". */
|
|
11
16
|
detail?: string;
|
|
12
17
|
/** Colors the value only; the words say why. Defaults to `default`. */
|
|
13
18
|
tone?: ActionBarSummaryTone;
|
|
14
19
|
};
|
|
20
|
+
/** Whether the summary has a figure to show; an empty value has none yet. */
|
|
21
|
+
export declare function hasActionBarSummaryFigure(summary: ActionBarSummary): boolean;
|
|
15
22
|
export declare function assertActionBarSummary(summary: ActionBarSummary): void;
|
|
16
23
|
/**
|
|
17
24
|
* The bar's figure: its label, the value in the heading variant at tabular
|
|
18
25
|
* figures, and its detail, on one baseline, wrapping as one phrase.
|
|
26
|
+
* Nothing while the value is empty.
|
|
19
27
|
*/
|
|
20
28
|
export declare function ActionBarSummaryLine({ summary, }: {
|
|
21
29
|
summary: ActionBarSummary;
|
|
22
|
-
}): import("react").JSX.Element;
|
|
30
|
+
}): import("react").JSX.Element | null;
|
|
@@ -4,9 +4,15 @@ export const actionBarSummaryTones = ['default', 'danger', 'success'];
|
|
|
4
4
|
function isBlank(text) {
|
|
5
5
|
return typeof text !== 'string' || !text.trim();
|
|
6
6
|
}
|
|
7
|
+
/** Whether the summary has a figure to show; an empty value has none yet. */
|
|
8
|
+
export function hasActionBarSummaryFigure(summary) {
|
|
9
|
+
return !isBlank(summary.value);
|
|
10
|
+
}
|
|
7
11
|
export function assertActionBarSummary(summary) {
|
|
8
|
-
if (isBlank(summary.label)
|
|
9
|
-
throw new RangeError('ActionBar summary needs a label
|
|
12
|
+
if (isBlank(summary.label))
|
|
13
|
+
throw new RangeError('ActionBar summary needs a label');
|
|
14
|
+
if (typeof summary.value !== 'string')
|
|
15
|
+
throw new RangeError('ActionBar summary value must be a string');
|
|
10
16
|
if (summary.detail !== undefined && isBlank(summary.detail))
|
|
11
17
|
throw new RangeError('ActionBar summary detail must not be empty');
|
|
12
18
|
if (summary.tone !== undefined &&
|
|
@@ -16,8 +22,11 @@ export function assertActionBarSummary(summary) {
|
|
|
16
22
|
/**
|
|
17
23
|
* The bar's figure: its label, the value in the heading variant at tabular
|
|
18
24
|
* figures, and its detail, on one baseline, wrapping as one phrase.
|
|
25
|
+
* Nothing while the value is empty.
|
|
19
26
|
*/
|
|
20
27
|
export function ActionBarSummaryLine({ summary, }) {
|
|
28
|
+
if (!hasActionBarSummaryFigure(summary))
|
|
29
|
+
return null;
|
|
21
30
|
const tone = summary.tone ?? 'default';
|
|
22
31
|
return (_jsxs("span", { className: cx('sw-action-bar-summary', tone !== 'default' && `sw-action-bar-summary-${tone}`), children: [_jsx("span", { className: "sw-action-bar-summary-label", children: summary.label }), ' ', _jsx("span", { className: "sw-action-bar-summary-value sw-text-heading sw-tabular", children: summary.value }), summary.detail ? (_jsxs(_Fragment, { children: [' ', _jsx("span", { className: "sw-action-bar-summary-detail", children: summary.detail })] })) : null] }));
|
|
23
32
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import { useLayoutEffect, useState } from 'react';
|
|
3
2
|
import { chartFallbackWidth } from '../../chart-metrics.js';
|
|
3
|
+
import { useElementWidth } from '../use-element-width.js';
|
|
4
4
|
/**
|
|
5
5
|
* Follows the width a chart is given, so its drawing is laid out in its
|
|
6
6
|
* own pixels: on mount, before the first paint, and whenever its box
|
|
@@ -8,27 +8,5 @@ import { chartFallbackWidth } from '../../chart-metrics.js';
|
|
|
8
8
|
* no ResizeObserver measures on window resizes instead.
|
|
9
9
|
*/
|
|
10
10
|
export function useChartWidth(ref) {
|
|
11
|
-
|
|
12
|
-
// Before paint, so a phone never shows the wide layout first.
|
|
13
|
-
useLayoutEffect(() => {
|
|
14
|
-
const box = ref.current;
|
|
15
|
-
if (!box)
|
|
16
|
-
return;
|
|
17
|
-
function measure() {
|
|
18
|
-
if (!box)
|
|
19
|
-
return;
|
|
20
|
-
const next = Math.round(box.getBoundingClientRect().width);
|
|
21
|
-
if (next > 0)
|
|
22
|
-
setWidth(next);
|
|
23
|
-
}
|
|
24
|
-
measure();
|
|
25
|
-
if (typeof ResizeObserver === 'undefined') {
|
|
26
|
-
window.addEventListener('resize', measure);
|
|
27
|
-
return () => window.removeEventListener('resize', measure);
|
|
28
|
-
}
|
|
29
|
-
const observer = new ResizeObserver(measure);
|
|
30
|
-
observer.observe(box);
|
|
31
|
-
return () => observer.disconnect();
|
|
32
|
-
}, [ref]);
|
|
33
|
-
return width;
|
|
11
|
+
return useElementWidth(ref, chartFallbackWidth);
|
|
34
12
|
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { type Ref } from 'react';
|
|
2
|
+
import { type CalendarPopup } from './use-calendar-popup.js';
|
|
3
|
+
export type CalendarEntryButtonProps = {
|
|
4
|
+
id: string;
|
|
5
|
+
/** The field's label element, which names the button with its words. */
|
|
6
|
+
labelId: string;
|
|
7
|
+
describedBy: string | undefined;
|
|
8
|
+
value: string;
|
|
9
|
+
locale: string;
|
|
10
|
+
/** What an empty field shows, such as "Choose a day". */
|
|
11
|
+
emptyText: string;
|
|
12
|
+
disabled: boolean;
|
|
13
|
+
invalid: boolean;
|
|
14
|
+
buttonRef: Ref<HTMLButtonElement>;
|
|
15
|
+
trigger: CalendarPopup['trigger'];
|
|
16
|
+
onPress: () => void;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* DateField's calendar entry: a neutral Button showing the calendar glyph
|
|
20
|
+
* and the chosen day, or the words that ask for one, that opens the
|
|
21
|
+
* calendar. It is named by the field's label and its own words ("Hire date
|
|
22
|
+
* Choose a day", "Hire date Oct 6, 2026") and described as the field is.
|
|
23
|
+
*/
|
|
24
|
+
export declare function CalendarEntryButton({ id, labelId, describedBy, value, locale, emptyText, disabled, invalid, buttonRef, trigger, onPress, }: CalendarEntryButtonProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useId } from 'react';
|
|
3
|
+
import { formatDateMedium } from '../../calendar-labels.js';
|
|
4
|
+
import { cx } from '../../class-names.js';
|
|
5
|
+
import { Button } from '../Button.js';
|
|
6
|
+
import { DateFieldGlyph } from './DateFieldGlyph.js';
|
|
7
|
+
/**
|
|
8
|
+
* DateField's calendar entry: a neutral Button showing the calendar glyph
|
|
9
|
+
* and the chosen day, or the words that ask for one, that opens the
|
|
10
|
+
* calendar. It is named by the field's label and its own words ("Hire date
|
|
11
|
+
* Choose a day", "Hire date Oct 6, 2026") and described as the field is.
|
|
12
|
+
*/
|
|
13
|
+
export function CalendarEntryButton({ id, labelId, describedBy, value, locale, emptyText, disabled, invalid, buttonRef, trigger, onPress, }) {
|
|
14
|
+
const textId = useId();
|
|
15
|
+
return (_jsxs(Button, { ...trigger, "aria-describedby": describedBy, "aria-labelledby": `${labelId} ${textId}`, className: cx('sw-date-field-calendar-entry', invalid && 'sw-date-field-calendar-entry-invalid'), disabled: disabled, id: id, onPress: onPress, ref: buttonRef, variant: "neutral", children: [_jsx(DateFieldGlyph, { name: "calendar" }), _jsx("span", { className: "sw-date-field-calendar-entry-text", id: textId, children: value ? formatDateMedium(value, locale) : emptyText })] }));
|
|
16
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type Ref } from 'react';
|
|
2
|
+
import { type CalendarPopup } from './use-calendar-popup.js';
|
|
3
|
+
import { type DateEntry } from './use-date-entry.js';
|
|
4
|
+
export type TypedEntryProps = {
|
|
5
|
+
id: string;
|
|
6
|
+
/** The field's label element, which describes the calendar button. */
|
|
7
|
+
labelId: string;
|
|
8
|
+
describedBy: string | undefined;
|
|
9
|
+
entry: DateEntry;
|
|
10
|
+
placeholder: string;
|
|
11
|
+
/** The calendar button's name, `labels.chooseDate`. */
|
|
12
|
+
toggleName: string;
|
|
13
|
+
disabled: boolean;
|
|
14
|
+
required: boolean;
|
|
15
|
+
invalid: boolean;
|
|
16
|
+
controlRef: Ref<HTMLDivElement>;
|
|
17
|
+
inputRef: Ref<HTMLInputElement>;
|
|
18
|
+
buttonRef: Ref<HTMLButtonElement>;
|
|
19
|
+
trigger: CalendarPopup['trigger'];
|
|
20
|
+
onToggle: () => void;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* DateField's typed entry: the text input in the locale's order, checked
|
|
24
|
+
* on blur and Enter, and the calendar button inside its end.
|
|
25
|
+
*/
|
|
26
|
+
export declare function TypedEntry({ id, labelId, describedBy, entry, placeholder, toggleName, disabled, required, invalid, controlRef, inputRef, buttonRef, trigger, onToggle, }: TypedEntryProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { tabStop } from '../../tab-stop.js';
|
|
3
|
+
import { DateFieldGlyph } from './DateFieldGlyph.js';
|
|
4
|
+
/**
|
|
5
|
+
* DateField's typed entry: the text input in the locale's order, checked
|
|
6
|
+
* on blur and Enter, and the calendar button inside its end.
|
|
7
|
+
*/
|
|
8
|
+
export function TypedEntry({ id, labelId, describedBy, entry, placeholder, toggleName, disabled, required, invalid, controlRef, inputRef, buttonRef, trigger, onToggle, }) {
|
|
9
|
+
return (_jsxs("div", { className: "sw-date-field-control", ref: controlRef, children: [_jsx("input", { "aria-describedby": describedBy, "aria-invalid": invalid, autoComplete: "off", className: "sw-date-field-input", disabled: disabled, id: id, onBlur: entry.check, onChange: (event) => entry.change(event.currentTarget.value), onKeyDown: (event) => {
|
|
10
|
+
if (event.key === 'Enter')
|
|
11
|
+
entry.check();
|
|
12
|
+
}, placeholder: placeholder, ref: inputRef, required: required, spellCheck: false, type: "text", value: entry.text }), _jsxs("button", { ...trigger, "aria-describedby": labelId, className: "sw-date-field-button", disabled: disabled, onClick: onToggle, ref: buttonRef, tabIndex: tabStop, type: "button", children: [_jsx("span", { className: "sw-sr-only", children: toggleName }), _jsx(DateFieldGlyph, { name: "calendar" })] })] }));
|
|
13
|
+
}
|
|
@@ -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
|
};
|