@12-apps/ui 2.1.0 → 3.1.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/components-catalog.md +8 -0
- package/package.json +2 -1
- package/src/components/data-display/Alert/Alert.types.ts +1 -1
- package/src/components/data-display/Avatar/Avatar.types.ts +1 -1
- package/src/components/data-display/Avatar/Avatar.view.tsx +5 -1
- package/src/components/data-display/Badge/Badge.styles.ts +11 -2
- package/src/components/data-display/Badge/Badge.types.ts +1 -1
- package/src/components/data-display/Carousel/Carousel.types.ts +4 -4
- package/src/components/data-display/Chart/Chart.types.ts +1 -1
- package/src/components/data-display/Chart/ChartSpec.types.ts +1 -1
- package/src/components/data-display/Chip/Chip.tsx +1 -1
- package/src/components/data-display/Chip/Chip.types.ts +6 -3
- package/src/components/data-display/DataViews/data-views-day-input.tsx +43 -22
- package/src/components/data-display/DateTimeDisplay/DateTimeDisplay.types.ts +1 -1
- package/src/components/data-display/InteractiveTooltip/InteractiveTooltip.types.ts +1 -1
- package/src/components/data-display/LoadingState/LoadingState.helpers.ts +4 -0
- package/src/components/data-display/LoadingState/LoadingState.types.ts +1 -1
- package/src/components/data-display/Progress/Progress.styles.ts +3 -0
- package/src/components/data-display/Progress/Progress.types.ts +2 -2
- package/src/components/data-display/Sheet/Sheet.types.ts +1 -1
- package/src/components/data-display/Stepper/Stepper.tsx +18 -6
- package/src/components/data-display/Stepper/Stepper.types.ts +2 -2
- package/src/components/data-display/Tooltip/Tooltip.types.ts +1 -1
- package/src/components/form/Button/Button.styles.ts +1 -0
- package/src/components/form/Command/Command.types.ts +1 -1
- package/src/components/form/CreatableSelect/CreatableSelect.tsx +5 -2
- package/src/components/form/CreatableSelect/CreatableSelect.types.ts +1 -1
- package/src/components/form/DateRangePicker/DateRangePicker.dates.ts +149 -0
- package/src/components/form/DateRangePicker/DateRangePicker.hooks.ts +87 -0
- package/src/components/form/DateRangePicker/DateRangePicker.md +228 -0
- package/src/components/form/DateRangePicker/DateRangePicker.quick.ts +97 -0
- package/src/components/form/DateRangePicker/DateRangePicker.range.ts +105 -0
- package/src/components/form/DateRangePicker/DateRangePicker.tsx +146 -0
- package/src/components/form/DateRangePicker/DateRangePicker.types.ts +193 -0
- package/src/components/form/DateRangePicker/DateRangePickerFields.tsx +90 -0
- package/src/components/form/DateRangePicker/DateRangePickerQuickList.tsx +123 -0
- package/src/components/form/DateRangePicker/index.ts +25 -0
- package/src/components/form/Input/Input.styles.ts +4 -0
- package/src/components/form/Input/Input.types.ts +1 -1
- package/src/components/form/InputOTP/InputOTP.styles.ts +1 -0
- package/src/components/form/InputOTP/InputOTP.types.ts +1 -1
- package/src/components/form/RadioGroup/RadioGroup.styles.ts +1 -0
- package/src/components/form/RadioGroup/RadioGroup.types.ts +1 -1
- package/src/components/form/Select/Select.types.ts +1 -1
- package/src/components/form/Slider/Slider.styles.ts +1 -0
- package/src/components/form/Slider/Slider.types.ts +1 -1
- package/src/components/form/Switch/Switch.styles.ts +1 -0
- package/src/components/form/Switch/Switch.types.ts +1 -1
- package/src/components/form/Textarea/Textarea.styles.ts +1 -0
- package/src/components/form/Textarea/Textarea.types.ts +1 -1
- package/src/components/form/Toggle/Toggle.styles.ts +1 -0
- package/src/components/form/Toggle/Toggle.types.ts +1 -1
- package/src/components/form/ToggleGroup/ToggleGroup.tsx +1 -0
- package/src/components/form/ToggleGroup/ToggleGroup.types.ts +1 -1
- package/src/components/form/index.ts +1 -0
- package/src/components/navigation/Breadcrumbs/Breadcrumbs.styles.tsx +6 -3
- package/src/components/navigation/Breadcrumbs/Breadcrumbs.types.ts +1 -1
- package/src/components/navigation/ContextMenu/ContextMenu.types.ts +1 -1
- package/src/components/navigation/DropdownMenu/DropdownMenu.types.ts +1 -1
- package/src/components/navigation/NavigationMenu/NavigationMenu.types.ts +1 -1
- package/src/components/navigation/Pagination/Pagination.types.ts +1 -1
- package/src/components/navigation/Tabs/Tabs.types.ts +1 -1
- package/src/components/typography/Blockquote/Blockquote.tsx +1 -0
- package/src/components/typography/Blockquote/Blockquote.types.ts +1 -1
- package/src/components/typography/Code/Code.types.ts +1 -1
- package/src/components/typography/Heading/Heading.styles.ts +23 -7
- package/src/components/typography/Heading/Heading.types.ts +1 -1
- package/src/components/typography/Paragraph/Paragraph.tsx +1 -0
- package/src/components/typography/Paragraph/Paragraph.types.ts +1 -1
- package/src/components/typography/Text/Text.tsx +11 -5
- package/src/components/typography/Text/Text.types.ts +1 -1
- package/src/components/utility/AnimatedIcon/AnimatedIcon.styles.ts +1 -0
- package/src/components/utility/AnimatedIcon/AnimatedIcon.types.ts +1 -1
- package/src/components/utility/WorkflowStep/WorkflowStep.types.ts +1 -1
- package/src/tokens/scales.ts +58 -7
package/components-catalog.md
CHANGED
|
@@ -311,6 +311,14 @@ Components for user input and form interactions.
|
|
|
311
311
|
**Location**: `src/components/form/Calendar`
|
|
312
312
|
**Docs**: `src/components/form/Calendar/Calendar.md`
|
|
313
313
|
|
|
314
|
+
### DateRangePicker
|
|
315
|
+
**Purpose**: Pick a range of days three ways over one value — calendar, quick ranges, typed dates
|
|
316
|
+
**Use Cases**: Report periods, analytics filters, booking windows, any from/to a reader edits
|
|
317
|
+
**Features**: Range `Calendar` reused (not forked), quick-range column (today, yesterday, this week, last 7/30/365 days, this month, this quarter, this year) with `aria-pressed`, masked `dd/mm/aaaa` fields, `timeZone`-aware "today", a `maxRangeDays` cap that disables over-cap options WITH the reason instead of clamping, overridable copy, stacks on narrow screens
|
|
318
|
+
**Types**: `DateRangeDraft`, `DayWindow`, `DateRangeStatus`, `QuickRange`
|
|
319
|
+
**Location**: `src/components/form/DateRangePicker`
|
|
320
|
+
**Docs**: `src/components/form/DateRangePicker/DateRangePicker.md`
|
|
321
|
+
|
|
314
322
|
### Checkbox
|
|
315
323
|
**Purpose**: Toggle selection checkbox
|
|
316
324
|
**Use Cases**: Multi-select, boolean options, agreements
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@12-apps/ui",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.1.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"packageManager": "pnpm@9.0.0",
|
|
6
6
|
"type": "module",
|
|
@@ -84,6 +84,7 @@
|
|
|
84
84
|
"./form/CodeEditor": "./src/components/form/CodeEditor/index.ts",
|
|
85
85
|
"./form/Command": "./src/components/form/Command/index.ts",
|
|
86
86
|
"./form/CreatableSelect": "./src/components/form/CreatableSelect/index.ts",
|
|
87
|
+
"./form/DateRangePicker": "./src/components/form/DateRangePicker/index.ts",
|
|
87
88
|
"./form/Form": "./src/components/form/Form/index.ts",
|
|
88
89
|
"./form/HeaderButton": "./src/components/form/HeaderButton/index.ts",
|
|
89
90
|
"./form/total-form": "./src/components/form/total-form/index.ts",
|
|
@@ -11,7 +11,7 @@ export interface AlertProps extends Omit<MuiAlertProps, 'variant' | 'color' | 'r
|
|
|
11
11
|
/**
|
|
12
12
|
* The color of the alert (when not using variant-specific colors)
|
|
13
13
|
*/
|
|
14
|
-
color?:
|
|
14
|
+
color?: ColorValue;
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
17
|
* Whether the alert should have a glow effect
|
|
@@ -57,7 +57,7 @@ export interface AvatarProps extends Omit<MuiAvatarProps, 'variant'> {
|
|
|
57
57
|
/**
|
|
58
58
|
* Color of the avatar background when no image is provided
|
|
59
59
|
*/
|
|
60
|
-
color?:
|
|
60
|
+
color?: ColorValue;
|
|
61
61
|
|
|
62
62
|
/**
|
|
63
63
|
* Whether the avatar is in a loading state
|
|
@@ -64,7 +64,11 @@ const getColorFromTheme = (theme: Theme, color: string): PaletteLike => {
|
|
|
64
64
|
secondary: theme.palette.secondary,
|
|
65
65
|
success: theme.palette.success,
|
|
66
66
|
warning: theme.palette.warning,
|
|
67
|
-
|
|
67
|
+
info: theme.palette.info,
|
|
68
|
+
// `danger`, not `error`. This map was keyed on MUI's word while the prop
|
|
69
|
+
// took the house one, so `danger` matched nothing and fell through to
|
|
70
|
+
// primary — a red avatar rendering blue, silently.
|
|
71
|
+
danger: theme.palette.error,
|
|
68
72
|
neutral: { main: theme.palette.grey[700], contrastText: '#fff' },
|
|
69
73
|
};
|
|
70
74
|
|
|
@@ -24,8 +24,10 @@ const getColorFromTheme = (theme: Theme, color: string) => {
|
|
|
24
24
|
secondary: theme.palette.secondary,
|
|
25
25
|
success: theme.palette.success,
|
|
26
26
|
warning: theme.palette.warning,
|
|
27
|
-
error: theme.palette.error,
|
|
28
27
|
info: theme.palette.info,
|
|
28
|
+
// `danger`, the house word the prop actually takes. Keyed on MUI's `error`
|
|
29
|
+
// it matched nothing and fell through to the default.
|
|
30
|
+
danger: theme.palette.error,
|
|
29
31
|
neutral: {
|
|
30
32
|
main: theme.palette.grey[600],
|
|
31
33
|
light: theme.palette.grey[400],
|
|
@@ -74,7 +76,14 @@ export const getSizeStyles = (size: BadgeSize) => {
|
|
|
74
76
|
fontSize: '0.875rem',
|
|
75
77
|
padding: '0 8px',
|
|
76
78
|
dotSize: 12,
|
|
77
|
-
iconSize: '1rem' }
|
|
79
|
+
iconSize: '1rem' },
|
|
80
|
+
xl: {
|
|
81
|
+
minWidth: 28,
|
|
82
|
+
height: 28,
|
|
83
|
+
fontSize: '1rem',
|
|
84
|
+
padding: '0 10px',
|
|
85
|
+
dotSize: 14,
|
|
86
|
+
iconSize: '1.125rem' } };
|
|
78
87
|
|
|
79
88
|
return sizeMap[size] || sizeMap.md;
|
|
80
89
|
};
|
|
@@ -14,7 +14,7 @@ export type BadgeVariant =
|
|
|
14
14
|
| 'success'
|
|
15
15
|
| 'warning';
|
|
16
16
|
|
|
17
|
-
export type BadgeSize =
|
|
17
|
+
export type BadgeSize = SizeValue;
|
|
18
18
|
|
|
19
19
|
export interface BadgeProps extends Omit<MuiBadgeProps, 'variant' | 'color' | 'content'> {
|
|
20
20
|
/**
|
|
@@ -14,7 +14,7 @@ export interface CarouselProps {
|
|
|
14
14
|
items: CarouselItem[];
|
|
15
15
|
variant?: 'default' | 'glass' | 'gradient' | 'elevated' | 'minimal' | 'cards';
|
|
16
16
|
size?: SizeValue;
|
|
17
|
-
color?:
|
|
17
|
+
color?: ColorValue;
|
|
18
18
|
autoPlay?: boolean;
|
|
19
19
|
autoPlayInterval?: number;
|
|
20
20
|
loop?: boolean;
|
|
@@ -51,7 +51,7 @@ export interface CarouselIndicatorsProps {
|
|
|
51
51
|
activeIndex: number;
|
|
52
52
|
onSelect: (index: number) => void;
|
|
53
53
|
position?: 'top' | 'bottom' | 'left' | 'right';
|
|
54
|
-
color?:
|
|
54
|
+
color?: ColorValue;
|
|
55
55
|
variant?: 'dots' | 'lines' | 'numbers';
|
|
56
56
|
className?: string;
|
|
57
57
|
style?: CSSProperties;
|
|
@@ -64,8 +64,8 @@ export interface CarouselArrowsProps {
|
|
|
64
64
|
disabled?: boolean;
|
|
65
65
|
disablePrev?: boolean;
|
|
66
66
|
disableNext?: boolean;
|
|
67
|
-
color?:
|
|
68
|
-
size?:
|
|
67
|
+
color?: ColorValue;
|
|
68
|
+
size?: SizeValue;
|
|
69
69
|
className?: string;
|
|
70
70
|
style?: CSSProperties;
|
|
71
71
|
}
|
|
@@ -28,7 +28,7 @@ export interface ChartProps {
|
|
|
28
28
|
type?: ChartType;
|
|
29
29
|
variant?: 'default' | 'glass' | 'gradient' | 'elevated' | 'minimal' | 'neon';
|
|
30
30
|
size?: SizeValue;
|
|
31
|
-
color?:
|
|
31
|
+
color?: ColorValue;
|
|
32
32
|
height?: number;
|
|
33
33
|
width?: number | string;
|
|
34
34
|
glow?: boolean;
|
|
@@ -18,7 +18,7 @@ export type ChartSpecType = 'line' | 'bar' | 'area' | 'pie' | 'donut';
|
|
|
18
18
|
* The house vocabulary minus `neutral`: a data series is always an accent, and
|
|
19
19
|
* an unaccented series would be indistinguishable from the grid it sits on.
|
|
20
20
|
*/
|
|
21
|
-
export type ChartSemanticColor =
|
|
21
|
+
export type ChartSemanticColor = ColorValue;
|
|
22
22
|
|
|
23
23
|
/**
|
|
24
24
|
* Number formatting applied to tooltip values and the y axis.
|
|
@@ -15,7 +15,7 @@ import type { ChipProps } from './Chip.types';
|
|
|
15
15
|
|
|
16
16
|
/** MUI draws two chip sizes; ours are the house abbreviations for them. */
|
|
17
17
|
const muiSizeFor = (size: NonNullable<ChipProps['size']>): 'small' | 'medium' =>
|
|
18
|
-
size === 'sm' ? 'small' : 'medium';
|
|
18
|
+
size === 'xs' || size === 'sm' ? 'small' : 'medium';
|
|
19
19
|
|
|
20
20
|
type MuiChipColor =
|
|
21
21
|
| 'primary'
|
|
@@ -13,10 +13,13 @@ export type ChipVariant = 'filled' | 'outlined';
|
|
|
13
13
|
* `ChipSize` nowhere and reached MUI as an unknown value, and the chip silently
|
|
14
14
|
* rendered at the default `medium`. Twelve of them are doing that today.
|
|
15
15
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
16
|
+
* All five stops, and `Chip.tsx` draws them: MUI gives a chip two heights, so
|
|
17
|
+
* `xs`/`sm` map to small and `md` and up to medium. Offering fewer than five
|
|
18
|
+
* here is what made callers write `size="sm"` against a type that did not have
|
|
19
|
+
* it — the scale is the vocabulary, and a component that speaks a dialect of it
|
|
20
|
+
* is the problem, not the solution.
|
|
18
21
|
*/
|
|
19
|
-
export type ChipSize =
|
|
22
|
+
export type ChipSize = SizeValue;
|
|
20
23
|
|
|
21
24
|
/**
|
|
22
25
|
* The colours a chip can draw — THE HOUSE VOCABULARY, identical to `ButtonProps`.
|
|
@@ -70,6 +70,36 @@ export function maskBrDate(raw: string): string {
|
|
|
70
70
|
return `${digits.slice(0, 2)}/${digits.slice(2, 4)}/${digits.slice(4)}`;
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
+
/**
|
|
74
|
+
* What has to land on the `<input>` itself rather than on the `TextField`.
|
|
75
|
+
*
|
|
76
|
+
* Anything `TextField` does not recognise is spread onto its root `FormControl`
|
|
77
|
+
* div. `inputMode` went up there once: the attribute sat on a non-editable
|
|
78
|
+
* wrapper, meant nothing, and the phone went on offering the letter keyboard
|
|
79
|
+
* for a field that only takes digits (the `dd/mm/aaaa` hint under a QWERTY
|
|
80
|
+
* layout). Only the `<input>` this reaches decides which keypad opens — and the
|
|
81
|
+
* same is true of `aria-describedby`, which on the wrapper points at a
|
|
82
|
+
* description no screen reader will read out for the field.
|
|
83
|
+
*
|
|
84
|
+
* `numeric` and not `tel`: both raise a keypad, but the phone one is a dialler
|
|
85
|
+
* with `+ * #` on it — keys that cannot occur in a date. A paste carrying
|
|
86
|
+
* separators still lands, because the MASK is what filters the text; the keypad
|
|
87
|
+
* only narrows what a thumb can reach for.
|
|
88
|
+
*
|
|
89
|
+
* Autofill off for the same reason: the browser's saved-value strip covers the
|
|
90
|
+
* keypad to suggest names and addresses, none of which are a date, and none of
|
|
91
|
+
* which the mask would keep.
|
|
92
|
+
*/
|
|
93
|
+
function dayInputProps(testId: string, describedBy: string | undefined) {
|
|
94
|
+
return {
|
|
95
|
+
'data-testid': testId,
|
|
96
|
+
maxLength: 10,
|
|
97
|
+
inputMode: 'numeric' as const,
|
|
98
|
+
autoComplete: 'off',
|
|
99
|
+
...(describedBy ? { 'aria-describedby': describedBy } : {}),
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
73
103
|
/**
|
|
74
104
|
* One end of a day window. The BOUND stays `AAAA-MM-DD` — the form the backend's
|
|
75
105
|
* inclusive day comparison already speaks, and the form the chip label reads —
|
|
@@ -80,12 +110,23 @@ export function DayBoundInput({
|
|
|
80
110
|
value,
|
|
81
111
|
onChange,
|
|
82
112
|
testId,
|
|
113
|
+
error = false,
|
|
114
|
+
describedBy,
|
|
83
115
|
}: {
|
|
84
116
|
label: string;
|
|
85
117
|
/** The applied bound, `AAAA-MM-DD`, or undefined when this end is open. */
|
|
86
118
|
value: string | undefined;
|
|
87
119
|
onChange: (bound: string | undefined) => void;
|
|
88
120
|
testId: string;
|
|
121
|
+
/**
|
|
122
|
+
* Paint the field as rejected. It only ever writes a real day by itself, so
|
|
123
|
+
* this is for a caller judging the PAIR: a window whose end precedes its
|
|
124
|
+
* start is two valid days and one impossible period, and the field it was
|
|
125
|
+
* typed into is the only place to say which half is being complained about.
|
|
126
|
+
*/
|
|
127
|
+
error?: boolean;
|
|
128
|
+
/** Id of the element carrying that complaint, so it is read WITH the field. */
|
|
129
|
+
describedBy?: string;
|
|
89
130
|
}): React.JSX.Element {
|
|
90
131
|
const applied = value ?? '';
|
|
91
132
|
const [text, setText] = useState(() => isoToBr(applied));
|
|
@@ -104,6 +145,7 @@ export function DayBoundInput({
|
|
|
104
145
|
size="small"
|
|
105
146
|
type="text"
|
|
106
147
|
label={label}
|
|
148
|
+
error={error}
|
|
107
149
|
// The mask IS the placeholder, so the expected order is on screen before
|
|
108
150
|
// the first keystroke rather than discovered by getting it wrong.
|
|
109
151
|
placeholder="dd/mm/aaaa"
|
|
@@ -124,28 +166,7 @@ export function DayBoundInput({
|
|
|
124
166
|
onBlur={() => setText(isoToBr(applied))}
|
|
125
167
|
// The placeholder occupies the space an un-shrunk label would render into.
|
|
126
168
|
InputLabelProps={{ shrink: true }}
|
|
127
|
-
|
|
128
|
-
// TextField does not recognise is spread onto its root `FormControl` div,
|
|
129
|
-
// and `inputMode` is not in that list — so passing it up there put the
|
|
130
|
-
// attribute on a non-editable wrapper, where it means nothing, and the
|
|
131
|
-
// phone went on offering the letter keyboard for a field that only takes
|
|
132
|
-
// digits (the `dd/mm/aaaa` hint sitting under a QWERTY layout). Only the
|
|
133
|
-
// `<input>` this reaches decides which keypad opens.
|
|
134
|
-
//
|
|
135
|
-
// `numeric` and not `tel`: both raise a keypad, but the phone one is a
|
|
136
|
-
// dialler with `+ * #` on it — keys that cannot occur in a date. A paste
|
|
137
|
-
// carrying separators still lands, because the MASK is what filters the
|
|
138
|
-
// text; the keypad only narrows what a thumb can reach for.
|
|
139
|
-
//
|
|
140
|
-
// Autofill off for the same reason: the browser's saved-value strip
|
|
141
|
-
// covers the keypad to suggest names and addresses, none of which are a
|
|
142
|
-
// date, and none of which the mask would keep.
|
|
143
|
-
inputProps={{
|
|
144
|
-
'data-testid': testId,
|
|
145
|
-
maxLength: 10,
|
|
146
|
-
inputMode: 'numeric',
|
|
147
|
-
autoComplete: 'off',
|
|
148
|
-
}}
|
|
169
|
+
inputProps={dayInputProps(testId, describedBy)}
|
|
149
170
|
sx={{ width: 165 }}
|
|
150
171
|
/>
|
|
151
172
|
);
|
|
@@ -4,15 +4,19 @@ export const SIZE_MAP: Record<
|
|
|
4
4
|
LoadingStateSize,
|
|
5
5
|
{ spinner: number; text: 'body2' | 'body1' | 'h6' }
|
|
6
6
|
> = {
|
|
7
|
+
xs: { spinner: 16, text: 'body2' },
|
|
7
8
|
sm: { spinner: 24, text: 'body2' },
|
|
8
9
|
md: { spinner: 40, text: 'body1' },
|
|
9
10
|
lg: { spinner: 56, text: 'h6' },
|
|
11
|
+
xl: { spinner: 64, text: 'h6' },
|
|
10
12
|
};
|
|
11
13
|
|
|
12
14
|
export const SKELETON_ROW_HEIGHT: Record<LoadingStateSize, number> = {
|
|
15
|
+
xs: 14,
|
|
13
16
|
sm: 20,
|
|
14
17
|
md: 28,
|
|
15
18
|
lg: 36,
|
|
19
|
+
xl: 44,
|
|
16
20
|
};
|
|
17
21
|
|
|
18
22
|
type LoadingStateDefaultedKeys = 'variant' | 'size' | 'skeletonRows';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { SizeValue } from '../../../tokens/scales';
|
|
2
2
|
export type LoadingStateVariant = 'spinner' | 'skeleton';
|
|
3
|
-
export type LoadingStateSize =
|
|
3
|
+
export type LoadingStateSize = SizeValue;
|
|
4
4
|
|
|
5
5
|
export interface LoadingStateProps {
|
|
6
6
|
/**
|
|
@@ -34,6 +34,7 @@ export const getColorFromTheme = (theme: Theme, color: string): PaletteColor =>
|
|
|
34
34
|
success: theme.palette.success,
|
|
35
35
|
warning: theme.palette.warning,
|
|
36
36
|
error: theme.palette.error,
|
|
37
|
+
info: theme.palette.info,
|
|
37
38
|
danger: theme.palette.error, // Add danger alias
|
|
38
39
|
};
|
|
39
40
|
|
|
@@ -42,9 +43,11 @@ export const getColorFromTheme = (theme: Theme, color: string): PaletteColor =>
|
|
|
42
43
|
};
|
|
43
44
|
|
|
44
45
|
const SIZE_MAP: Record<ProgressSize, { height: number; circularSize: number; fontSize: string }> = {
|
|
46
|
+
xs: { height: 2, circularSize: 24, fontSize: '0.625rem' },
|
|
45
47
|
sm: { height: 4, circularSize: 32, fontSize: '0.75rem' },
|
|
46
48
|
md: { height: 6, circularSize: 40, fontSize: '0.875rem' },
|
|
47
49
|
lg: { height: 8, circularSize: 48, fontSize: '1rem' },
|
|
50
|
+
xl: { height: 10, circularSize: 56, fontSize: '1.125rem' },
|
|
48
51
|
};
|
|
49
52
|
|
|
50
53
|
export const getSizeStyles = (size?: ProgressSize) => SIZE_MAP[size as ProgressSize] || SIZE_MAP.md;
|
|
@@ -2,7 +2,7 @@ import type { ColorValue, SizeValue } from '../../../tokens/scales';
|
|
|
2
2
|
import type { LinearProgressProps } from '@mui/material';
|
|
3
3
|
|
|
4
4
|
export type ProgressVariant = 'linear' | 'circular' | 'segmented' | 'gradient' | 'glass';
|
|
5
|
-
export type ProgressSize =
|
|
5
|
+
export type ProgressSize = SizeValue;
|
|
6
6
|
|
|
7
7
|
export interface ProgressProps extends Omit<LinearProgressProps, 'variant' | 'color'> {
|
|
8
8
|
/**
|
|
@@ -18,7 +18,7 @@ export interface ProgressProps extends Omit<LinearProgressProps, 'variant' | 'co
|
|
|
18
18
|
/**
|
|
19
19
|
* The color of the progress
|
|
20
20
|
*/
|
|
21
|
-
color?:
|
|
21
|
+
color?: ColorValue;
|
|
22
22
|
|
|
23
23
|
/**
|
|
24
24
|
* Whether the progress should have a glow effect
|
|
@@ -8,6 +8,18 @@ import {
|
|
|
8
8
|
import React from 'react';
|
|
9
9
|
|
|
10
10
|
import type { Step, StepItemProps, StepperProps } from './Stepper.types';
|
|
11
|
+
import type { SizeValue } from '../../../tokens/scales';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* The five house stops, drawn rather than collapsed.
|
|
15
|
+
*
|
|
16
|
+
* These were two-way ternaries on `sm`, which is what a two-value size prop
|
|
17
|
+
* allows; with the full scale they have to be real values or the three new
|
|
18
|
+
* stops would render identically to `md` and the prop would be a lie.
|
|
19
|
+
*/
|
|
20
|
+
const STEP_MIN_WIDTH: Record<SizeValue, number> = { xs: 44, sm: 56, md: 80, lg: 96, xl: 112 };
|
|
21
|
+
const STEP_ICON_SIZE: Record<SizeValue, number> = { xs: 14, sm: 18, md: 24, lg: 30, xl: 36 };
|
|
22
|
+
|
|
11
23
|
|
|
12
24
|
const StepperRoot = styled(Box, {
|
|
13
25
|
shouldForwardProp: (prop) => prop !== 'orientation',
|
|
@@ -37,12 +49,12 @@ const StepItem = styled(Box, {
|
|
|
37
49
|
}));
|
|
38
50
|
|
|
39
51
|
/** Circle diameter per size. */
|
|
40
|
-
const CIRCLE_SIZE = { sm: 32, md: 48
|
|
52
|
+
const CIRCLE_SIZE: Record<SizeValue, number> = { xs: 24, sm: 32, md: 48, lg: 60, xl: 72 };
|
|
41
53
|
|
|
42
54
|
const StepButton = styled(Button, {
|
|
43
55
|
shouldForwardProp: (prop) =>
|
|
44
56
|
!['isActive', 'isCompleted', 'clickable', 'stepSize'].includes(prop as string),
|
|
45
|
-
})<{ isActive: boolean; isCompleted: boolean; clickable: boolean; stepSize:
|
|
57
|
+
})<{ isActive: boolean; isCompleted: boolean; clickable: boolean; stepSize: SizeValue }>(
|
|
46
58
|
({ theme, isActive, isCompleted, clickable, stepSize }) => ({
|
|
47
59
|
minWidth: CIRCLE_SIZE[stepSize],
|
|
48
60
|
width: CIRCLE_SIZE[stepSize],
|
|
@@ -80,7 +92,7 @@ const StepButton = styled(Button, {
|
|
|
80
92
|
const StepConnector = styled(Box, {
|
|
81
93
|
shouldForwardProp: (prop) =>
|
|
82
94
|
!['orientation', 'isCompleted', 'size'].includes(prop as string),
|
|
83
|
-
})<{ orientation: 'horizontal' | 'vertical'; isCompleted: boolean; size:
|
|
95
|
+
})<{ orientation: 'horizontal' | 'vertical'; isCompleted: boolean; size: SizeValue }>(
|
|
84
96
|
({ theme, orientation, isCompleted, size }) => ({
|
|
85
97
|
backgroundColor: isCompleted ? theme.palette.primary.main : theme.palette.grey[300],
|
|
86
98
|
transition: theme.transitions.create('background-color', {
|
|
@@ -115,7 +127,7 @@ const StepLabel = styled(Box, {
|
|
|
115
127
|
marginTop: orientation === 'horizontal' ? 8 : 0,
|
|
116
128
|
}));
|
|
117
129
|
|
|
118
|
-
type StepSize =
|
|
130
|
+
type StepSize = SizeValue;
|
|
119
131
|
type LabelVariant = 'caption' | 'body2';
|
|
120
132
|
|
|
121
133
|
/** Space-joined state classes for a step (active / completed / optional). */
|
|
@@ -132,7 +144,7 @@ function stepClasses(isActive: boolean, isCompleted: boolean, optional?: boolean
|
|
|
132
144
|
/** The circle content-column width per orientation/size. */
|
|
133
145
|
function contentSx(orientation: 'horizontal' | 'vertical', size: StepSize) {
|
|
134
146
|
return orientation === 'horizontal'
|
|
135
|
-
? { minWidth: size
|
|
147
|
+
? { minWidth: STEP_MIN_WIDTH[size], maxWidth: 150, flex: '0 1 auto' }
|
|
136
148
|
: { flexShrink: 0 };
|
|
137
149
|
}
|
|
138
150
|
|
|
@@ -162,7 +174,7 @@ const StepCircle: React.FC<
|
|
|
162
174
|
data-testid={`stepper-step-icon-${index}`}
|
|
163
175
|
>
|
|
164
176
|
{isCompleted ? (
|
|
165
|
-
<CheckCircle sx={{ fontSize: size
|
|
177
|
+
<CheckCircle sx={{ fontSize: STEP_ICON_SIZE[size] }} />
|
|
166
178
|
) : (
|
|
167
179
|
<Typography variant={labelVariant} fontWeight="bold">
|
|
168
180
|
{index + 1}
|
|
@@ -41,7 +41,7 @@ export interface StepperProps {
|
|
|
41
41
|
* takes far less vertical space — useful above the fold on mobile checkouts.
|
|
42
42
|
* @default 'md'
|
|
43
43
|
*/
|
|
44
|
-
size?:
|
|
44
|
+
size?: SizeValue;
|
|
45
45
|
|
|
46
46
|
/**
|
|
47
47
|
* Navigation variant controlling step access
|
|
@@ -89,7 +89,7 @@ export interface StepItemProps {
|
|
|
89
89
|
isCompleted: boolean;
|
|
90
90
|
isLast: boolean;
|
|
91
91
|
orientation: 'horizontal' | 'vertical';
|
|
92
|
-
size:
|
|
92
|
+
size: SizeValue;
|
|
93
93
|
variant: 'linear' | 'non-linear';
|
|
94
94
|
clickable: boolean;
|
|
95
95
|
onStepClick?: (id: string) => void;
|
|
@@ -46,6 +46,7 @@ export const getColorFromTheme = (theme: Theme, color: string): ColorPalette =>
|
|
|
46
46
|
secondary: theme.palette.secondary as ColorPalette,
|
|
47
47
|
success: theme.palette.success as ColorPalette,
|
|
48
48
|
warning: theme.palette.warning as ColorPalette,
|
|
49
|
+
info: theme.palette.info as ColorPalette,
|
|
49
50
|
danger: theme.palette.error as ColorPalette,
|
|
50
51
|
};
|
|
51
52
|
|
|
@@ -23,7 +23,7 @@ export interface CommandProps {
|
|
|
23
23
|
onSelect?: (item: CommandItem) => void;
|
|
24
24
|
variant?: 'default' | 'glass' | 'gradient' | 'minimal' | 'elevated';
|
|
25
25
|
size?: SizeValue;
|
|
26
|
-
color?:
|
|
26
|
+
color?: ColorValue;
|
|
27
27
|
glow?: boolean;
|
|
28
28
|
pulse?: boolean;
|
|
29
29
|
loading?: boolean;
|
|
@@ -9,6 +9,7 @@ import { useId, useMemo } from 'react';
|
|
|
9
9
|
|
|
10
10
|
import { FormControl, FormLabel, FormMessage } from '../Form';
|
|
11
11
|
import type { CreatableSelectOption, CreatableSelectProps } from './CreatableSelect.types';
|
|
12
|
+
import type { SizeValue } from '../../../tokens/scales';
|
|
12
13
|
|
|
13
14
|
/** Internal option shape: a real option, or the synthetic "create new" row. */
|
|
14
15
|
interface InternalOption extends CreatableSelectOption {
|
|
@@ -25,7 +26,9 @@ const filterOptions = createFilterOptions<InternalOption>();
|
|
|
25
26
|
const INDENT_STEP = 16;
|
|
26
27
|
|
|
27
28
|
/** `sm`/`md` → MUI's own scale, the same mapping {@link Input} uses. */
|
|
28
|
-
|
|
29
|
+
// All five house stops onto MUI's two. `xs`/`sm` draw small; `md` and up draw
|
|
30
|
+
// medium, because an autocomplete field has no third or fourth height to give.
|
|
31
|
+
const MUI_SIZE = { xs: 'small', sm: 'small', md: 'medium', lg: 'medium', xl: 'medium' } as const;
|
|
29
32
|
|
|
30
33
|
/** Match the outlined border treatment of `Input` so siblings look identical. */
|
|
31
34
|
const fieldSx = {
|
|
@@ -92,7 +95,7 @@ function renderField(
|
|
|
92
95
|
placeholder?: string;
|
|
93
96
|
error?: string;
|
|
94
97
|
loading: boolean;
|
|
95
|
-
size:
|
|
98
|
+
size: SizeValue;
|
|
96
99
|
dataTestId: string;
|
|
97
100
|
},
|
|
98
101
|
): React.JSX.Element {
|
|
@@ -45,7 +45,7 @@ export interface CreatableSelectProps {
|
|
|
45
45
|
* Control height, matching {@link Input}'s scale so the two line up when they
|
|
46
46
|
* share a grid row (default: `md`).
|
|
47
47
|
*/
|
|
48
|
-
size?:
|
|
48
|
+
size?: SizeValue;
|
|
49
49
|
/** Base test id; the input is `<dataTestId>-input`. */
|
|
50
50
|
dataTestId?: string;
|
|
51
51
|
}
|