@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.
Files changed (181) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/README.md +255 -5
  3. package/dist/calendar-labels.d.ts +2 -0
  4. package/dist/calendar-labels.js +7 -0
  5. package/dist/calendar-month.d.ts +6 -0
  6. package/dist/calendar-month.js +15 -0
  7. package/dist/components/BarChart.d.ts +20 -4
  8. package/dist/components/BarChart.js +13 -6
  9. package/dist/components/ButtonGroup.d.ts +8 -1
  10. package/dist/components/ButtonGroup.js +11 -2
  11. package/dist/components/DateField.d.ts +14 -2
  12. package/dist/components/DateField.js +14 -11
  13. package/dist/components/DateRangeButton.d.ts +65 -0
  14. package/dist/components/DateRangeButton.js +54 -0
  15. package/dist/components/DenominationGrid.d.ts +3 -2
  16. package/dist/components/DenominationGrid.js +21 -3
  17. package/dist/components/Dialog.d.ts +29 -13
  18. package/dist/components/Dialog.js +22 -20
  19. package/dist/components/Field.d.ts +13 -2
  20. package/dist/components/Field.js +14 -38
  21. package/dist/components/FilterChips.d.ts +7 -37
  22. package/dist/components/FilterChips.js +32 -24
  23. package/dist/components/ListGroup.d.ts +33 -0
  24. package/dist/components/ListGroup.js +19 -0
  25. package/dist/components/ListRow.d.ts +60 -0
  26. package/dist/components/ListRow.js +60 -0
  27. package/dist/components/NavCount.d.ts +21 -0
  28. package/dist/components/NavCount.js +23 -0
  29. package/dist/components/SectionNav.js +1 -8
  30. package/dist/components/SegmentedControl.d.ts +17 -1
  31. package/dist/components/SegmentedControl.js +11 -3
  32. package/dist/components/StatTile.d.ts +11 -4
  33. package/dist/components/StatTile.js +11 -4
  34. package/dist/components/Switch.d.ts +16 -1
  35. package/dist/components/Switch.js +17 -6
  36. package/dist/components/action-bar/ActionBarSummary.d.ts +10 -2
  37. package/dist/components/action-bar/ActionBarSummary.js +11 -2
  38. package/dist/components/{date-field → calendar}/CalendarGrid.d.ts +17 -2
  39. package/dist/components/{date-field → calendar}/CalendarGrid.js +11 -3
  40. package/dist/components/calendar/CalendarPopover.d.ts +25 -0
  41. package/dist/components/calendar/CalendarPopover.js +22 -0
  42. package/dist/components/calendar/MonthStep.d.ts +16 -0
  43. package/dist/components/calendar/MonthStep.js +17 -0
  44. package/dist/components/calendar/use-grid-focus.d.ts +18 -0
  45. package/dist/components/calendar/use-grid-focus.js +35 -0
  46. package/dist/components/calendar/wrap-tab.d.ts +7 -0
  47. package/dist/components/calendar/wrap-tab.js +29 -0
  48. package/dist/components/date-field/CalendarDialog.js +9 -67
  49. package/dist/components/date-field/CalendarEntryButton.d.ts +24 -0
  50. package/dist/components/date-field/CalendarEntryButton.js +16 -0
  51. package/dist/components/date-field/CalendarHeader.js +2 -11
  52. package/dist/components/date-field/TypedEntry.d.ts +26 -0
  53. package/dist/components/date-field/TypedEntry.js +13 -0
  54. package/dist/components/date-range/DateRangePopover.d.ts +7 -0
  55. package/dist/components/date-range/DateRangePopover.js +17 -0
  56. package/dist/components/date-range/DateRangeSheet.d.ts +7 -0
  57. package/dist/components/date-range/DateRangeSheet.js +15 -0
  58. package/dist/components/date-range/RangeMonths.d.ts +28 -0
  59. package/dist/components/date-range/RangeMonths.js +50 -0
  60. package/dist/components/date-range/RangePresets.d.ts +16 -0
  61. package/dist/components/date-range/RangePresets.js +10 -0
  62. package/dist/components/date-range/RangeSummary.d.ts +15 -0
  63. package/dist/components/date-range/RangeSummary.js +11 -0
  64. package/dist/components/date-range/range-panel-props.d.ts +31 -0
  65. package/dist/components/date-range/range-panel-props.js +1 -0
  66. package/dist/components/date-range/use-range-popup.d.ts +21 -0
  67. package/dist/components/date-range/use-range-popup.js +53 -0
  68. package/dist/components/denomination-grid/CountCell.d.ts +16 -0
  69. package/dist/components/denomination-grid/CountCell.js +14 -0
  70. package/dist/components/denomination-grid/DenominationStrip.d.ts +4 -1
  71. package/dist/components/denomination-grid/DenominationStrip.js +2 -2
  72. package/dist/components/denomination-grid/EntryCell.d.ts +3 -1
  73. package/dist/components/denomination-grid/EntryCell.js +2 -2
  74. package/dist/components/denomination-grid/StripRow.js +1 -10
  75. package/dist/components/denomination-grid/TransposedStrip.d.ts +17 -0
  76. package/dist/components/denomination-grid/TransposedStrip.js +43 -0
  77. package/dist/components/denomination-grid/types.d.ts +31 -0
  78. package/dist/components/denomination-grid/types.js +7 -1
  79. package/dist/components/denomination-grid/validate-options.d.ts +7 -0
  80. package/dist/components/denomination-grid/validate-options.js +27 -0
  81. package/dist/components/dialog/DialogFooter.d.ts +6 -5
  82. package/dist/components/dialog/DialogFooter.js +6 -6
  83. package/dist/components/dialog/validate.d.ts +24 -0
  84. package/dist/components/dialog/validate.js +23 -0
  85. package/dist/components/field/field-checks.d.ts +34 -0
  86. package/dist/components/field/field-checks.js +42 -0
  87. package/dist/components/filter-chips/FilterChip.d.ts +16 -0
  88. package/dist/components/filter-chips/FilterChip.js +20 -0
  89. package/dist/components/filter-chips/options.d.ts +75 -0
  90. package/dist/components/filter-chips/options.js +11 -0
  91. package/dist/components/filter-chips/validate.d.ts +9 -0
  92. package/dist/components/filter-chips/validate.js +48 -0
  93. package/dist/components/link-press.d.ts +7 -0
  94. package/dist/components/link-press.js +13 -0
  95. package/dist/components/list/ListRowAccessoryMark.d.ts +5 -0
  96. package/dist/components/list/ListRowAccessoryMark.js +7 -0
  97. package/dist/components/list/ListRowContent.d.ts +22 -0
  98. package/dist/components/list/ListRowContent.js +11 -0
  99. package/dist/components/list/list-row-container.d.ts +5 -0
  100. package/dist/components/list/list-row-container.js +6 -0
  101. package/dist/components/use-media-query.d.ts +5 -0
  102. package/dist/components/use-media-query.js +18 -0
  103. package/dist/css/chart-tones.d.ts +2 -0
  104. package/dist/css/chart-tones.js +5 -1
  105. package/dist/css/chevron.d.ts +6 -0
  106. package/dist/css/chevron.js +14 -0
  107. package/dist/css/css-bar-chart.d.ts +17 -2
  108. package/dist/css/css-bar-chart.js +38 -7
  109. package/dist/css/css-button-group-attached.d.ts +3 -0
  110. package/dist/css/css-button-group-attached.js +26 -5
  111. package/dist/css/css-button-group-track.js +1 -1
  112. package/dist/css/css-canvas-grouped.d.ts +17 -0
  113. package/dist/css/css-canvas-grouped.js +40 -0
  114. package/dist/css/css-checkbox.js +2 -2
  115. package/dist/css/css-components.js +15 -0
  116. package/dist/css/css-count-pill.d.ts +2 -0
  117. package/dist/css/css-count-pill.js +54 -0
  118. package/dist/css/css-date-field-calendar-entry.d.ts +11 -0
  119. package/dist/css/css-date-field-calendar-entry.js +50 -0
  120. package/dist/css/css-date-field.js +7 -2
  121. package/dist/css/css-date-range-presets.d.ts +2 -0
  122. package/dist/css/css-date-range-presets.js +109 -0
  123. package/dist/css/css-date-range.d.ts +2 -0
  124. package/dist/css/css-date-range.js +197 -0
  125. package/dist/css/css-denomination-grid.js +4 -0
  126. package/dist/css/css-denomination-transposed.d.ts +4 -0
  127. package/dist/css/css-denomination-transposed.js +178 -0
  128. package/dist/css/css-dialog-canvas.d.ts +3 -0
  129. package/dist/css/css-dialog-canvas.js +20 -0
  130. package/dist/css/css-dialog-frame.js +19 -2
  131. package/dist/css/css-dialog-grabber.d.ts +4 -0
  132. package/dist/css/css-dialog-grabber.js +56 -0
  133. package/dist/css/css-dialog.js +3 -2
  134. package/dist/css/css-document.js +3 -0
  135. package/dist/css/css-field-width.d.ts +12 -0
  136. package/dist/css/css-field-width.js +49 -0
  137. package/dist/css/css-field.js +4 -0
  138. package/dist/css/css-filter-chips-layout.d.ts +2 -0
  139. package/dist/css/css-filter-chips-layout.js +42 -0
  140. package/dist/css/css-filter-chips.js +4 -0
  141. package/dist/css/css-list-row.d.ts +8 -0
  142. package/dist/css/css-list-row.js +209 -0
  143. package/dist/css/css-list.d.ts +3 -0
  144. package/dist/css/css-list.js +85 -0
  145. package/dist/css/css-scroll-region.d.ts +18 -0
  146. package/dist/css/css-scroll-region.js +41 -0
  147. package/dist/css/css-section-nav-filled.js +2 -25
  148. package/dist/css/css-segmented-sizing.d.ts +13 -0
  149. package/dist/css/css-segmented-sizing.js +66 -0
  150. package/dist/css/css-segmented.js +4 -0
  151. package/dist/css/css-stat-tile.d.ts +7 -0
  152. package/dist/css/css-stat-tile.js +16 -0
  153. package/dist/css/css-steps.js +2 -2
  154. package/dist/css/css-switch.d.ts +6 -0
  155. package/dist/css/css-switch.js +39 -0
  156. package/dist/css/css-table-bounded-edges.js +4 -27
  157. package/dist/css/range-band.d.ts +2 -0
  158. package/dist/css/range-band.js +7 -0
  159. package/dist/css/stacking.d.ts +1 -0
  160. package/dist/css/stacking.js +6 -3
  161. package/dist/css/stylesheet.js +10 -0
  162. package/dist/date-field-labels.d.ts +5 -0
  163. package/dist/date-field-labels.js +1 -0
  164. package/dist/date-range-draft.d.ts +26 -0
  165. package/dist/date-range-draft.js +36 -0
  166. package/dist/date-range-input.d.ts +26 -0
  167. package/dist/date-range-input.js +58 -0
  168. package/dist/date-range-labels.d.ts +22 -0
  169. package/dist/date-range-labels.js +26 -0
  170. package/dist/date-range-view.d.ts +12 -0
  171. package/dist/date-range-view.js +29 -0
  172. package/dist/date-range-words.d.ts +11 -0
  173. package/dist/date-range-words.js +40 -0
  174. package/dist/index.d.ts +16 -9
  175. package/dist/index.js +14 -7
  176. package/dist/list-options.d.ts +71 -0
  177. package/dist/list-options.js +84 -0
  178. package/dist/styles.css +863 -41
  179. package/dist/theme/ThemeProvider.d.ts +11 -1
  180. package/dist/theme/ThemeProvider.js +4 -2
  181. package/package.json +1 -1
