@scalewing/react 1.39.0 → 1.41.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +33 -0
- package/README.md +255 -5
- package/dist/calendar-labels.d.ts +2 -0
- package/dist/calendar-labels.js +7 -0
- package/dist/calendar-month.d.ts +6 -0
- package/dist/calendar-month.js +15 -0
- package/dist/components/BarChart.d.ts +20 -4
- package/dist/components/BarChart.js +13 -6
- package/dist/components/ButtonGroup.d.ts +8 -1
- package/dist/components/ButtonGroup.js +11 -2
- package/dist/components/DateField.d.ts +14 -2
- package/dist/components/DateField.js +14 -11
- package/dist/components/DateRangeButton.d.ts +65 -0
- package/dist/components/DateRangeButton.js +54 -0
- package/dist/components/DenominationGrid.d.ts +3 -2
- package/dist/components/DenominationGrid.js +21 -3
- package/dist/components/Dialog.d.ts +29 -13
- package/dist/components/Dialog.js +22 -20
- package/dist/components/Field.d.ts +13 -2
- package/dist/components/Field.js +14 -38
- package/dist/components/FilterChips.d.ts +7 -37
- package/dist/components/FilterChips.js +32 -24
- package/dist/components/ListGroup.d.ts +33 -0
- package/dist/components/ListGroup.js +19 -0
- package/dist/components/ListRow.d.ts +60 -0
- package/dist/components/ListRow.js +60 -0
- package/dist/components/NavCount.d.ts +21 -0
- package/dist/components/NavCount.js +23 -0
- package/dist/components/SectionNav.js +1 -8
- package/dist/components/SegmentedControl.d.ts +17 -1
- package/dist/components/SegmentedControl.js +11 -3
- package/dist/components/StatTile.d.ts +11 -4
- package/dist/components/StatTile.js +11 -4
- package/dist/components/Switch.d.ts +16 -1
- package/dist/components/Switch.js +17 -6
- package/dist/components/action-bar/ActionBarSummary.d.ts +10 -2
- package/dist/components/action-bar/ActionBarSummary.js +11 -2
- package/dist/components/{date-field → calendar}/CalendarGrid.d.ts +17 -2
- package/dist/components/{date-field → calendar}/CalendarGrid.js +11 -3
- package/dist/components/calendar/CalendarPopover.d.ts +25 -0
- package/dist/components/calendar/CalendarPopover.js +22 -0
- package/dist/components/calendar/MonthStep.d.ts +16 -0
- package/dist/components/calendar/MonthStep.js +17 -0
- package/dist/components/calendar/use-grid-focus.d.ts +18 -0
- package/dist/components/calendar/use-grid-focus.js +35 -0
- package/dist/components/calendar/wrap-tab.d.ts +7 -0
- package/dist/components/calendar/wrap-tab.js +29 -0
- package/dist/components/date-field/CalendarDialog.js +9 -67
- package/dist/components/date-field/CalendarEntryButton.d.ts +24 -0
- package/dist/components/date-field/CalendarEntryButton.js +16 -0
- package/dist/components/date-field/CalendarHeader.js +2 -11
- package/dist/components/date-field/TypedEntry.d.ts +26 -0
- package/dist/components/date-field/TypedEntry.js +13 -0
- package/dist/components/date-range/DateRangePopover.d.ts +7 -0
- package/dist/components/date-range/DateRangePopover.js +17 -0
- package/dist/components/date-range/DateRangeSheet.d.ts +7 -0
- package/dist/components/date-range/DateRangeSheet.js +15 -0
- package/dist/components/date-range/RangeMonths.d.ts +28 -0
- package/dist/components/date-range/RangeMonths.js +50 -0
- package/dist/components/date-range/RangePresets.d.ts +16 -0
- package/dist/components/date-range/RangePresets.js +10 -0
- package/dist/components/date-range/RangeSummary.d.ts +15 -0
- package/dist/components/date-range/RangeSummary.js +11 -0
- package/dist/components/date-range/range-panel-props.d.ts +31 -0
- package/dist/components/date-range/range-panel-props.js +1 -0
- package/dist/components/date-range/use-range-popup.d.ts +21 -0
- package/dist/components/date-range/use-range-popup.js +53 -0
- package/dist/components/denomination-grid/CountCell.d.ts +16 -0
- package/dist/components/denomination-grid/CountCell.js +14 -0
- package/dist/components/denomination-grid/DenominationStrip.d.ts +4 -1
- package/dist/components/denomination-grid/DenominationStrip.js +2 -2
- package/dist/components/denomination-grid/EntryCell.d.ts +3 -1
- package/dist/components/denomination-grid/EntryCell.js +2 -2
- package/dist/components/denomination-grid/StripRow.js +1 -10
- package/dist/components/denomination-grid/TransposedStrip.d.ts +17 -0
- package/dist/components/denomination-grid/TransposedStrip.js +43 -0
- package/dist/components/denomination-grid/types.d.ts +31 -0
- package/dist/components/denomination-grid/types.js +7 -1
- package/dist/components/denomination-grid/validate-options.d.ts +7 -0
- package/dist/components/denomination-grid/validate-options.js +27 -0
- package/dist/components/dialog/DialogFooter.d.ts +6 -5
- package/dist/components/dialog/DialogFooter.js +6 -6
- package/dist/components/dialog/validate.d.ts +24 -0
- package/dist/components/dialog/validate.js +23 -0
- package/dist/components/field/field-checks.d.ts +34 -0
- package/dist/components/field/field-checks.js +42 -0
- package/dist/components/filter-chips/FilterChip.d.ts +16 -0
- package/dist/components/filter-chips/FilterChip.js +20 -0
- package/dist/components/filter-chips/options.d.ts +75 -0
- package/dist/components/filter-chips/options.js +11 -0
- package/dist/components/filter-chips/validate.d.ts +9 -0
- package/dist/components/filter-chips/validate.js +48 -0
- package/dist/components/link-press.d.ts +7 -0
- package/dist/components/link-press.js +13 -0
- package/dist/components/list/ListRowAccessoryMark.d.ts +5 -0
- package/dist/components/list/ListRowAccessoryMark.js +7 -0
- package/dist/components/list/ListRowContent.d.ts +22 -0
- package/dist/components/list/ListRowContent.js +11 -0
- package/dist/components/list/list-row-container.d.ts +5 -0
- package/dist/components/list/list-row-container.js +6 -0
- package/dist/components/use-media-query.d.ts +5 -0
- package/dist/components/use-media-query.js +18 -0
- package/dist/css/chart-tones.d.ts +2 -0
- package/dist/css/chart-tones.js +5 -1
- package/dist/css/chevron.d.ts +6 -0
- package/dist/css/chevron.js +14 -0
- package/dist/css/css-bar-chart.d.ts +17 -2
- package/dist/css/css-bar-chart.js +38 -7
- package/dist/css/css-button-group-attached.d.ts +3 -0
- package/dist/css/css-button-group-attached.js +26 -5
- package/dist/css/css-button-group-track.js +1 -1
- package/dist/css/css-canvas-grouped.d.ts +17 -0
- package/dist/css/css-canvas-grouped.js +40 -0
- package/dist/css/css-checkbox.js +2 -2
- package/dist/css/css-components.js +15 -0
- package/dist/css/css-count-pill.d.ts +2 -0
- package/dist/css/css-count-pill.js +54 -0
- package/dist/css/css-date-field-calendar-entry.d.ts +11 -0
- package/dist/css/css-date-field-calendar-entry.js +50 -0
- package/dist/css/css-date-field.js +7 -2
- package/dist/css/css-date-range-presets.d.ts +2 -0
- package/dist/css/css-date-range-presets.js +109 -0
- package/dist/css/css-date-range.d.ts +2 -0
- package/dist/css/css-date-range.js +197 -0
- package/dist/css/css-denomination-grid.js +4 -0
- package/dist/css/css-denomination-transposed.d.ts +4 -0
- package/dist/css/css-denomination-transposed.js +178 -0
- package/dist/css/css-dialog-canvas.d.ts +3 -0
- package/dist/css/css-dialog-canvas.js +20 -0
- package/dist/css/css-dialog-frame.js +19 -2
- package/dist/css/css-dialog-grabber.d.ts +4 -0
- package/dist/css/css-dialog-grabber.js +56 -0
- package/dist/css/css-dialog.js +3 -2
- package/dist/css/css-document.js +3 -0
- package/dist/css/css-field-width.d.ts +12 -0
- package/dist/css/css-field-width.js +49 -0
- package/dist/css/css-field.js +4 -0
- package/dist/css/css-filter-chips-layout.d.ts +2 -0
- package/dist/css/css-filter-chips-layout.js +42 -0
- package/dist/css/css-filter-chips.js +4 -0
- package/dist/css/css-list-row.d.ts +8 -0
- package/dist/css/css-list-row.js +209 -0
- package/dist/css/css-list.d.ts +3 -0
- package/dist/css/css-list.js +85 -0
- package/dist/css/css-scroll-region.d.ts +18 -0
- package/dist/css/css-scroll-region.js +41 -0
- package/dist/css/css-section-nav-filled.js +2 -25
- package/dist/css/css-segmented-sizing.d.ts +13 -0
- package/dist/css/css-segmented-sizing.js +66 -0
- package/dist/css/css-segmented.js +4 -0
- package/dist/css/css-stat-tile.d.ts +7 -0
- package/dist/css/css-stat-tile.js +16 -0
- package/dist/css/css-steps.js +2 -2
- package/dist/css/css-switch.d.ts +6 -0
- package/dist/css/css-switch.js +39 -0
- package/dist/css/css-table-bounded-edges.js +4 -27
- package/dist/css/range-band.d.ts +2 -0
- package/dist/css/range-band.js +7 -0
- package/dist/css/stacking.d.ts +1 -0
- package/dist/css/stacking.js +6 -3
- package/dist/css/stylesheet.js +10 -0
- package/dist/date-field-labels.d.ts +5 -0
- package/dist/date-field-labels.js +1 -0
- package/dist/date-range-draft.d.ts +26 -0
- package/dist/date-range-draft.js +36 -0
- package/dist/date-range-input.d.ts +26 -0
- package/dist/date-range-input.js +58 -0
- package/dist/date-range-labels.d.ts +22 -0
- package/dist/date-range-labels.js +26 -0
- package/dist/date-range-view.d.ts +12 -0
- package/dist/date-range-view.js +29 -0
- package/dist/date-range-words.d.ts +11 -0
- package/dist/date-range-words.js +40 -0
- package/dist/index.d.ts +16 -9
- package/dist/index.js +14 -7
- package/dist/list-options.d.ts +71 -0
- package/dist/list-options.js +84 -0
- package/dist/styles.css +863 -41
- package/dist/theme/ThemeProvider.d.ts +11 -1
- package/dist/theme/ThemeProvider.js +4 -2
- package/package.json +1 -1
|
@@ -0,0 +1,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,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,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).
|
package/dist/css/chart-tones.js
CHANGED
|
@@ -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: ${
|
|
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(' ');
|
package/dist/css/chevron.d.ts
CHANGED
|
@@ -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;
|
package/dist/css/chevron.js
CHANGED
|
@@ -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 =
|
|
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
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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:
|
|
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
|
|
47
|
+
${group} > .sw-button ~ .sw-button {
|
|
37
48
|
margin-inline-start: -1px;
|
|
38
49
|
}
|
|
39
50
|
|
|
40
|
-
${group} > .sw-button:first-
|
|
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-
|
|
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
|
|
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 [
|
|
89
|
+
return [
|
|
90
|
+
'sw-button-group-attached',
|
|
91
|
+
buttonGroupFullWidthClass('md'),
|
|
92
|
+
...buttonGroupTrackClassCatalog(),
|
|
93
|
+
];
|
|
73
94
|
}
|