@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
@@ -1,7 +1,7 @@
1
1
  import { type HTMLAttributes } from 'react';
2
- import { type BarChartTone, barChartTones } from '../css/css-bar-chart.js';
3
- export type { BarChartTone };
4
- export { barChartTones };
2
+ import { type BarChartAppearance, barChartAppearances, type BarChartTone, barChartTones } from '../css/css-bar-chart.js';
3
+ export type { BarChartAppearance, BarChartTone };
4
+ export { barChartAppearances, barChartTones };
5
5
  export type BarChartItem = {
6
6
  label: string;
7
7
  value: number;
@@ -9,7 +9,9 @@ export type BarChartItem = {
9
9
  /**
10
10
  * Colors the bar by what it means, in place of its sign's default (the
11
11
  * accent, or danger for a negative value): such as `'warning'` for an
12
- * overage and `'danger'` for a shortage. The color is a cue beside the
12
+ * overage, `'danger'` for a shortage and `'neutral'` (the muted grey)
13
+ * for a bar that is counted but not the point, such as the usual days
14
+ * beside the busiest ones. The color is a cue beside the
13
15
  * value and its label, never the only sign of the meaning. An unknown tone
14
16
  * throws a `RangeError`.
15
17
  */
@@ -33,6 +35,13 @@ export type BarChartProps = Omit<HTMLAttributes<HTMLDivElement>, 'children'> & {
33
35
  * alone tells a negative value.
34
36
  */
35
37
  diverging?: boolean;
38
+ /**
39
+ * `track` (the default) draws each bar in a track with the value axis
40
+ * under the bars. `bare` draws the bars alone, with no track and no
41
+ * axis, for bars whose values are all written beside them. An unknown
42
+ * appearance throws a `RangeError`.
43
+ */
44
+ appearance?: BarChartAppearance;
36
45
  };
37
46
  export declare const BarChart: import("react").ForwardRefExoticComponent<Omit<HTMLAttributes<HTMLDivElement>, "children"> & {
38
47
  'aria-label': string;
@@ -52,4 +61,11 @@ export declare const BarChart: import("react").ForwardRefExoticComponent<Omit<HT
52
61
  * alone tells a negative value.
53
62
  */
54
63
  diverging?: boolean;
64
+ /**
65
+ * `track` (the default) draws each bar in a track with the value axis
66
+ * under the bars. `bare` draws the bars alone, with no track and no
67
+ * axis, for bars whose values are all written beside them. An unknown
68
+ * appearance throws a `RangeError`.
69
+ */
70
+ appearance?: BarChartAppearance;
55
71
  } & import("react").RefAttributes<HTMLDivElement>>;
@@ -2,21 +2,28 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { barChartFillRatio, barChartScaleMax, formatBarChartValue, } from '@scalewing/tokens';
3
3
  import { forwardRef } from 'react';
4
4
  import { cx } from '../class-names.js';
5
- import { barChartTones } from '../css/css-bar-chart.js';
6
- export { barChartTones };
5
+ import { barChartAppearances, barChartTones, } from '../css/css-bar-chart.js';
6
+ export { barChartAppearances, barChartTones };
7
7
  /** The fill's classes: the sign's (which also shapes a diverging bar) and the tone's. */
8
8
  function barChartFillClassName(item) {
9
9
  if (item.tone !== undefined && !barChartTones.includes(item.tone))
10
10
  throw new RangeError(`BarChart item tone must be one of ${barChartTones.join(', ')}; received ${String(item.tone)}`);
11
11
  return cx('sw-bar-chart-fill', item.value < 0 && 'sw-bar-chart-fill-negative', item.tone && `sw-bar-chart-fill-${item.tone}`);
12
12
  }
13
- export const BarChart = forwardRef(function BarChart({ 'aria-label': ariaLabel, className, diverging = false, formatValue = formatBarChartValue, items, max, style, ...rest }, ref) {
13
+ export const BarChart = forwardRef(function BarChart({ 'aria-label': ariaLabel, appearance = 'track', className, diverging = false, formatValue = formatBarChartValue, items, max, style, ...rest }, ref) {
14
+ if (!barChartAppearances.includes(appearance))
15
+ throw new RangeError(`Unknown BarChart appearance: ${String(appearance)}`);
14
16
  const scaleMax = barChartScaleMax(items.map((item) => item.value), max);
15
- const axis = diverging ? [-scaleMax, 0, scaleMax] : [0, scaleMax];
16
- return (_jsxs("div", { ref: ref, className: cx('sw-bar-chart', diverging && 'sw-bar-chart-diverging', className), style: style, ...rest, children: [_jsx("ul", { "aria-label": ariaLabel, className: "sw-bar-chart-plot", children: items.map((item, index) => {
17
+ const bare = appearance === 'bare';
18
+ const axis = bare
19
+ ? []
20
+ : diverging
21
+ ? [-scaleMax, 0, scaleMax]
22
+ : [0, scaleMax];
23
+ return (_jsxs("div", { ref: ref, className: cx('sw-bar-chart', diverging && 'sw-bar-chart-diverging', bare && 'sw-bar-chart-bare', className), style: style, ...rest, children: [_jsx("ul", { "aria-label": ariaLabel, className: "sw-bar-chart-plot", children: items.map((item, index) => {
17
24
  const ratio = barChartFillRatio(item.value, scaleMax);
18
25
  return (_jsxs("li", { className: "sw-bar-chart-row", children: [_jsx("span", { className: "sw-bar-chart-label", children: item.label }), _jsx("span", { className: "sw-bar-chart-track", children: _jsx("span", { className: barChartFillClassName(item), style: {
19
26
  '--sw-bar-fill': ratio,
20
27
  } }) }), _jsx("span", { className: "sw-bar-chart-value", children: item.valueLabel ?? formatValue(item.value) })] }, `${item.label}-${index}`));
21
- }) }), _jsxs("div", { "aria-hidden": "true", className: "sw-bar-chart-axis", children: [_jsx("span", {}), _jsx("span", { className: "sw-bar-chart-axis-track", children: axis.map((value) => (_jsx("span", { children: formatValue(value) }, value))) }), _jsx("span", {})] })] }));
28
+ }) }), bare ? null : (_jsxs("div", { "aria-hidden": "true", className: "sw-bar-chart-axis", children: [_jsx("span", {}), _jsx("span", { className: "sw-bar-chart-axis-track", children: axis.map((value) => (_jsx("span", { children: formatValue(value) }, value))) }), _jsx("span", {})] }))] }));
22
29
  });
@@ -1,3 +1,4 @@
1
+ import { type Breakpoint } from '../css/breakpoints.js';
1
2
  import { type ButtonGroupAppearance, buttonGroupAppearances } from '../css/css-button-group-track.js';
2
3
  import { type BoxProps } from './Box.js';
3
4
  export type ButtonGroupJustify = 'start' | 'end' | 'between';
@@ -19,6 +20,12 @@ export type ButtonGroupProps = BoxProps & {
19
20
  * variant; `default` keeps each button's own fill and outline.
20
21
  */
21
22
  appearance?: ButtonGroupAppearance;
23
+ /**
24
+ * Below this breakpoint (an `attached` group only) the group fills its
25
+ * column and its buttons share the width equally, such as ‹, a period and
26
+ * › across a phone. From it up the group sizes to its buttons.
27
+ */
28
+ fullWidthBelow?: Breakpoint;
22
29
  };
23
30
  /**
24
31
  * The action row of a form or dialog: buttons on one line, wrapping when
@@ -27,4 +34,4 @@ export type ButtonGroupProps = BoxProps & {
27
34
  * (put the primary action last, as the row already does). It is a `group`,
28
35
  * so an `aria-label` names the row for assistive tech.
29
36
  */
30
- export declare function ButtonGroup({ appearance, attached, className, justify, ...rest }: ButtonGroupProps): import("react").JSX.Element;
37
+ export declare function ButtonGroup({ appearance, attached, className, fullWidthBelow, justify, ...rest }: ButtonGroupProps): import("react").JSX.Element;
@@ -1,5 +1,7 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { cx } from '../class-names.js';
3
+ import { breakpoints } from '../css/breakpoints.js';
4
+ import { buttonGroupFullWidthClass } from '../css/css-button-group-attached.js';
3
5
  import { buttonGroupAppearances, buttonGroupTrackClass, } from '../css/css-button-group-track.js';
4
6
  import { Box } from './Box.js';
5
7
  export { buttonGroupAppearances };
@@ -10,10 +12,17 @@ export { buttonGroupAppearances };
10
12
  * (put the primary action last, as the row already does). It is a `group`,
11
13
  * so an `aria-label` names the row for assistive tech.
12
14
  */
13
- export function ButtonGroup({ appearance = 'default', attached = false, className, justify = 'end', ...rest }) {
15
+ export function ButtonGroup({ appearance = 'default', attached = false, className, fullWidthBelow, justify = 'end', ...rest }) {
14
16
  if (!buttonGroupAppearances.includes(appearance))
15
17
  throw new RangeError(`Unknown ButtonGroup appearance: ${String(appearance)}`);
16
18
  if (appearance === 'track' && !attached)
17
19
  throw new RangeError('ButtonGroup appearance="track" needs attached');
18
- return (_jsx(Box, { className: cx('sw-button-group', `sw-button-group-${justify}`, attached && 'sw-button-group-attached', appearance === 'track' && buttonGroupTrackClass, className), role: "group", ...rest }));
20
+ if (fullWidthBelow !== undefined) {
21
+ if (!breakpoints.includes(fullWidthBelow))
22
+ throw new RangeError(`Unknown ButtonGroup fullWidthBelow: ${String(fullWidthBelow)}`);
23
+ // An unattached group already stacks full width below md.
24
+ if (!attached)
25
+ throw new RangeError('ButtonGroup fullWidthBelow needs attached');
26
+ }
27
+ return (_jsx(Box, { className: cx('sw-button-group', `sw-button-group-${justify}`, attached && 'sw-button-group-attached', appearance === 'track' && buttonGroupTrackClass, fullWidthBelow && buttonGroupFullWidthClass(fullWidthBelow), className), role: "group", ...rest }));
19
28
  }
@@ -1,7 +1,9 @@
1
1
  import { type WeekStart } from '../calendar-month.js';
2
2
  import { type DateFieldLabels } from '../date-field-labels.js';
3
+ import { type DateFieldEntry } from '../css/css-date-field-calendar-entry.js';
3
4
  import { type RequiredMark } from './FieldLabelText.js';
4
- export type { DateFieldLabels, WeekStart };
5
+ export type { DateFieldEntry, DateFieldLabels, WeekStart };
6
+ export { dateFieldEntries } from '../css/css-date-field-calendar-entry.js';
5
7
  export type DateFieldProps = {
6
8
  label: string;
7
9
  /** Calendar date `YYYY-MM-DD`, or '' for no date. */
@@ -37,5 +39,15 @@ export type DateFieldProps = {
37
39
  weekStartsOn?: WeekStart;
38
40
  /** The control's own words; English by default. */
39
41
  labels?: Partial<DateFieldLabels>;
42
+ /**
43
+ * `typed` (the default) is a text entry in the locale's order with a
44
+ * calendar button. `calendar` is one button showing the chosen day (or
45
+ * `labels.chooseDate`, such as "Choose a day", while empty) that opens
46
+ * the same calendar, for a form where a day is picked, never typed. It
47
+ * has no text to check, so validation and a required day stay with the
48
+ * consumer's `error`; while `required`, `labels.required` is said after
49
+ * its name.
50
+ */
51
+ entry?: DateFieldEntry;
40
52
  };
41
- export declare function DateField({ label, value, onChange, min, max, today, disabled, required, requiredMark, description, error, locale, weekStartsOn, labels, }: DateFieldProps): import("react").JSX.Element;
53
+ export declare function DateField({ label, value, onChange, min, max, today, disabled, required, requiredMark, description, error, locale, weekStartsOn, labels, entry: entryKind, }: DateFieldProps): import("react").JSX.Element;
@@ -6,16 +6,19 @@ import { resolveDateFieldLabels, } from '../date-field-labels.js';
6
6
  import { dateEntryPattern, dateEntryPlaceholder } from '../date-entry.js';
7
7
  import { dateEntryValidity } from '../date-entry-validity.js';
8
8
  import { assertDateBounds, isOutsideDateRange } from '../date-only.js';
9
+ import { assertDateFieldEntry, } from '../css/css-date-field-calendar-entry.js';
9
10
  import { FieldErrorRegion } from './FieldErrorRegion.js';
10
11
  import { assertRequiredMark, FieldLabelText, } from './FieldLabelText.js';
11
12
  import { CalendarDialog } from './date-field/CalendarDialog.js';
12
- import { DateFieldGlyph } from './date-field/DateFieldGlyph.js';
13
+ import { CalendarEntryButton } from './date-field/CalendarEntryButton.js';
14
+ import { TypedEntry } from './date-field/TypedEntry.js';
13
15
  import { useCalendarPopup } from './date-field/use-calendar-popup.js';
14
16
  import { useDateEntry } from './date-field/use-date-entry.js';
15
17
  import { useLangLocale } from './date-field/use-lang-locale.js';
16
- import { tabStop } from '../tab-stop.js';
17
- export function DateField({ label, value, onChange, min, max, today, disabled = false, required = false, requiredMark = 'visible', description, error, locale, weekStartsOn = 0, labels, }) {
18
+ export { dateFieldEntries } from '../css/css-date-field-calendar-entry.js';
19
+ export function DateField({ label, value, onChange, min, max, today, disabled = false, required = false, requiredMark = 'visible', description, error, locale, weekStartsOn = 0, labels, entry: entryKind = 'typed', }) {
18
20
  assertRequiredMark(requiredMark);
21
+ assertDateFieldEntry(entryKind);
19
22
  assertDateBounds(value, min, max);
20
23
  assertWeekStart(weekStartsOn);
21
24
  const words = resolveDateFieldLabels(labels);
@@ -45,7 +48,10 @@ export function DateField({ label, value, onChange, min, max, today, disabled =
45
48
  const descriptionId = useId();
46
49
  const entryErrorId = useId();
47
50
  const errorId = useId();
51
+ const requiredId = useId();
52
+ const calendarEntry = entryKind === 'calendar';
48
53
  const describedBy = [
54
+ calendarEntry && required ? requiredId : null,
49
55
  description ? descriptionId : null,
50
56
  entry.invalid ? entryErrorId : null,
51
57
  error ? errorId : null,
@@ -53,12 +59,9 @@ export function DateField({ label, value, onChange, min, max, today, disabled =
53
59
  .filter((id) => id !== null)
54
60
  .join(' ');
55
61
  const invalid = Boolean(error) || entry.invalid || isOutsideDateRange(value, min, max);
56
- return (_jsxs("div", { className: "sw-date-field", ref: rootRef, children: [_jsx("label", { className: "sw-date-field-label", htmlFor: inputId, id: labelId, children: _jsx(FieldLabelText, { label: label, required: required, requiredMark: requiredMark }) }), _jsxs("div", { className: "sw-date-field-control", ref: controlRef, children: [_jsx("input", { "aria-describedby": describedBy || undefined, "aria-invalid": invalid, autoComplete: "off", className: "sw-date-field-input", disabled: disabled, id: inputId, onBlur: entry.check, onChange: (event) => entry.change(event.currentTarget.value), onKeyDown: (event) => {
57
- if (event.key === 'Enter')
58
- entry.check();
59
- }, placeholder: dateEntryPlaceholder(pattern, {
60
- day: words.dayPlaceholder,
61
- month: words.monthPlaceholder,
62
- year: words.yearPlaceholder,
63
- }), 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: toggle, ref: buttonRef, tabIndex: tabStop, type: "button", children: [_jsx("span", { className: "sw-sr-only", children: words.chooseDate }), _jsx(DateFieldGlyph, { name: "calendar" })] })] }), description ? (_jsx("span", { className: "sw-date-field-description", id: descriptionId, children: description })) : null, _jsx(FieldErrorRegion, { className: "sw-date-field-error", id: entryErrorId, message: entry.invalid ? words.invalidEntry : undefined }), _jsx(FieldErrorRegion, { className: "sw-date-field-error", id: errorId, message: error }), shown ? (_jsx(CalendarDialog, { ...dialog, buttonRef: buttonRef, anchorRef: controlRef, labelId: labelId, labels: words, locale: resolvedLocale, max: max, min: min, required: required, value: value, weekStartsOn: weekStartsOn })) : null] }));
62
+ return (_jsxs("div", { className: "sw-date-field", ref: rootRef, children: [_jsx("label", { className: "sw-date-field-label", htmlFor: inputId, id: labelId, children: _jsx(FieldLabelText, { label: label, required: required, requiredMark: requiredMark }) }), calendarEntry ? (_jsxs("div", { className: "sw-date-field-control", ref: controlRef, children: [_jsx(CalendarEntryButton, { buttonRef: buttonRef, describedBy: describedBy || undefined, disabled: disabled, emptyText: words.chooseDate, id: inputId, invalid: invalid, labelId: labelId, locale: resolvedLocale, onPress: toggle, trigger: trigger, value: value }), required ? (_jsx("span", { hidden: true, id: requiredId, children: words.required })) : null] })) : (_jsx(TypedEntry, { buttonRef: buttonRef, controlRef: controlRef, describedBy: describedBy || undefined, disabled: disabled, entry: entry, id: inputId, inputRef: inputRef, invalid: invalid, labelId: labelId, onToggle: toggle, placeholder: dateEntryPlaceholder(pattern, {
63
+ day: words.dayPlaceholder,
64
+ month: words.monthPlaceholder,
65
+ year: words.yearPlaceholder,
66
+ }), required: required, toggleName: words.chooseDate, trigger: trigger })), description ? (_jsx("span", { className: "sw-date-field-description", id: descriptionId, children: description })) : null, _jsx(FieldErrorRegion, { className: "sw-date-field-error", id: entryErrorId, message: entry.invalid ? words.invalidEntry : undefined }), _jsx(FieldErrorRegion, { className: "sw-date-field-error", id: errorId, message: error }), shown ? (_jsx(CalendarDialog, { ...dialog, buttonRef: buttonRef, anchorRef: controlRef, labelId: labelId, labels: words, locale: resolvedLocale, max: max, min: min, required: required, value: value, weekStartsOn: weekStartsOn })) : null] }));
64
67
  }
@@ -0,0 +1,65 @@
1
+ import { type WeekStart } from '../calendar-month.js';
2
+ import { type DateRangePreset } from '../date-range-input.js';
3
+ import { type DateRangeButtonLabels } from '../date-range-labels.js';
4
+ import { type DateOnlyRange } from '../date-only.js';
5
+ import { type ButtonSize, type ButtonVariant } from './Button.js';
6
+ export type { DateRangeButtonLabels, DateRangePreset };
7
+ /** A span of calendar days `YYYY-MM-DD`, both ends included. */
8
+ export type DateRangeButtonValue = DateOnlyRange;
9
+ export type DateRangeButtonProps = {
10
+ /**
11
+ * What pressing it does, such as "Choose a period". The button's name is
12
+ * this label, `labels.nameSeparator`, then the range it shows; the
13
+ * popover and the sheet are named by it.
14
+ */
15
+ label: string;
16
+ /**
17
+ * The range shown, `{ start, end }` as `YYYY-MM-DD`, start not after end.
18
+ * A range outside `min`/`max` or longer than `maxDays` stays the value.
19
+ */
20
+ value: DateRangeButtonValue;
21
+ /** Receives a new range on Apply or a preset, never converted through UTC. */
22
+ onChange: (value: DateRangeButtonValue) => void;
23
+ /**
24
+ * Ranges chosen in one press, such as "Last 7 days": a list beside the
25
+ * months (a row above the month on a phone). A press applies its range
26
+ * and closes. Each must lie inside `min`/`max` and `maxDays`.
27
+ */
28
+ presets?: readonly DateRangePreset[];
29
+ min?: string;
30
+ /** The last day that can be picked, such as today. */
31
+ max?: string;
32
+ /**
33
+ * The longest range, in days with both ends counted: once a first day is
34
+ * pressed, the days after this many are disabled.
35
+ */
36
+ maxDays?: number;
37
+ /**
38
+ * The calendar's today `YYYY-MM-DD`, the day it marks. Defaults to the
39
+ * device's local date; pass the business's day when it keeps its own.
40
+ */
41
+ today?: string;
42
+ disabled?: boolean;
43
+ /** The `<button>` element's id. */
44
+ id?: string;
45
+ /**
46
+ * BCP 47 tag for the dates and the calendar's month and weekday names.
47
+ * Defaults to the nearest `lang` attribute, then en-US.
48
+ */
49
+ locale?: string;
50
+ /** First column of the calendar: 0 Sunday (default) or 1 Monday. */
51
+ weekStartsOn?: WeekStart;
52
+ /** The calendar's words; English by default. */
53
+ labels?: Partial<DateRangeButtonLabels>;
54
+ /** A Button size; a coarse pointer gets a 44 px target. */
55
+ size?: ButtonSize;
56
+ /** Default `neutral`, the quiet grey of a page's period controls. */
57
+ variant?: ButtonVariant;
58
+ };
59
+ /**
60
+ * A button showing a range of days that opens a calendar to pick a first
61
+ * and a last day: from `md` up a popover with the presets and two months,
62
+ * below it a bottom sheet with one month. Apply gives the range; Cancel,
63
+ * Escape and a press outside keep the value. The ref reaches the `<button>`.
64
+ */
65
+ export declare const DateRangeButton: import("react").ForwardRefExoticComponent<DateRangeButtonProps & import("react").RefAttributes<HTMLButtonElement>>;
@@ -0,0 +1,54 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { forwardRef, useCallback } from 'react';
4
+ import { breakpointQuery } from '../css/breakpoints.js';
5
+ import { latestPick, rangeDayCount, sameRange } from '../date-range-draft.js';
6
+ import { assertDateRangeInput, } from '../date-range-input.js';
7
+ import { resolveDateRangeButtonLabels, } from '../date-range-labels.js';
8
+ import { formatRangeLong, formatRangeShort } from '../date-range-words.js';
9
+ import { todayDateOnly } from '../date-only.js';
10
+ import { Button } from './Button.js';
11
+ import { DateFieldGlyph } from './date-field/DateFieldGlyph.js';
12
+ import { useLangLocale } from './date-field/use-lang-locale.js';
13
+ import { DateRangePopover } from './date-range/DateRangePopover.js';
14
+ import { DateRangeSheet } from './date-range/DateRangeSheet.js';
15
+ import { useRangePopup } from './date-range/use-range-popup.js';
16
+ import { useMediaQuery } from './use-media-query.js';
17
+ /** Sets a forwarded ref, callback or object, to the same node. */
18
+ function setForwardedRef(ref, node) {
19
+ if (typeof ref === 'function')
20
+ ref(node);
21
+ else if (ref)
22
+ ref.current = node;
23
+ }
24
+ /**
25
+ * A button showing a range of days that opens a calendar to pick a first
26
+ * and a last day: from `md` up a popover with the presets and two months,
27
+ * below it a bottom sheet with one month. Apply gives the range; Cancel,
28
+ * Escape and a press outside keep the value. The ref reaches the `<button>`.
29
+ */
30
+ export const DateRangeButton = forwardRef(function DateRangeButton({ label, value, onChange, presets = [], min, max, maxDays, today, disabled = false, id, locale, weekStartsOn = 0, labels, size = 'md', variant = 'neutral', }, ref) {
31
+ assertDateRangeInput({
32
+ label,
33
+ value,
34
+ presets,
35
+ min,
36
+ max,
37
+ maxDays,
38
+ today,
39
+ weekStartsOn,
40
+ });
41
+ const words = resolveDateRangeButtonLabels(labels);
42
+ const popup = useRangePopup(value, disabled, onChange);
43
+ const resolvedLocale = useLangLocale(locale, popup.buttonRef);
44
+ const wide = useMediaQuery(breakpointQuery('from', 'md'), true);
45
+ const { buttonRef } = popup;
46
+ const setButton = useCallback((node) => {
47
+ buttonRef.current = node;
48
+ setForwardedRef(ref, node);
49
+ }, [buttonRef, ref]);
50
+ const { range } = popup.draft;
51
+ const shown = formatRangeShort(value, resolvedLocale);
52
+ const Panel = wide ? DateRangePopover : DateRangeSheet;
53
+ return (_jsxs(_Fragment, { children: [_jsxs(Button, { "aria-controls": popup.shown ? popup.id : undefined, "aria-expanded": popup.shown, "aria-haspopup": "dialog", className: "sw-date-range-button", disabled: disabled, id: id, onPress: popup.toggle, ref: setButton, size: size, variant: variant, children: [_jsx("span", { className: "sw-sr-only", children: `${label}${words.nameSeparator}${shown}` }), _jsx(DateFieldGlyph, { name: "calendar" }), _jsx("span", { "aria-hidden": "true", children: shown })] }), popup.shown ? (_jsx(Panel, { buttonRef: buttonRef, count: words.dayCount(rangeDayCount(range)), draft: popup.draft, id: popup.id, label: label, labels: words, latest: latestPick(popup.draft, max, maxDays), line: formatRangeLong(range, resolvedLocale), locale: resolvedLocale, max: max, min: min, onApply: () => popup.apply(range), onCancel: popup.cancel, onChoosePreset: (preset) => popup.apply(preset.range), onPress: popup.press, presets: presets, pressedPresetId: presets.find((preset) => sameRange(preset.range, range))?.id, today: today ?? todayDateOnly(), weekStartsOn: weekStartsOn })) : null] }));
54
+ });
@@ -2,7 +2,8 @@ import { denominationGridFrames, denominationTotalEmphases } from '../css/css-de
2
2
  import { denominationCellToneAppearances } from '../css/css-denomination-cell-tones.js';
3
3
  import { denominationLabelAppearances } from '../css/css-denomination-label.js';
4
4
  import { type DenominationGridProps } from './denomination-grid/types.js';
5
- export type { DenominationCellToneAppearance, DenominationGridColumn, DenominationGridEntry, DenominationGridFrame, DenominationGridLayout, DenominationGridProps, DenominationGridRow, DenominationGridRowEmphasis, DenominationGridRowRole, DenominationGridTone, DenominationGridValueRow, DenominationLabelWidth, DenominationTotalEmphasis, } from './denomination-grid/types.js';
5
+ export type { DenominationCellToneAppearance, DenominationGridColumn, DenominationGridEntry, DenominationGridFrame, DenominationGridLayout, DenominationGridNegativeAppearance, DenominationGridProps, DenominationGridRow, DenominationGridRowEmphasis, DenominationGridRowRole, DenominationGridTone, DenominationGridValueRow, DenominationLabelWidth, DenominationTotalEmphasis, DenominationTransposeBreakpoint, } from './denomination-grid/types.js';
6
+ export { denominationNegativeAppearances, denominationTransposeBreakpoints, } from './denomination-grid/types.js';
6
7
  export type { DenominationLabelAppearance } from '../css/css-denomination-label.js';
7
8
  export { denominationCellToneAppearances, denominationGridFrames, denominationLabelAppearances, denominationTotalEmphases, };
8
- export declare function DenominationGrid({ label, columns, rows, layout, subtotal, zeroLabel, totalLabel, rowRole, labelWidth, labelAppearance, frame, totalEmphasis, cellToneAppearance, }: DenominationGridProps): import("react").JSX.Element;
9
+ export declare function DenominationGrid({ label, columns, rows, layout, subtotal, zeroLabel, totalLabel, rowRole, labelWidth, labelAppearance, frame, totalEmphasis, cellToneAppearance, transposeBelow, countDigits, }: DenominationGridProps): import("react").JSX.Element;
@@ -1,25 +1,33 @@
1
+ 'use client';
1
2
  import { jsx as _jsx } from "react/jsx-runtime";
2
3
  import { cx } from '../class-names.js';
4
+ import { breakpointQuery } from '../css/breakpoints.js';
5
+ import { denominationCountDigitsClass } from '../css/css-denomination-transposed.js';
3
6
  import { denominationLabelWidths } from '../css/css-denomination-aligned.js';
4
7
  import { denominationGridFrames, denominationTotalEmphases, denominationTotalsStrongClass, } from '../css/css-denomination-appearance.js';
5
8
  import { denominationCellToneAppearances, denominationCellTonesPillClass, } from '../css/css-denomination-cell-tones.js';
6
9
  import { denominationLabelAppearances, denominationLabelsPlainClass, } from '../css/css-denomination-label.js';
7
10
  import { DenominationStrip } from './denomination-grid/DenominationStrip.js';
8
11
  import { DenominationTiles } from './denomination-grid/DenominationTiles.js';
12
+ import { TransposedStrip } from './denomination-grid/TransposedStrip.js';
9
13
  import { assertDenominationGrid } from './denomination-grid/validate.js';
14
+ import { assertDenominationOptions } from './denomination-grid/validate-options.js';
10
15
  import { assertDenominationValueRows } from './denomination-grid/validate-value-row.js';
16
+ import { useMediaQuery } from './use-media-query.js';
11
17
  import { assertDenominationEntries, assertDenominationExtras, } from './denomination-grid/validate-entry.js';
18
+ export { denominationNegativeAppearances, denominationTransposeBreakpoints, } from './denomination-grid/types.js';
12
19
  export { denominationCellToneAppearances, denominationGridFrames, denominationLabelAppearances, denominationTotalEmphases, };
13
20
  /** Fails closed on a value outside a closed set, naming the prop. */
14
21
  function assertOneOf(values, value, prop) {
15
22
  if (!values.includes(value))
16
23
  throw new RangeError(`Unknown ${prop}: ${String(value)}`);
17
24
  }
18
- export function DenominationGrid({ label, columns, rows, layout = 'strip', subtotal, zeroLabel = '—', totalLabel, rowRole = 'region', labelWidth, labelAppearance = 'tone', frame = 'none', totalEmphasis = 'default', cellToneAppearance = 'text', }) {
25
+ export function DenominationGrid({ label, columns, rows, layout = 'strip', subtotal, zeroLabel = '—', totalLabel, rowRole = 'region', labelWidth, labelAppearance = 'tone', frame = 'none', totalEmphasis = 'default', cellToneAppearance = 'text', transposeBelow, countDigits, }) {
19
26
  assertDenominationGrid(label, columns, rows);
20
27
  assertDenominationExtras(layout, columns, rows);
21
28
  assertDenominationEntries(layout, columns, rows);
22
29
  assertDenominationValueRows(layout, columns, rows);
30
+ assertDenominationOptions(layout, rows, transposeBelow, countDigits);
23
31
  if (typeof zeroLabel !== 'string')
24
32
  throw new RangeError('zeroLabel must be a string');
25
33
  if (totalLabel !== undefined && !totalLabel.trim())
@@ -32,8 +40,18 @@ export function DenominationGrid({ label, columns, rows, layout = 'strip', subto
32
40
  assertOneOf(denominationGridFrames, frame, 'frame');
33
41
  assertOneOf(denominationTotalEmphases, totalEmphasis, 'totalEmphasis');
34
42
  assertOneOf(denominationCellToneAppearances, cellToneAppearance, 'cellToneAppearance');
35
- const appearanceClass = cx(labelAppearance === 'plain' && denominationLabelsPlainClass, totalEmphasis === 'strong' && denominationTotalsStrongClass, cellToneAppearance === 'pill' && denominationCellTonesPillClass);
43
+ const appearanceClass = cx(labelAppearance === 'plain' && denominationLabelsPlainClass, totalEmphasis === 'strong' && denominationTotalsStrongClass, cellToneAppearance === 'pill' && denominationCellTonesPillClass, countDigits !== undefined && denominationCountDigitsClass);
44
+ const countStyle = countDigits === undefined
45
+ ? undefined
46
+ : { '--sw-denomination-count-digits': countDigits };
47
+ // Read on every render (hooks run in order); without transposeBelow the
48
+ // query never matches.
49
+ const transposed = useMediaQuery(transposeBelow === undefined
50
+ ? 'not all'
51
+ : breakpointQuery('below', transposeBelow), false);
36
52
  if (layout === 'tiles')
37
53
  return (_jsx(DenominationTiles, { className: appearanceClass || undefined, columns: columns, label: label, rowRole: rowRole, rows: rows, subtotal: subtotal, zeroLabel: zeroLabel }));
38
- return (_jsx(DenominationStrip, { className: appearanceClass || undefined, columns: columns, framed: frame === 'table', label: label, labelWidth: labelWidth, rows: rows, totalLabel: totalLabel, zeroLabel: zeroLabel }));
54
+ if (transposeBelow !== undefined && transposed)
55
+ return (_jsx(TransposedStrip, { className: appearanceClass || undefined, columns: columns, label: label, rows: rows, style: countStyle, totalLabel: totalLabel, zeroLabel: zeroLabel }));
56
+ return (_jsx(DenominationStrip, { className: appearanceClass || undefined, countStyle: countStyle, columns: columns, framed: frame === 'table', label: label, labelWidth: labelWidth, rows: rows, totalLabel: totalLabel, zeroLabel: zeroLabel }));
39
57
  }
@@ -1,17 +1,11 @@
1
1
  import { type DialogHTMLAttributes, type ReactNode } from 'react';
2
2
  import { type Breakpoint } from '../css/breakpoints.js';
3
3
  import { type DialogAppearance, dialogAppearances } from '../css/css-dialog-banded.js';
4
+ import { type ThemeCanvas } from '../css/css-canvas-grouped.js';
4
5
  import { type ActionBarSummary } from './action-bar/ActionBarSummary.js';
6
+ import { type DialogRole, type DialogSize, dialogSizes } from './dialog/validate.js';
5
7
  export { dialogAppearances, type DialogAppearance };
6
- export declare const dialogSizes: readonly ["md", "lg", "xl"];
7
- /**
8
- * `md` is the reading width (32rem) for a message or a short form; `lg`
9
- * (56rem) holds a row of six fields or a data grid without folding it;
10
- * `xl` (65rem) holds a grid of six count columns with its labels and total.
11
- */
12
- export type DialogSize = (typeof dialogSizes)[number];
13
- /** `alertdialog` for a dialog that interrupts to warn, such as an idle sign-out. */
14
- export type DialogRole = 'dialog' | 'alertdialog';
8
+ export { dialogSizes, type DialogRole, type DialogSize };
15
9
  /**
16
10
  * The heading level of the dialog's title. It changes only the element (and
17
11
  * so the outline a screen reader reads), never the title's look.
@@ -44,11 +38,19 @@ export type DialogProps = Omit<DialogHTMLAttributes<HTMLDialogElement>, 'onClose
44
38
  * bottom edge at the full viewport width, its top corners rounded and its
45
39
  * bottom corners square, a strip of backdrop left in view above it, and
46
40
  * its bottom padding clearing `env(safe-area-inset-bottom)`. It slides up
47
- * on open unless reduced motion is asked for. There is no drag grabber
48
- * (and no drag): it closes as any dialog does. From the breakpoint up it
49
- * is the centered dialog, at its `size`.
41
+ * on open unless reduced motion is asked for. There is no drag: it
42
+ * closes as any dialog does. From the breakpoint up it is the centered
43
+ * dialog, at its `size`.
50
44
  */
51
45
  sheetBelow?: Breakpoint;
46
+ /**
47
+ * Draws a grabber, a short rounded bar centered at the top edge, while
48
+ * the dialog is a bottom sheet (below `sheetBelow`, which it needs). It
49
+ * only marks the sheet as one: it is hidden from assistive technology
50
+ * and does not drag. It stays at the top while the content scrolls.
51
+ * From the breakpoint up it is not drawn.
52
+ */
53
+ sheetGrabber?: boolean;
52
54
  /**
53
55
  * The accessible name of a close button, such as "Close". When given, an
54
56
  * icon-only ghost button ends the title row and asks `onClose`, like
@@ -74,6 +76,13 @@ export type DialogProps = Omit<DialogHTMLAttributes<HTMLDialogElement>, 'onClose
74
76
  * polite live region: "Drawer +$1,000.00 vault −$1,000.00".
75
77
  */
76
78
  summary?: ActionBarSummary;
79
+ /**
80
+ * One short line in the footer, after the summary and before the
81
+ * actions, in the same polite live region: plain words (muted, caption
82
+ * size), or a `Badge` and a short line in one `span`. Nothing
83
+ * (`undefined`, `null`, `false`, or blank words) leaves it out.
84
+ */
85
+ status?: ReactNode;
77
86
  /** `alertdialog` for a warning that interrupts. Default `dialog`. */
78
87
  role?: DialogRole;
79
88
  /**
@@ -82,6 +91,13 @@ export type DialogProps = Omit<DialogHTMLAttributes<HTMLDialogElement>, 'onClose
82
91
  * quiet round fill; `glass` (the default) is one frosted panel.
83
92
  */
84
93
  appearance?: DialogAppearance;
94
+ /**
95
+ * The dialog's fill. `grouped` puts its content on the grouped canvas
96
+ * (the quieter grey `ThemeProvider canvas="grouped"` paints), solid with
97
+ * no blur, so `Card`s inside stand apart; a banded title row and the
98
+ * footer stay on the surface. `default` keeps the appearance's fill.
99
+ */
100
+ canvas?: ThemeCanvas;
85
101
  children: ReactNode;
86
102
  };
87
- export declare function Dialog({ 'aria-describedby': describedBy, appearance, children, className, closeLabel, description, footer, icon, role, summary, onCancel, onClose, onKeyDown, onPointerDown, onSubmit, open, sheetBelow, size, title, titleLevel, ...rest }: DialogProps): import("react").JSX.Element;
103
+ export declare function Dialog({ 'aria-describedby': describedBy, appearance, canvas, children, className, closeLabel, description, footer, icon, role, summary, onCancel, onClose, onKeyDown, onPointerDown, onSubmit, open, sheetBelow, sheetGrabber, size, status, title, titleLevel, ...rest }: DialogProps): import("react").JSX.Element;
@@ -4,33 +4,35 @@ import { useId } from 'react';
4
4
  import { cx } from '../class-names.js';
5
5
  import { dialogSheetClass } from '../css/css-dialog.js';
6
6
  import { dialogAppearances, dialogBandedClass, } from '../css/css-dialog-banded.js';
7
+ import { dialogCanvasClass } from '../css/css-dialog-canvas.js';
8
+ import { dialogGrabberClass, dialogHasGrabberClass, } from '../css/css-dialog-grabber.js';
7
9
  import { spacingClassNames } from '../spacing-classes.js';
8
10
  import { useDialogCloseRequests } from './dialog-close-requests.js';
9
- import { assertActionBarSummary, } from './action-bar/ActionBarSummary.js';
10
11
  import { DialogFooter } from './dialog/DialogFooter.js';
11
12
  import { DialogTitle } from './dialog/DialogTitle.js';
13
+ import { assertDialogProps, dialogSizes, } from './dialog/validate.js';
12
14
  import { Stack } from './Stack.js';
13
15
  export { dialogAppearances };
14
- export const dialogSizes = ['md', 'lg', 'xl'];
15
- const dialogRoles = ['dialog', 'alertdialog'];
16
- function assertDialogProps(size, role, description, summary, appearance) {
17
- if (!dialogSizes.includes(size))
18
- throw new RangeError(`Unknown Dialog size: ${String(size)}`);
19
- if (!dialogAppearances.includes(appearance))
20
- throw new RangeError(`Unknown Dialog appearance: ${String(appearance)}`);
21
- if (role !== undefined && !dialogRoles.includes(role))
22
- throw new RangeError(`Unknown Dialog role: ${String(role)}`);
23
- if (description !== undefined &&
24
- (typeof description !== 'string' || !description.trim()))
25
- throw new RangeError('Dialog description must not be empty');
26
- if (summary)
27
- assertActionBarSummary(summary);
28
- }
29
- export function Dialog({ 'aria-describedby': describedBy, appearance = 'glass', children, className, closeLabel, description, footer, icon, role, summary, onCancel, onClose, onKeyDown, onPointerDown, onSubmit, open, sheetBelow, size = 'md', title, titleLevel = 3, ...rest }) {
30
- assertDialogProps(size, role, description, summary, appearance);
16
+ export { dialogSizes };
17
+ export function Dialog({ 'aria-describedby': describedBy, appearance = 'glass', canvas = 'default', children, className, closeLabel, description, footer, icon, role, summary, onCancel, onClose, onKeyDown, onPointerDown, onSubmit, open, sheetBelow, sheetGrabber = false, size = 'md', status, title, titleLevel = 3, ...rest }) {
18
+ assertDialogProps({
19
+ appearance,
20
+ canvas,
21
+ description,
22
+ role,
23
+ sheetBelow,
24
+ sheetGrabber,
25
+ size,
26
+ summary,
27
+ });
31
28
  const titleId = useId();
32
29
  const descriptionId = useId();
33
- const hasFooter = Boolean(footer) || Boolean(summary);
30
+ // Nothing, or blank words, leaves the line out, as in ActionBar.
31
+ const hasStatus = status !== undefined &&
32
+ status !== null &&
33
+ status !== false &&
34
+ !(typeof status === 'string' && !status.trim());
35
+ const hasFooter = Boolean(footer) || Boolean(summary) || hasStatus;
34
36
  const { nodeRef, handlers } = useDialogCloseRequests({
35
37
  open,
36
38
  onClose,
@@ -39,5 +41,5 @@ export function Dialog({ 'aria-describedby': describedBy, appearance = 'glass',
39
41
  onPointerDown,
40
42
  onSubmit,
41
43
  });
42
- return (_jsx("dialog", { ...rest, ref: nodeRef, "aria-describedby": describedBy ?? (description ? descriptionId : undefined), "aria-labelledby": titleId, className: cx('sw-dialog', size !== 'md' && `sw-dialog-${size}`, sheetBelow && dialogSheetClass(sheetBelow), appearance === 'banded' && dialogBandedClass, ...spacingClassNames({ padding: 5 }), className), role: role === 'alertdialog' ? 'alertdialog' : undefined, ...handlers, children: _jsxs(Stack, { gap: 4, children: [_jsx(DialogTitle, { banded: appearance === 'banded', closeLabel: closeLabel, description: description, descriptionId: descriptionId, icon: icon, id: titleId, level: titleLevel === 2 ? 2 : 3, onClose: onClose, title: title }), children, hasFooter ? (_jsx(DialogFooter, { summary: summary, children: footer })) : null] }) }));
44
+ return (_jsxs("dialog", { ...rest, ref: nodeRef, "aria-describedby": describedBy ?? (description ? descriptionId : undefined), "aria-labelledby": titleId, className: cx('sw-dialog', size !== 'md' && `sw-dialog-${size}`, sheetBelow && dialogSheetClass(sheetBelow), appearance === 'banded' && dialogBandedClass, canvas === 'grouped' && dialogCanvasClass, sheetGrabber && dialogHasGrabberClass, ...spacingClassNames({ padding: 5 }), className), role: role === 'alertdialog' ? 'alertdialog' : undefined, ...handlers, children: [sheetGrabber ? (_jsx("span", { "aria-hidden": "true", className: dialogGrabberClass })) : null, _jsxs(Stack, { gap: 4, children: [_jsx(DialogTitle, { banded: appearance === 'banded', closeLabel: closeLabel, description: description, descriptionId: descriptionId, icon: icon, id: titleId, level: titleLevel === 2 ? 2 : 3, onClose: onClose, title: title }), children, hasFooter ? (_jsx(DialogFooter, { status: hasStatus ? status : undefined, summary: summary, children: footer })) : null] })] }));
43
45
  }
@@ -1,9 +1,10 @@
1
1
  import { type SpacingStep } from '@scalewing/tokens';
2
2
  import { type ReactNode } from 'react';
3
3
  import { type RequiredMark } from './FieldLabelText.js';
4
+ import { type FieldWidth } from '../css/css-field-width.js';
5
+ import { fieldVariants, type FieldVariant } from './field/field-checks.js';
4
6
  export type FieldSize = 'xs' | 'md';
5
- export declare const fieldVariants: readonly ["outlined", "search"];
6
- export type FieldVariant = (typeof fieldVariants)[number];
7
+ export { fieldVariants, type FieldVariant };
7
8
  export type FieldProps = {
8
9
  children: ReactNode;
9
10
  gap?: SpacingStep;
@@ -60,5 +61,15 @@ export type FieldProps = {
60
61
  clearLabel?: string;
61
62
  /** Called by a search field's clear button; the consumer empties the value. */
62
63
  onClear?: () => void;
64
+ /**
65
+ * Caps the control's width at a Table column size (`xs` 4rem, `sm` 6rem,
66
+ * `md` 8rem, `lg` 12rem, `xl` 16rem), at the start of the column and
67
+ * never wider than it, so a short value such as a rate or a count does
68
+ * not stretch across a form. The label and messages keep the column's
69
+ * width. Left out, the control fills the column. It needs one native
70
+ * text input, select or textarea child (a `TypeError` otherwise); an
71
+ * unknown width throws a `RangeError`.
72
+ */
73
+ width?: FieldWidth;
63
74
  };
64
75
  export declare function Field(props: FieldProps): import("react").JSX.Element;