@@ -0,0 +1,34 @@
1
+ import { type KeyboardEvent } from 'react';
2
+ import type { FieldProps } from '../Field.js';
3
+ export declare const fieldVariants: readonly ["outlined", "search"];
4
+ export type FieldVariant = (typeof fieldVariants)[number];
5
+ /** The props Field reads from, and sets on, its native control child. */
6
+ export type FieldControlProps = {
7
+ 'aria-describedby'?: string;
8
+ 'aria-label'?: string;
9
+ 'aria-invalid'?: boolean | 'true' | 'false';
10
+ 'aria-labelledby'?: string;
11
+ id?: string;
12
+ required?: boolean;
13
+ disabled?: boolean;
14
+ readOnly?: boolean;
15
+ type?: string;
16
+ value?: unknown;
17
+ enterKeyHint?: string;
18
+ onKeyDown?: (event: KeyboardEvent<HTMLInputElement>) => void;
19
+ };
20
+ /**
21
+ * Fails closed on a Field it cannot draw: an unknown variant or width,
22
+ * search props on an outlined field, and a search field without its clear
23
+ * button, its handler or one controlled native input.
24
+ */
25
+ export declare function assertFieldProps(props: FieldProps, child: unknown): void;
26
+ /**
27
+ * `invalid` was passed, even as `false`, but not as `undefined` (a wrapper
28
+ * forwarding an optional prop passes nothing): such a field needs one native
29
+ * control from its first render, so a form learns it on the happy path
30
+ * instead of on its first failed submit. `error` keeps its older rule (it
31
+ * requires the control only while it holds a message), so a composed
32
+ * child beside `error={undefined}` still renders.
33
+ */
34
+ export declare function passesInvalid(props: FieldProps): boolean;
@@ -0,0 +1,42 @@
1
+ import { isValidElement } from 'react';
2
+ import { assertFieldWidth } from '../../css/css-field-width.js';
3
+ export const fieldVariants = ['outlined', 'search'];
4
+ /**
5
+ * Fails closed on a Field it cannot draw: an unknown variant or width,
6
+ * search props on an outlined field, and a search field without its clear
7
+ * button, its handler or one controlled native input.
8
+ */
9
+ export function assertFieldProps(props, child) {
10
+ const { variant = 'outlined' } = props;
11
+ assertFieldWidth(props.width);
12
+ if (!fieldVariants.includes(variant))
13
+ throw new RangeError(`Unknown Field variant: ${String(variant)}`);
14
+ if (variant !== 'search') {
15
+ if (props.leading !== undefined ||
16
+ props.clearLabel !== undefined ||
17
+ props.onClear !== undefined)
18
+ throw new TypeError('Field leading, clearLabel and onClear are for variant="search"');
19
+ return;
20
+ }
21
+ if (!props.clearLabel?.trim())
22
+ throw new RangeError('A search Field needs a nonempty clearLabel');
23
+ if (typeof props.onClear !== 'function')
24
+ throw new TypeError('A search Field needs onClear');
25
+ if (props.prefix || props.suffix)
26
+ throw new TypeError('A search Field takes leading, not prefix or suffix');
27
+ if (!isValidElement(child) || child.type !== 'input')
28
+ throw new TypeError('A search Field needs one native input child');
29
+ if (child.props.value === undefined)
30
+ throw new TypeError('A search Field needs a controlled input: pass value (and onChange)');
31
+ }
32
+ /**
33
+ * `invalid` was passed, even as `false`, but not as `undefined` (a wrapper
34
+ * forwarding an optional prop passes nothing): such a field needs one native
35
+ * control from its first render, so a form learns it on the happy path
36
+ * instead of on its first failed submit. `error` keeps its older rule (it
37
+ * requires the control only while it holds a message), so a composed
38
+ * child beside `error={undefined}` still renders.
39
+ */
40
+ export function passesInvalid(props) {
41
+ return props.invalid !== undefined || props.changed !== undefined;
42
+ }
@@ -0,0 +1,16 @@
1
+ import { type FilterChipCountAppearance, type FilterChipOption } from './options.js';
2
+ type FilterChipProps = {
3
+ option: FilterChipOption;
4
+ /** The group's shared input name. */
5
+ name: string;
6
+ type: 'radio' | 'checkbox';
7
+ checked: boolean;
8
+ required: boolean;
9
+ /** Whether this chip is a Tab stop: every checkbox, and one radio. */
10
+ tabStopHere: boolean;
11
+ countAppearance: FilterChipCountAppearance;
12
+ onCheckedChange: (checked: boolean) => void;
13
+ };
14
+ /** One chip: a native radio or checkbox under its face. */
15
+ export declare function FilterChip({ option, name, type, checked, required, tabStopHere, countAppearance, onCheckedChange, }: FilterChipProps): import("react").JSX.Element;
16
+ export {};
@@ -0,0 +1,20 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { cx } from '../../class-names.js';
3
+ import { tabStop } from '../../tab-stop.js';
4
+ /**
5
+ * A chip's count: a muted figure after a space, or inline in the chip's
6
+ * own words after a middle dot hidden from assistive technology, so the
7
+ * name reads "Open 3" either way.
8
+ */
9
+ function ChipCount({ count, appearance, }) {
10
+ if (appearance === 'inline')
11
+ return (_jsxs(_Fragment, { children: [' ', _jsx("span", { "aria-hidden": "true", children: "\u00B7" }), ' ', _jsx("span", { className: "sw-filter-chip-count-inline", children: count })] }));
12
+ return (_jsxs(_Fragment, { children: [' ', _jsx("span", { className: "sw-filter-chip-count", children: count })] }));
13
+ }
14
+ /** One chip: a native radio or checkbox under its face. */
15
+ export function FilterChip({ option, name, type, checked, required, tabStopHere, countAppearance, onCheckedChange, }) {
16
+ return (_jsxs("label", { className: cx('sw-filter-chip', option.count === 0 && 'sw-filter-chip-quiet'), children: [_jsx("input", { checked: checked, className: "sw-filter-chip-input", disabled: option.disabled, name: name, onChange: (event) => {
17
+ if (!option.disabled)
18
+ onCheckedChange(event.currentTarget.checked);
19
+ }, required: required, tabIndex: tabStopHere ? tabStop : undefined, type: type, value: option.value }), _jsxs("span", { className: "sw-filter-chip-face", children: [option.label, option.count === undefined ? null : (_jsx(ChipCount, { appearance: countAppearance, count: option.count }))] })] }));
20
+ }
@@ -0,0 +1,75 @@
1
+ import { type RequiredMark } from '../FieldLabelText.js';
2
+ export type FilterChipOption = {
3
+ value: string;
4
+ label: string;
5
+ count?: number;
6
+ disabled?: boolean;
7
+ };
8
+ /**
9
+ * How a chip's count is drawn: `figure` (the default), a muted tabular
10
+ * figure after a space; `inline`, the label's own words and type, after a
11
+ * middle dot ("Open · 3"), the dot hidden from assistive technology.
12
+ */
13
+ export declare const filterChipCountAppearances: readonly ["figure", "inline"];
14
+ export type FilterChipCountAppearance = (typeof filterChipCountAppearances)[number];
15
+ /**
16
+ * What the chips do when they do not fit their row: `wrap` (the default)
17
+ * onto more rows, or `scroll` sideways on one row.
18
+ */
19
+ export declare const filterChipsOverflows: readonly ["wrap", "scroll"];
20
+ export type FilterChipsOverflow = (typeof filterChipsOverflows)[number];
21
+ /** One chosen chip, a radio in a group. */
22
+ type SingleChoice = {
23
+ multiple?: false;
24
+ /** The chosen option's value, or `''` while nothing is chosen yet. */
25
+ value: string;
26
+ onChange: (value: string) => void;
27
+ /**
28
+ * A choice must be made, such as a reason: each radio is `required` and
29
+ * the label shows the required mark (hidden from assistive technology,
30
+ * which reads the radios' own state).
31
+ */
32
+ required?: boolean;
33
+ };
34
+ /**
35
+ * Several chips on at once, each a checkbox, such as the days of a week.
36
+ * Nothing makes a checkbox group required, so `required` is refused: say
37
+ * what is needed in `error`.
38
+ */
39
+ type MultipleChoice = {
40
+ multiple: true;
41
+ /** The chosen options' values, in any order; empty for none. */
42
+ value: readonly string[];
43
+ /** Every chosen value, in the options' order. */
44
+ onChange: (values: string[]) => void;
45
+ required?: false;
46
+ };
47
+ type FilterChipsChoice = SingleChoice | MultipleChoice;
48
+ export type FilterChipsProps = FilterChipsChoice & {
49
+ label: string;
50
+ options: readonly FilterChipOption[];
51
+ /**
52
+ * Keeps the label as the group's name but does not draw it, for chips
53
+ * whose meaning their words and place already show.
54
+ */
55
+ labelVisuallyHidden?: boolean;
56
+ /**
57
+ * `hidden` leaves out the label's " *" while the control stays required
58
+ * (and announced so); `visible` (default) draws it.
59
+ * When hidden, say in visible text which fields must be filled; a
60
+ * placeholder alone is not enough.
61
+ */
62
+ requiredMark?: RequiredMark;
63
+ countAppearance?: FilterChipCountAppearance;
64
+ overflow?: FilterChipsOverflow;
65
+ /**
66
+ * A message under the chips, such as "Pick a reason": a polite live
67
+ * region linked to the group by `aria-describedby`; the group is marked
68
+ * invalid and every chip takes the danger edge. An empty string is no
69
+ * error.
70
+ */
71
+ error?: string;
72
+ /** More ids that describe the group, such as a hint beside it. */
73
+ 'aria-describedby'?: string;
74
+ };
75
+ export {};
@@ -0,0 +1,11 @@
1
+ /**
2
+ * How a chip's count is drawn: `figure` (the default), a muted tabular
3
+ * figure after a space; `inline`, the label's own words and type, after a
4
+ * middle dot ("Open · 3"), the dot hidden from assistive technology.
5
+ */
6
+ export const filterChipCountAppearances = ['figure', 'inline'];
7
+ /**
8
+ * What the chips do when they do not fit their row: `wrap` (the default)
9
+ * onto more rows, or `scroll` sideways on one row.
10
+ */
11
+ export const filterChipsOverflows = ['wrap', 'scroll'];
@@ -0,0 +1,9 @@
1
+ import { type FilterChipsProps } from './options.js';
2
+ /**
3
+ * Fails closed on chips it cannot draw: an empty label, no options, blank
4
+ * or repeated values, a chosen value that is no option, a count that is
5
+ * not a whole number from 0, an unknown count appearance or overflow, and
6
+ * a multiple choice asked to be required or given a value that is not a
7
+ * list.
8
+ */
9
+ export declare function assertFilterChips(props: FilterChipsProps): void;
@@ -0,0 +1,48 @@
1
+ import { assertRequiredMark } from '../FieldLabelText.js';
2
+ import { filterChipCountAppearances, filterChipsOverflows, } from './options.js';
3
+ function isCount(count) {
4
+ return count === undefined || (Number.isInteger(count) && count >= 0);
5
+ }
6
+ function chosenValues(props) {
7
+ if (props.multiple)
8
+ return props.value;
9
+ return props.value === '' ? [] : [props.value];
10
+ }
11
+ /**
12
+ * Fails closed on chips it cannot draw: an empty label, no options, blank
13
+ * or repeated values, a chosen value that is no option, a count that is
14
+ * not a whole number from 0, an unknown count appearance or overflow, and
15
+ * a multiple choice asked to be required or given a value that is not a
16
+ * list.
17
+ */
18
+ export function assertFilterChips(props) {
19
+ const { label, options } = props;
20
+ if (!label.trim())
21
+ throw new RangeError('label must not be empty');
22
+ assertRequiredMark(props.requiredMark ?? 'visible');
23
+ if (options.length === 0 ||
24
+ options.some((option) => !option.value.trim() || !option.label.trim()))
25
+ throw new RangeError('options must have nonempty values and labels');
26
+ const values = options.map((option) => option.value);
27
+ if (new Set(values).size !== values.length)
28
+ throw new RangeError('options must have unique values');
29
+ if (props.multiple) {
30
+ if (!Array.isArray(props.value))
31
+ throw new RangeError('a multiple FilterChips value must be a list');
32
+ if (props.required)
33
+ throw new RangeError('a multiple FilterChips cannot be required; say what is needed in error');
34
+ }
35
+ const chosen = chosenValues(props);
36
+ if (chosen.some((value) => !values.includes(value)))
37
+ throw new RangeError(props.multiple
38
+ ? 'every value must match an option'
39
+ : 'value must match an option or be empty');
40
+ if (options.some((option) => !isCount(option.count)))
41
+ throw new RangeError('count must be a non-negative integer');
42
+ const countAppearance = props.countAppearance ?? 'figure';
43
+ if (!filterChipCountAppearances.includes(countAppearance))
44
+ throw new RangeError(`Unknown FilterChips countAppearance: ${String(countAppearance)}`);
45
+ const overflow = props.overflow ?? 'wrap';
46
+ if (!filterChipsOverflows.includes(overflow))
47
+ throw new RangeError(`Unknown FilterChips overflow: ${String(overflow)}`);
48
+ }
@@ -0,0 +1,7 @@
1
+ import { type MouseEvent } from 'react';
2
+ /**
3
+ * Whether a press on a link is a plain one a client router may take: the
4
+ * primary button, no modifier key (which opens a tab or a window) and not
5
+ * already handled. Shared by SectionNav and ListRow.
6
+ */
7
+ export declare function plainPress(event: MouseEvent<HTMLAnchorElement>): boolean;
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Whether a press on a link is a plain one a client router may take: the
3
+ * primary button, no modifier key (which opens a tab or a window) and not
4
+ * already handled. Shared by SectionNav and ListRow.
5
+ */
6
+ export function plainPress(event) {
7
+ return (event.button === 0 &&
8
+ !event.defaultPrevented &&
9
+ !event.metaKey &&
10
+ !event.ctrlKey &&
11
+ !event.shiftKey &&
12
+ !event.altKey);
13
+ }
@@ -0,0 +1,5 @@
1
+ import { type ListRowAccessory } from '../../list-options.js';
2
+ /** A row's chevron or check: control chrome drawn by the generated CSS. */
3
+ export declare function ListRowAccessoryMark({ kind }: {
4
+ kind: ListRowAccessory;
5
+ }): import("react").JSX.Element | null;
@@ -0,0 +1,7 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ /** A row's chevron or check: control chrome drawn by the generated CSS. */
3
+ export function ListRowAccessoryMark({ kind }) {
4
+ if (kind === 'none')
5
+ return null;
6
+ return (_jsx("span", { "aria-hidden": "true", className: kind === 'check' ? 'sw-list-row-check' : 'sw-list-row-chevron' }));
7
+ }
@@ -0,0 +1,22 @@
1
+ import { type ReactNode } from 'react';
2
+ import { type BadgeTone } from '../../css/css-data.js';
3
+ import { type ListRowAccessory, type ListRowTitleAppearance, type ListRowTone, type ListRowValueTone } from '../../list-options.js';
4
+ export type ListRowContentProps = {
5
+ /** Prefixes the ids that name and describe a pressable row. */
6
+ id: string;
7
+ title: string;
8
+ detail?: string;
9
+ value?: string;
10
+ valueDetail?: string;
11
+ valueTone?: ListRowValueTone;
12
+ valueBadge?: BadgeTone;
13
+ leading?: ReactNode;
14
+ tone: ListRowTone;
15
+ titleAppearance: ListRowTitleAppearance;
16
+ accessory: ListRowAccessory;
17
+ };
18
+ /**
19
+ * A list row's parts in reading order: the hidden leading mark, the title
20
+ * over its detail, the value over its detail, then the chevron or check.
21
+ */
22
+ export declare function ListRowContent({ id, title, detail, value, valueDetail, valueTone, valueBadge, leading, tone, titleAppearance, accessory, }: ListRowContentProps): import("react").JSX.Element;
@@ -0,0 +1,11 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { cx } from '../../class-names.js';
3
+ import { Badge } from '../Badge.js';
4
+ import { ListRowAccessoryMark } from './ListRowAccessoryMark.js';
5
+ /**
6
+ * A list row's parts in reading order: the hidden leading mark, the title
7
+ * over its detail, the value over its detail, then the chevron or check.
8
+ */
9
+ export function ListRowContent({ id, title, detail, value, valueDetail, valueTone, valueBadge, leading, tone, titleAppearance, accessory, }) {
10
+ return (_jsxs(_Fragment, { children: [leading === undefined ? null : (_jsx("span", { "aria-hidden": "true", className: "sw-list-row-leading", children: leading })), _jsxs("span", { className: "sw-list-row-label", children: [_jsx("span", { className: cx('sw-list-row-title', titleAppearance === 'plain' && 'sw-list-row-title-plain', tone === 'danger' && 'sw-list-row-title-danger'), id: `${id}-title`, children: title }), detail ? (_jsx("span", { className: "sw-list-row-detail", id: `${id}-detail`, children: detail })) : null] }), value === undefined ? null : (_jsxs("span", { className: "sw-list-row-end", children: [_jsx("span", { className: cx('sw-list-row-value', valueTone && `sw-list-row-value-${valueTone}`), id: `${id}-value`, children: valueBadge ? _jsx(Badge, { tone: valueBadge, children: value }) : value }), valueDetail ? (_jsx("span", { className: "sw-list-row-value-detail", id: `${id}-value-detail`, children: valueDetail })) : null] })), _jsx(ListRowAccessoryMark, { kind: accessory })] }));
11
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * The element a ListRow renders as: an `li` in a ListGroup's list, a `div`
3
+ * anywhere else, such as a ListGroup's `footer`.
4
+ */
5
+ export declare const ListRowContainer: import("react").Context<"div" | "li">;
@@ -0,0 +1,6 @@
1
+ import { createContext } from 'react';
2
+ /**
3
+ * The element a ListRow renders as: an `li` in a ListGroup's list, a `div`
4
+ * anywhere else, such as a ListGroup's `footer`.
5
+ */
6
+ export const ListRowContainer = createContext('div');
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Whether a media query matches now, following its changes. Where there is
3
+ * no `matchMedia` (a server render, jsdom) it reads as `fallback`.
4
+ */
5
+ export declare function useMediaQuery(query: string, fallback: boolean): boolean;
@@ -0,0 +1,18 @@
1
+ import { useCallback, useSyncExternalStore } from 'react';
2
+ function canMatch() {
3
+ return (typeof window !== 'undefined' && typeof window.matchMedia === 'function');
4
+ }
5
+ /**
6
+ * Whether a media query matches now, following its changes. Where there is
7
+ * no `matchMedia` (a server render, jsdom) it reads as `fallback`.
8
+ */
9
+ export function useMediaQuery(query, fallback) {
10
+ const subscribe = useCallback((onChange) => {
11
+ if (!canMatch())
12
+ return () => undefined;
13
+ const list = window.matchMedia(query);
14
+ list.addEventListener('change', onChange);
15
+ return () => list.removeEventListener('change', onChange);
16
+ }, [query]);
17
+ return useSyncExternalStore(subscribe, () => (canMatch() ? window.matchMedia(query).matches : fallback), () => fallback);
18
+ }
@@ -46,6 +46,8 @@ export declare const dangerFillShare = 0.4;
46
46
  export declare const chartFillMixes: Record<ChartFillTone, ChartColorMix>;
47
47
  /** Each stroke tone's colour: a semantic colour, or muted for neutral. */
48
48
  export declare const chartStrokeColors: Record<ChartStrokeTone, ChartColorSource>;
49
+ /** A stroke tone's colour as CSS, such as a line's or a bar's. */
50
+ export declare function chartStroke(tone: ChartStrokeTone): string;
49
51
  /**
50
52
  * How much of a line's stroke the area under it takes, over transparency:
51
53
  * a quiet tint behind the line, decorative (the line carries the values).
@@ -70,6 +70,10 @@ export const chartStrokeColors = {
70
70
  danger: { semantic: 'danger' },
71
71
  neutral: { semantic: 'muted' },
72
72
  };
73
+ /** A stroke tone's colour as CSS, such as a line's or a bar's. */
74
+ export function chartStroke(tone) {
75
+ return sourceProperty(chartStrokeColors[tone]);
76
+ }
73
77
  /**
74
78
  * How much of a line's stroke the area under it takes, over transparency:
75
79
  * a quiet tint behind the line, decorative (the line carries the values).
@@ -115,7 +119,7 @@ function toneDeclarations(tone) {
115
119
  if (isOneOf(chartFillTones, tone))
116
120
  declarations.push(`--sw-chart-fill: ${chartFill(tone)};`);
117
121
  if (isOneOf(chartStrokeTones, tone))
118
- declarations.push(`--sw-chart-stroke: ${sourceProperty(chartStrokeColors[tone])};`);
122
+ declarations.push(`--sw-chart-stroke: ${chartStroke(tone)};`);
119
123
  if (isOneOf(chartInkTones, tone))
120
124
  declarations.push(`--sw-chart-ink: ${chartColorMix(chartInkMixes[tone])};`);
121
125
  return declarations.join(' ');
@@ -6,3 +6,9 @@
6
6
  export declare function chevronStrokeIn(color: string): string;
7
7
  /** The muted chevron Accordion, Select and the menus share. */
8
8
  export declare const chevronStroke: string;
9
+ /**
10
+ * A check's two strokes in `color`: the bottom and right borders of a box
11
+ * the caller sizes and turns 45°, so forced colours keep them. Checkbox,
12
+ * Steps and ListRow share it.
13
+ */
14
+ export declare function checkStrokeIn(color: string): string;
@@ -1,3 +1,8 @@
1
+ /*
2
+ * The strokes of Scalewing's private control chrome, drawn with borders so
3
+ * forced colours keep them: the chevron (Accordion, Select, the menus,
4
+ * Pagination, ListRow) and the check (Checkbox, Steps, ListRow).
5
+ */
1
6
  /**
2
7
  * A stroked chevron in `color`: two sides of a `space-2` square (its
3
8
  * bottom and right borders, so forced colours keep it), turned by each
@@ -12,3 +17,12 @@ export function chevronStrokeIn(color) {
12
17
  }
13
18
  /** The muted chevron Accordion, Select and the menus share. */
14
19
  export const chevronStroke = chevronStrokeIn('var(--sw-color-muted)');
20
+ /**
21
+ * A check's two strokes in `color`: the bottom and right borders of a box
22
+ * the caller sizes and turns 45°, so forced colours keep them. Checkbox,
23
+ * Steps and ListRow share it.
24
+ */
25
+ export function checkStrokeIn(color) {
26
+ return `border-bottom: 2px solid ${color};
27
+ border-right: 2px solid ${color};`;
28
+ }
@@ -1,9 +1,24 @@
1
+ import { type ChartStrokeTone } from './chart-tones.js';
1
2
  /**
2
3
  * The meanings a bar's fill can take in place of its sign's default (the
3
4
  * accent, or danger for a negative value), such as a diverging chart whose
4
5
  * overage is a warning and whose shortage is danger.
5
6
  */
6
- export declare const barChartTones: readonly ["accent", "success", "warning", "danger"];
7
- export type BarChartTone = (typeof barChartTones)[number];
7
+ export declare const barChartTones: readonly ["accent", "success", "warning", "danger", "neutral"];
8
+ export type BarChartTone = ChartStrokeTone;
9
+ /**
10
+ * A tone's colour: the charts' stroke colour of that tone (`chart-tones.ts`),
11
+ * a semantic colour at 3:1 or more, the muted grey for `neutral`, a bar
12
+ * that is counted but not the point (the other days beside the busiest
13
+ * ones). Not the lighter fill mix a column takes, which is 1.5:1.
14
+ */
15
+ export declare function barChartToneColor(tone: BarChartTone): string;
16
+ /**
17
+ * `track` (the default) draws each bar in a track with the value axis
18
+ * under the bars; `bare` draws the bars alone, with no track and no axis,
19
+ * for bars whose values are all written beside them.
20
+ */
21
+ export declare const barChartAppearances: readonly ["track", "bare"];
22
+ export type BarChartAppearance = (typeof barChartAppearances)[number];
8
23
  export declare function cssBarChartClasses(): string;
9
24
  export declare function barChartClassCatalog(): string[];
@@ -1,4 +1,5 @@
1
1
  import { typographyVariants } from '@scalewing/tokens';
2
+ import { chartStroke, chartStrokeTones, } from './chart-tones.js';
2
3
  const caption = typographyVariants.caption;
3
4
  const data = typographyVariants.data;
4
5
  /**
@@ -6,12 +7,22 @@ const data = typographyVariants.data;
6
7
  * accent, or danger for a negative value), such as a diverging chart whose
7
8
  * overage is a warning and whose shortage is danger.
8
9
  */
9
- export const barChartTones = [
10
- 'accent',
11
- 'success',
12
- 'warning',
13
- 'danger',
14
- ];
10
+ export const barChartTones = chartStrokeTones;
11
+ /**
12
+ * A tone's colour: the charts' stroke colour of that tone (`chart-tones.ts`),
13
+ * a semantic colour at 3:1 or more, the muted grey for `neutral`, a bar
14
+ * that is counted but not the point (the other days beside the busiest
15
+ * ones). Not the lighter fill mix a column takes, which is 1.5:1.
16
+ */
17
+ export function barChartToneColor(tone) {
18
+ return chartStroke(tone);
19
+ }
20
+ /**
21
+ * `track` (the default) draws each bar in a track with the value axis
22
+ * under the bars; `bare` draws the bars alone, with no track and no axis,
23
+ * for bars whose values are all written beside them.
24
+ */
25
+ export const barChartAppearances = ['track', 'bare'];
15
26
  /*
16
27
  * A tone's rule follows the negative fill's, at the same specificity, so a
17
28
  * toned negative bar keeps its diverging geometry and takes the tone's
@@ -22,7 +33,7 @@ export const barChartTones = [
22
33
  */
23
34
  function toneRules() {
24
35
  return barChartTones
25
- .map((tone) => `.sw-bar-chart-fill-${tone} { background: var(--sw-color-${tone}); }`)
36
+ .map((tone) => `.sw-bar-chart-fill-${tone} { background: ${barChartToneColor(tone)}; }`)
26
37
  .join('\n');
27
38
  }
28
39
  /*
@@ -66,6 +77,23 @@ function divergingRules() {
66
77
  .sw-bar-chart-diverging .sw-bar-chart-track::before { background: CanvasText; }
67
78
  }`;
68
79
  }
80
+ /*
81
+ * A bare chart draws no track: the bar sits on the surface around it, the
82
+ * track's room kept so the labels and values line up as before (in forced
83
+ * colours its edge is the canvas colour, as forcing replaces a transparent
84
+ * one). A diverging bare chart keeps its zero hairline.
85
+ */
86
+ function bareRules() {
87
+ return `.sw-bar-chart-bare .sw-bar-chart-track {
88
+ background: transparent;
89
+ border-color: transparent;
90
+ }
91
+
92
+ /* Forced colours replace a transparent edge; the canvas colour hides it. */
93
+ @media (forced-colors: active) {
94
+ .sw-bar-chart-bare .sw-bar-chart-track { border-color: Canvas; }
95
+ }`;
96
+ }
69
97
  export function cssBarChartClasses() {
70
98
  return `.sw-bar-chart {
71
99
  display: grid;
@@ -151,6 +179,8 @@ ${toneRules()}
151
179
 
152
180
  ${divergingRules()}
153
181
 
182
+ ${bareRules()}
183
+
154
184
  @media (forced-colors: active) {
155
185
  .sw-bar-chart-fill { background: CanvasText; }
156
186
  }`;
@@ -169,5 +199,6 @@ export function barChartClassCatalog() {
169
199
  'sw-bar-chart-value',
170
200
  'sw-bar-chart-axis-track',
171
201
  'sw-bar-chart-diverging',
202
+ 'sw-bar-chart-bare',
172
203
  ];
173
204
  }
@@ -1,2 +1,5 @@
1
+ import { type Breakpoint } from './breakpoints.js';
2
+ /** The class that stretches an attached group below a breakpoint. */
3
+ export declare function buttonGroupFullWidthClass(breakpoint: Breakpoint): string;
1
4
  export declare function cssButtonGroupAttachedClasses(): string;
2
5
  export declare function buttonGroupAttachedClassCatalog(): string[];
@@ -1,6 +1,10 @@
1
1
  import { breakpointQuery } from './breakpoints.js';
2
2
  import { buttonGroupTrackClassCatalog, cssButtonGroupTrackClasses, } from './css-button-group-track.js';
3
3
  import { joinedZIndex } from './stacking.js';
4
+ /** The class that stretches an attached group below a breakpoint. */
5
+ export function buttonGroupFullWidthClass(breakpoint) {
6
+ return `sw-button-group-full-below-${breakpoint}`;
7
+ }
4
8
  /*
5
9
  * An attached ButtonGroup is one joined control, such as Previous, Today
6
10
  * and Next: its direct Button children side by side with no gap and one
@@ -13,6 +17,13 @@ import { joinedZIndex } from './stacking.js';
13
17
  * divider in its own text colour at low strength, so a joined row of them
14
18
  * still reads as separate buttons; forced colours draw every border.
15
19
  *
20
+ * The buttons are found by type, not by place, so an open calendar a
21
+ * button renders beside itself (CalendarButton, DateRangeButton) does not
22
+ * cost the next button its divider or the last one its rounded corners.
23
+ *
24
+ * With `fullWidthBelow`, below that breakpoint the group fills its column
25
+ * and its buttons share the width equally.
26
+ *
16
27
  * The group is its own stacking context: a pressed button's ring paints
17
28
  * over its neighbours and the focused button's standard outer ring over
18
29
  * both, so neither is covered by the next button.
@@ -33,21 +44,21 @@ ${group} > .sw-button {
33
44
  border-radius: 0;
34
45
  }
35
46
 
36
- ${group} > .sw-button + .sw-button {
47
+ ${group} > .sw-button ~ .sw-button {
37
48
  margin-inline-start: -1px;
38
49
  }
39
50
 
40
- ${group} > .sw-button:first-child {
51
+ ${group} > .sw-button:first-of-type {
41
52
  border-end-start-radius: var(--sw-radius-sm);
42
53
  border-start-start-radius: var(--sw-radius-sm);
43
54
  }
44
55
 
45
- ${group} > .sw-button:last-child {
56
+ ${group} > .sw-button:last-of-type {
46
57
  border-end-end-radius: var(--sw-radius-sm);
47
58
  border-start-end-radius: var(--sw-radius-sm);
48
59
  }
49
60
 
50
- ${group} > .sw-button + .sw-button:not(.sw-button-secondary) {
61
+ ${group} > .sw-button ~ .sw-button:not(.sw-button-secondary) {
51
62
  border-inline-start-color: color-mix(in srgb, currentColor 30%, transparent);
52
63
  }
53
64
 
@@ -66,8 +77,18 @@ ${group} > .sw-button:focus-visible {
66
77
  ${group} > * { width: auto; }
67
78
  }
68
79
 
80
+ /* Its own block, after the phone row's, so a group that does not fill is unchanged. */
81
+ @media ${breakpointQuery('below', 'md')} {
82
+ ${group}.${buttonGroupFullWidthClass('md')} { width: 100%; }
83
+ ${group}.${buttonGroupFullWidthClass('md')} > .sw-button { flex: 1 1 0; min-width: 0; }
84
+ }
85
+
69
86
  ${cssButtonGroupTrackClasses()}`;
70
87
  }
71
88
  export function buttonGroupAttachedClassCatalog() {
72
- return ['sw-button-group-attached', ...buttonGroupTrackClassCatalog()];
89
+ return [
90
+ 'sw-button-group-attached',
91
+ buttonGroupFullWidthClass('md'),
92
+ ...buttonGroupTrackClassCatalog(),
93
+ ];
73
94
  }
@@ -30,7 +30,7 @@ ${track} > .sw-button {
30
30
  color: var(--sw-color-text);
31
31
  }
32
32
 
33
- ${track} > .sw-button + .sw-button {
33
+ ${track} > .sw-button ~ .sw-button {
34
34
  border-inline-start-color: var(--sw-color-border);
35
35
  }
36
36