@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
|
@@ -207,6 +207,9 @@ export const breadcrumbLinkStyles = ({
|
|
|
207
207
|
padding: theme.spacing(0.375, 0.5) } });
|
|
208
208
|
|
|
209
209
|
export const sizeIconMap = {
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
210
|
+
xs: 'small',
|
|
211
|
+
sm: 'small',
|
|
212
|
+
md: 'small',
|
|
213
|
+
lg: 'medium',
|
|
214
|
+
xl: 'medium',
|
|
215
|
+
} as const;
|
|
@@ -106,7 +106,7 @@ export interface BreadcrumbsProps extends Omit<MuiBreadcrumbsProps, 'separator'>
|
|
|
106
106
|
* Size of the breadcrumbs
|
|
107
107
|
* @default 'md'
|
|
108
108
|
*/
|
|
109
|
-
size?:
|
|
109
|
+
size?: SizeValue;
|
|
110
110
|
|
|
111
111
|
/**
|
|
112
112
|
* Color scheme for separators and accents
|
|
@@ -1,24 +1,32 @@
|
|
|
1
1
|
import type { CSSObject, Theme } from '@mui/material';
|
|
2
2
|
|
|
3
3
|
import type { HeadingProps } from './Heading.types';
|
|
4
|
+
import type { ColorValue } from '../../../tokens/scales';
|
|
4
5
|
|
|
5
6
|
type Level = NonNullable<HeadingProps['level']>;
|
|
6
7
|
type Weight = NonNullable<HeadingProps['weight']>;
|
|
7
8
|
|
|
8
|
-
|
|
9
|
+
// Exhaustive over ColorValue, and deliberately without a fallback. The map
|
|
10
|
+
// happened to hold all seven already, but it was `Record<string, string>` with
|
|
11
|
+
// `|| text.primary` behind it — so nothing kept it that way, and the eighth
|
|
12
|
+
// colour would have rendered as ordinary body text with no complaint. That is
|
|
13
|
+
// the failure this whole vocabulary exists to make impossible; `Text` was
|
|
14
|
+
// tightened the same way and this one was missed.
|
|
15
|
+
const getColorFromTheme = (theme: Theme, color: ColorValue): string => {
|
|
9
16
|
if (color === 'neutral') {
|
|
10
17
|
return theme.palette.text.primary;
|
|
11
18
|
}
|
|
12
19
|
|
|
13
|
-
const colorMap: Record<
|
|
20
|
+
const colorMap: Record<Exclude<ColorValue, 'neutral'>, string> = {
|
|
14
21
|
primary: theme.palette.primary.main,
|
|
15
22
|
secondary: theme.palette.secondary.main,
|
|
16
23
|
success: theme.palette.success.main,
|
|
17
24
|
warning: theme.palette.warning.main,
|
|
25
|
+
info: theme.palette.info.main,
|
|
18
26
|
danger: theme.palette.error.main,
|
|
19
27
|
};
|
|
20
28
|
|
|
21
|
-
return colorMap[color]
|
|
29
|
+
return colorMap[color];
|
|
22
30
|
};
|
|
23
31
|
|
|
24
32
|
const WEIGHTS: Record<Weight, number> = {
|
|
@@ -50,22 +58,30 @@ const PRIMARY_STOPS = (theme: Theme): [string, string] => [
|
|
|
50
58
|
theme.palette.secondary.main,
|
|
51
59
|
];
|
|
52
60
|
|
|
53
|
-
|
|
61
|
+
// Keyed over the whole vocabulary, with no `?? PRIMARY_STOPS` behind it. Two
|
|
62
|
+
// stops were missing — `info` and `neutral` — so a gradient heading in either
|
|
63
|
+
// colour painted the PRIMARY gradient and looked deliberate while ignoring the
|
|
64
|
+
// prop entirely. The fallback is what hid it, so the fallback is gone.
|
|
65
|
+
const GRADIENT_STOPS: Record<ColorValue, (theme: Theme) => [string, string]> = {
|
|
54
66
|
primary: PRIMARY_STOPS,
|
|
55
67
|
secondary: (theme) => [theme.palette.secondary.main, theme.palette.primary.main],
|
|
56
68
|
success: (theme) => [theme.palette.success.light, theme.palette.success.dark],
|
|
57
69
|
warning: (theme) => [theme.palette.warning.light, theme.palette.warning.dark],
|
|
70
|
+
info: (theme) => [theme.palette.info.light, theme.palette.info.dark],
|
|
58
71
|
danger: (theme) => [theme.palette.error.light, theme.palette.error.dark],
|
|
72
|
+
// The grey ramp has no light/dark pair, and every stop on it is
|
|
73
|
+
// `string | undefined` under `noUncheckedIndexedAccess`.
|
|
74
|
+
neutral: (theme) => [theme.palette.grey[500] ?? '#9e9e9e', theme.palette.grey[900] ?? '#212121'],
|
|
59
75
|
};
|
|
60
76
|
|
|
61
|
-
const gradientFor = (theme: Theme, color:
|
|
62
|
-
const [from, to] =
|
|
77
|
+
const gradientFor = (theme: Theme, color: ColorValue) => {
|
|
78
|
+
const [from, to] = GRADIENT_STOPS[color](theme);
|
|
63
79
|
return `linear-gradient(135deg, ${from} 0%, ${to} 100%)`;
|
|
64
80
|
};
|
|
65
81
|
|
|
66
82
|
export interface HeadingFlags {
|
|
67
83
|
customLevel?: string;
|
|
68
|
-
customColor?:
|
|
84
|
+
customColor?: ColorValue;
|
|
69
85
|
customWeight?: string;
|
|
70
86
|
gradient?: boolean;
|
|
71
87
|
}
|
|
@@ -5,7 +5,7 @@ export type HeadingLevel = 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'display';
|
|
|
5
5
|
|
|
6
6
|
export interface HeadingProps extends React.HTMLAttributes<globalThis.HTMLHeadingElement> {
|
|
7
7
|
level?: HeadingLevel;
|
|
8
|
-
color?:
|
|
8
|
+
color?: ColorValue;
|
|
9
9
|
weight?: 'light' | 'normal' | 'medium' | 'semibold' | 'bold';
|
|
10
10
|
gradient?: boolean;
|
|
11
11
|
children: React.ReactNode;
|
|
@@ -15,6 +15,7 @@ const getColorFromTheme = (theme: Theme, color: string) => {
|
|
|
15
15
|
secondary: theme.palette.secondary.main,
|
|
16
16
|
success: theme.palette.success.main,
|
|
17
17
|
warning: theme.palette.warning.main,
|
|
18
|
+
info: theme.palette.info.main,
|
|
18
19
|
danger: theme.palette.error.main,
|
|
19
20
|
};
|
|
20
21
|
|
|
@@ -5,7 +5,7 @@ export type ParagraphVariant = 'default' | 'lead' | 'muted' | 'small';
|
|
|
5
5
|
|
|
6
6
|
export interface ParagraphProps extends React.HTMLAttributes<globalThis.HTMLParagraphElement> {
|
|
7
7
|
variant?: ParagraphVariant;
|
|
8
|
-
color?:
|
|
8
|
+
color?: ColorValue;
|
|
9
9
|
size?: SizeValue;
|
|
10
10
|
children: React.ReactNode;
|
|
11
11
|
}
|
|
@@ -6,8 +6,9 @@ import { styled } from '@mui/material/styles';
|
|
|
6
6
|
import React from 'react';
|
|
7
7
|
|
|
8
8
|
import type { TextProps } from './Text.types';
|
|
9
|
+
import type { ColorValue } from '../../../tokens/scales';
|
|
9
10
|
|
|
10
|
-
const getColorFromTheme = (theme: Theme, color:
|
|
11
|
+
const getColorFromTheme = (theme: Theme, color: ColorValue): string => {
|
|
11
12
|
if (color === 'neutral') {
|
|
12
13
|
return theme.palette.text.primary;
|
|
13
14
|
}
|
|
@@ -16,14 +17,19 @@ const getColorFromTheme = (theme: Theme, color: string) => {
|
|
|
16
17
|
return theme.palette.text.secondary;
|
|
17
18
|
}
|
|
18
19
|
|
|
19
|
-
|
|
20
|
+
// Exhaustive over ColorValue on purpose. It was a partial `Record<string, …>`
|
|
21
|
+
// with a silent `|| text.primary` fallback, so `info` — accepted by the type
|
|
22
|
+
// — rendered as ordinary body text. Typing the key means a colour added to
|
|
23
|
+
// the vocabulary cannot reach here without a home.
|
|
24
|
+
const colorMap: Record<Exclude<ColorValue, 'neutral' | 'secondary'>, string> = {
|
|
20
25
|
primary: theme.palette.primary.main,
|
|
21
26
|
success: theme.palette.success.main,
|
|
22
27
|
warning: theme.palette.warning.main,
|
|
28
|
+
info: theme.palette.info.main,
|
|
23
29
|
danger: theme.palette.error.main,
|
|
24
30
|
};
|
|
25
31
|
|
|
26
|
-
return colorMap[color]
|
|
32
|
+
return colorMap[color];
|
|
27
33
|
};
|
|
28
34
|
|
|
29
35
|
const SIZE_MAP: Record<string, { fontSize: string; lineHeight: number }> = {
|
|
@@ -45,7 +51,7 @@ const WEIGHT_MAP: Record<string, number> = {
|
|
|
45
51
|
interface TextStyleArgs {
|
|
46
52
|
theme: Theme;
|
|
47
53
|
customVariant?: string;
|
|
48
|
-
customColor?:
|
|
54
|
+
customColor?: ColorValue;
|
|
49
55
|
customSize?: string;
|
|
50
56
|
customWeight?: string;
|
|
51
57
|
italic?: boolean;
|
|
@@ -119,7 +125,7 @@ const StyledText = styled(Typography, {
|
|
|
119
125
|
].includes(prop as string),
|
|
120
126
|
})<{
|
|
121
127
|
customVariant?: string;
|
|
122
|
-
customColor?:
|
|
128
|
+
customColor?: ColorValue;
|
|
123
129
|
customSize?: string;
|
|
124
130
|
customWeight?: string;
|
|
125
131
|
italic?: boolean;
|
|
@@ -5,7 +5,7 @@ export type TextVariant = 'body' | 'heading' | 'caption' | 'code';
|
|
|
5
5
|
|
|
6
6
|
export interface TextProps extends React.HTMLAttributes<HTMLSpanElement> {
|
|
7
7
|
variant?: TextVariant;
|
|
8
|
-
color?:
|
|
8
|
+
color?: ColorValue;
|
|
9
9
|
size?: SizeValue;
|
|
10
10
|
weight?: 'light' | 'normal' | 'medium' | 'semibold' | 'bold';
|
|
11
11
|
as?: keyof React.JSX.IntrinsicElements;
|
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
import type { AnimationSize, AnimationVariant } from './AnimatedIcon.types';
|
|
25
25
|
|
|
26
26
|
export const sizeConfigs: Record<AnimationSize, { size: number; fontSize: number }> = {
|
|
27
|
+
xs: { size: 12, fontSize: 10 },
|
|
27
28
|
sm: { size: 24, fontSize: 20 },
|
|
28
29
|
md: { size: 32, fontSize: 28 },
|
|
29
30
|
lg: { size: 48, fontSize: 44 },
|
|
@@ -20,7 +20,7 @@ export type AnimationVariant =
|
|
|
20
20
|
| 'neonFlicker'
|
|
21
21
|
| 'breathe';
|
|
22
22
|
|
|
23
|
-
export type AnimationSize =
|
|
23
|
+
export type AnimationSize = SizeValue;
|
|
24
24
|
|
|
25
25
|
export type ShadowStyle = 'soft' | 'hard' | 'elevated' | 'none';
|
|
26
26
|
|
|
@@ -19,7 +19,7 @@ export type WorkflowStepOrientation = 'horizontal' | 'vertical';
|
|
|
19
19
|
/**
|
|
20
20
|
* Color theme for the workflow step component
|
|
21
21
|
*/
|
|
22
|
-
export type WorkflowStepColor =
|
|
22
|
+
export type WorkflowStepColor = ColorValue;
|
|
23
23
|
|
|
24
24
|
/**
|
|
25
25
|
* Size variant for step indicators
|
package/src/tokens/scales.ts
CHANGED
|
@@ -18,13 +18,13 @@ import type { Theme } from '@mui/material/styles';
|
|
|
18
18
|
*
|
|
19
19
|
* So the vocabulary is declared once, here, and components derive from it.
|
|
20
20
|
*
|
|
21
|
-
* DERIVE, DON'T COPY
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
21
|
+
* DERIVE, DON'T COPY — AND TAKE THE WHOLE VOCABULARY. Every size prop is
|
|
22
|
+
* `SizeValue` and every colour prop is `ColorValue`, with no `Extract` and no
|
|
23
|
+
* `Exclude`. Handing a component a slice was tried and undone: it is the same
|
|
24
|
+
* fragmentation wearing the canonical type's name, and it leaves a caller having
|
|
25
|
+
* to remember which components accept `xs` and which accept `info`. Where the
|
|
26
|
+
* underlying widget draws fewer steps than five, that is a rendering detail and
|
|
27
|
+
* belongs at the MUI boundary below — not in the prop's type.
|
|
28
28
|
*
|
|
29
29
|
* A component whose size or colour is not this concept at all keeps its own type
|
|
30
30
|
* — `Scrollbar`'s `thin|medium|thick` is a track width, `Sheet`'s `full` is a
|
|
@@ -37,9 +37,17 @@ import type { Theme } from '@mui/material/styles';
|
|
|
37
37
|
*
|
|
38
38
|
* Abbreviated, because that is what 26 of the 30 components with a real size
|
|
39
39
|
* already used and what every caller has been taught to write.
|
|
40
|
+
*
|
|
41
|
+
* Declared as the array and the type together so the two cannot disagree. The
|
|
42
|
+
* runtime list is what Storybook `argTypes.options` and the demo grids iterate;
|
|
43
|
+
* spelled by hand in each story it drifts, and it had — twelve colour lists
|
|
44
|
+
* omitted `info`, so the control that documents the vocabulary offered a
|
|
45
|
+
* component less than the component accepted.
|
|
40
46
|
*/
|
|
41
47
|
export type SizeValue = 'xs' | 'sm' | 'md' | 'lg' | 'xl';
|
|
42
48
|
|
|
49
|
+
export const SIZE_VALUES = ['xs', 'sm', 'md', 'lg', 'xl'] as const satisfies readonly SizeValue[];
|
|
50
|
+
|
|
43
51
|
/**
|
|
44
52
|
* The house colour vocabulary.
|
|
45
53
|
*
|
|
@@ -53,6 +61,8 @@ export type SizeValue = 'xs' | 'sm' | 'md' | 'lg' | 'xl';
|
|
|
53
61
|
* component calls its unaccented state). The translation must be a real mapping
|
|
54
62
|
* and never a cast: a cast is precisely what let `danger` through untouched and
|
|
55
63
|
* had MUI fall back to grey.
|
|
64
|
+
*
|
|
65
|
+
* Array and type declared together, for the reason given on {@link SIZE_VALUES}.
|
|
56
66
|
*/
|
|
57
67
|
export type ColorValue =
|
|
58
68
|
| 'primary'
|
|
@@ -63,6 +73,47 @@ export type ColorValue =
|
|
|
63
73
|
| 'danger'
|
|
64
74
|
| 'neutral';
|
|
65
75
|
|
|
76
|
+
export const COLOR_VALUES = [
|
|
77
|
+
'primary',
|
|
78
|
+
'secondary',
|
|
79
|
+
'success',
|
|
80
|
+
'warning',
|
|
81
|
+
'info',
|
|
82
|
+
'danger',
|
|
83
|
+
'neutral',
|
|
84
|
+
] as const satisfies readonly ColorValue[];
|
|
85
|
+
|
|
86
|
+
/* ── Array and union, pinned to each other ────────────────────────────────── */
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* The arrays above are written out rather than derived, because deriving the
|
|
90
|
+
* union from the array (`(typeof COLOR_VALUES)[number]`) costs the alias its
|
|
91
|
+
* name: TypeScript resolves the indexed access eagerly, and every error in the
|
|
92
|
+
* package stops saying `not assignable to type 'ColorValue'` and starts dumping
|
|
93
|
+
* seven string literals. In a package whose entire purpose is teaching this
|
|
94
|
+
* vocabulary, the name in the error message is worth keeping.
|
|
95
|
+
*
|
|
96
|
+
* Writing both means they could disagree, so both directions are checked here.
|
|
97
|
+
* `satisfies` above catches an entry that is not a member. These catch the other
|
|
98
|
+
* half — a member missing from the array — which `satisfies` cannot see and
|
|
99
|
+
* which is the direction that actually bit: twelve story control lists offered
|
|
100
|
+
* six colours for a vocabulary of seven, so `info` was undocumented everywhere.
|
|
101
|
+
*
|
|
102
|
+
* A gap makes the assignment below fail, and the error names the missing member.
|
|
103
|
+
*/
|
|
104
|
+
type Missing<Union extends string, Listed extends string> = Exclude<Union, Listed>;
|
|
105
|
+
|
|
106
|
+
const _allSizesListed: Missing<SizeValue, (typeof SIZE_VALUES)[number]> extends never
|
|
107
|
+
? true
|
|
108
|
+
: never = true;
|
|
109
|
+
|
|
110
|
+
const _allColorsListed: Missing<ColorValue, (typeof COLOR_VALUES)[number]> extends never
|
|
111
|
+
? true
|
|
112
|
+
: never = true;
|
|
113
|
+
|
|
114
|
+
void _allSizesListed;
|
|
115
|
+
void _allColorsListed;
|
|
116
|
+
|
|
66
117
|
/* ── The MUI boundary ─────────────────────────────────────────────────────── */
|
|
67
118
|
|
|
68
119
|
/** What MUI calls a size. Three entries against our five. */
|