@12-apps/ui 6.44.0 → 6.44.2
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/NATIVE.md +1 -1
- package/dist/_internal/components/data-display/DataViews/base-card.js +4 -3
- package/dist/_internal/components/data-display/DataViews/base-list-card-gutters.js +5 -3
- package/dist/_internal/components/data-display/DataViews/base-list-card.js +5 -4
- package/dist/_internal/components/data-display/LazyImage/LazyImage.js +7 -1
- package/dist/_internal/components/data-display/index.js +1 -1
- package/dist/_internal/components/form/Checkbox/Checkbox.js +4 -2
- package/dist/_internal/components/form/Checkbox/Checkbox.metrics.js +7 -3
- package/dist/_internal/components/form/index.js +2 -2
- package/dist/_internal/components/index.js +3 -3
- package/dist/{chunk-B2ZITMYG.js → chunk-26QYVCDK.js} +2 -2
- package/dist/{chunk-CEJTGG6J.js → chunk-CIBTYBBN.js} +2 -2
- package/dist/{chunk-FOM7OMJR.js → chunk-DDL6W7JS.js} +6 -3
- package/dist/chunk-DDL6W7JS.js.map +1 -0
- package/dist/{chunk-QWFAEUFV.js → chunk-EO6LCGGQ.js} +40 -16
- package/dist/chunk-EO6LCGGQ.js.map +1 -0
- package/dist/{chunk-KMC6WP66.js → chunk-KPZ53JY6.js} +2 -2
- package/dist/{chunk-TIQXDPIX.js → chunk-RIJGMRJK.js} +10 -1
- package/dist/chunk-RIJGMRJK.js.map +1 -0
- package/dist/data-display/DataViews.js +5 -5
- package/dist/data-display/LazyImage.js +7 -1
- package/dist/form/Checkbox.js +4 -2
- package/dist/native/form/Checkbox.js +9 -2
- package/dist/native/form/Checkbox.js.map +1 -1
- package/dist/types/components/data-display/LazyImage/LazyImage.d.ts.map +1 -1
- package/dist/types/components/data-display/LazyImage/LazyImage.types.d.ts +13 -0
- package/dist/types/components/data-display/LazyImage/LazyImage.types.d.ts.map +1 -1
- package/dist/types/components/form/Checkbox/Checkbox.base.d.ts +23 -3
- package/dist/types/components/form/Checkbox/Checkbox.base.d.ts.map +1 -1
- package/dist/types/components/form/Checkbox/Checkbox.d.ts.map +1 -1
- package/dist/types/components/form/Checkbox/Checkbox.metrics.d.ts +23 -8
- package/dist/types/components/form/Checkbox/Checkbox.metrics.d.ts.map +1 -1
- package/dist/types/components/form/Checkbox/Checkbox.types.d.ts +5 -2
- package/dist/types/components/form/Checkbox/Checkbox.types.d.ts.map +1 -1
- package/dist/types-native/components/form/Checkbox/Checkbox.base.d.ts +23 -3
- package/dist/types-native/components/form/Checkbox/Checkbox.base.d.ts.map +1 -1
- package/dist/types-native/components/form/Checkbox/Checkbox.metrics.d.ts +23 -8
- package/dist/types-native/components/form/Checkbox/Checkbox.metrics.d.ts.map +1 -1
- package/dist/types-native/components/form/Checkbox/Checkbox.native.d.ts +0 -1
- package/dist/types-native/components/form/Checkbox/Checkbox.native.d.ts.map +1 -1
- package/dist/types-native/components/form/Checkbox/Checkbox.types.native.d.ts +1 -5
- package/dist/types-native/components/form/Checkbox/Checkbox.types.native.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/data-display/LazyImage/LazyImage.md +23 -1
- package/src/components/data-display/LazyImage/LazyImage.tsx +80 -15
- package/src/components/data-display/LazyImage/LazyImage.types.ts +13 -0
- package/src/components/form/Checkbox/Checkbox.base.ts +25 -3
- package/src/components/form/Checkbox/Checkbox.md +1 -0
- package/src/components/form/Checkbox/Checkbox.metrics.ts +32 -4
- package/src/components/form/Checkbox/Checkbox.native.tsx +2 -1
- package/src/components/form/Checkbox/Checkbox.tsx +3 -0
- package/src/components/form/Checkbox/Checkbox.types.native.ts +1 -5
- package/src/components/form/Checkbox/Checkbox.types.ts +5 -2
- package/dist/chunk-FOM7OMJR.js.map +0 -1
- package/dist/chunk-QWFAEUFV.js.map +0 -1
- package/dist/chunk-TIQXDPIX.js.map +0 -1
- /package/dist/{chunk-B2ZITMYG.js.map → chunk-26QYVCDK.js.map} +0 -0
- /package/dist/{chunk-CEJTGG6J.js.map → chunk-CIBTYBBN.js.map} +0 -0
- /package/dist/{chunk-KMC6WP66.js.map → chunk-KPZ53JY6.js.map} +0 -0
|
@@ -8,6 +8,7 @@ import { imgPassThrough, resolveLazyImageProps, useLazyImage } from './LazyImage
|
|
|
8
8
|
import type { LazyImageProps } from './LazyImage.types';
|
|
9
9
|
import { sheen } from '../../../tokens/ink';
|
|
10
10
|
import { rem } from '../../../tokens/relative';
|
|
11
|
+
import { fieldHeight } from '../../../tokens/field-height';
|
|
11
12
|
|
|
12
13
|
const ImageContainer = styled(Box)(() => ({
|
|
13
14
|
position: 'relative',
|
|
@@ -100,6 +101,70 @@ const orDefault = (value: Length, fallback: number | string): number | string =>
|
|
|
100
101
|
*/
|
|
101
102
|
const emptyToUnset = (value: Length): Length => (value === '' ? undefined : value);
|
|
102
103
|
|
|
104
|
+
/** A length, already resolved to CSS — {@link innerLength} or {@link sxLength}. */
|
|
105
|
+
type LengthResolver = (theme: Theme, value: Length) => string | undefined;
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Whether a length is a PERCENTAGE (a numeric {@link isFraction} or a `%`
|
|
109
|
+
* string) — copying it onto the OTHER axis re-creates this ticket's own
|
|
110
|
+
* collapse either way (width%/height% measure different boxes; Chromium-confirmed).
|
|
111
|
+
*/
|
|
112
|
+
const isRelativeLength = (value: Length): boolean =>
|
|
113
|
+
isFraction(value) || (typeof value === 'string' && value.trim().endsWith('%'));
|
|
114
|
+
|
|
115
|
+
/** A resolved axis pair; `aspectRatio` is set when the OTHER axis comes from CSS, not a length. */
|
|
116
|
+
type AxisPair = { width: string | undefined; height: string | undefined; aspectRatio?: string };
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* The unset-axis DEFAULT for a PLACEHOLDER only (skeleton, `ReactNode`
|
|
120
|
+
* fallback) — never the real image/`metrics` (an unset width sizes it
|
|
121
|
+
* naturally; `brand-link.tsx`'s logo relies on that). Decision (FUT-2805): an
|
|
122
|
+
* unset axis borrows the set one (a square); neither set, both take the
|
|
123
|
+
* theme's field height; both set stays unchanged. `resolve` is `innerLength`
|
|
124
|
+
* (skeleton) or `sxLength` (the container). A RELATIVE single axis
|
|
125
|
+
* ({@link isRelativeLength}) cannot borrow the same way: it keeps its value
|
|
126
|
+
* and squares up via `aspectRatio: '1 / 1'`, the other axis explicitly
|
|
127
|
+
* `'auto'` (never `undefined` — `Skeleton`'s own default, a fixed `40`,
|
|
128
|
+
* would silently win otherwise).
|
|
129
|
+
*/
|
|
130
|
+
const placeholderAxis = (theme: Theme, width: Length, height: Length, resolve: LengthResolver): AxisPair => {
|
|
131
|
+
const w = emptyToUnset(width);
|
|
132
|
+
// `height` always arrives defaulted to the literal `'auto'` when unset
|
|
133
|
+
// (`LAZY_IMAGE_DEFAULTS`) — indistinguishable from an explicit `"auto"`,
|
|
134
|
+
// and no distinction is needed: both mean "no fixed height requested".
|
|
135
|
+
const h = height === 'auto' ? undefined : emptyToUnset(height);
|
|
136
|
+
|
|
137
|
+
if (w === undefined && h === undefined) {
|
|
138
|
+
const field = fieldHeight(theme);
|
|
139
|
+
return { width: field, height: field };
|
|
140
|
+
}
|
|
141
|
+
if (h === undefined) {
|
|
142
|
+
if (isRelativeLength(w)) return { width: resolve(theme, w), height: 'auto', aspectRatio: '1 / 1' };
|
|
143
|
+
const resolved = resolve(theme, w);
|
|
144
|
+
return { width: resolved, height: resolved };
|
|
145
|
+
}
|
|
146
|
+
if (w === undefined) {
|
|
147
|
+
if (isRelativeLength(h)) return { width: 'auto', height: resolve(theme, h), aspectRatio: '1 / 1' };
|
|
148
|
+
const resolved = resolve(theme, h);
|
|
149
|
+
return { width: resolved, height: resolved };
|
|
150
|
+
}
|
|
151
|
+
return { width: resolve(theme, w), height: resolve(theme, h) };
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* `ImageContainer`'s own axis: the ordinary `orDefault(...,'auto')` in every
|
|
156
|
+
* state, except a ReactNode `fallback` while showing — the one case where
|
|
157
|
+
* the container is the only thing that CAN fix its own collapse
|
|
158
|
+
* (`FallbackContainer` is `position: absolute`, never feeding shrink-to-fit).
|
|
159
|
+
*/
|
|
160
|
+
const containerAxisFor = (theme: Theme, props: ResolvedLazyImageProps, hasError: boolean): AxisPair => {
|
|
161
|
+
const { width, height, fallback } = props;
|
|
162
|
+
const needsPlaceholderSize = hasError && Boolean(fallback) && typeof fallback !== 'string';
|
|
163
|
+
return needsPlaceholderSize
|
|
164
|
+
? placeholderAxis(theme, width, height, sxLength)
|
|
165
|
+
: { width: sxLength(theme, orDefault(width, 'auto')), height: sxLength(theme, orDefault(height, 'auto')) };
|
|
166
|
+
};
|
|
167
|
+
|
|
103
168
|
/** The box the image occupies, as CSS, shared by the real image and every stand-in for it. */
|
|
104
169
|
interface BoxMetrics {
|
|
105
170
|
width?: string;
|
|
@@ -116,13 +181,18 @@ interface IndicatorProps {
|
|
|
116
181
|
|
|
117
182
|
const SkeletonIndicator: React.FC<IndicatorProps> = ({ props }) => {
|
|
118
183
|
const theme = useTheme();
|
|
184
|
+
// FUT-2805: replaces the old literal `'100%'`/`'auto'` fallbacks, which
|
|
185
|
+
// collapsed to 0 in a shrink-to-fit `ImageContainer` with nothing to
|
|
186
|
+
// resolve against.
|
|
187
|
+
const { width, height, aspectRatio } = placeholderAxis(theme, props.width, props.height, innerLength);
|
|
119
188
|
return (
|
|
120
189
|
<Skeleton
|
|
121
190
|
// FUT-2774 #5: `skeletonProps.variant` was typed but never read — the
|
|
122
191
|
// variant was hard-coded regardless of what a caller passed.
|
|
123
192
|
variant={props.skeletonProps.variant ?? 'rectangular'}
|
|
124
|
-
width={
|
|
125
|
-
height={
|
|
193
|
+
width={width}
|
|
194
|
+
height={height}
|
|
195
|
+
style={aspectRatio ? { aspectRatio } : undefined}
|
|
126
196
|
// FUT-2774 #5: `||` reads a caller's `false` the same as "unset", so
|
|
127
197
|
// `skeletonProps.animation={false}` could never turn the animation off.
|
|
128
198
|
animation={props.skeletonProps.animation ?? 'pulse'}
|
|
@@ -228,17 +298,9 @@ const ErrorFallback: React.FC<IndicatorProps> = ({ props, metrics }) => {
|
|
|
228
298
|
return (
|
|
229
299
|
<FallbackContainer
|
|
230
300
|
sx={{
|
|
231
|
-
// FUT-2774 #4 (see `emptyToUnset`)
|
|
232
|
-
//
|
|
233
|
-
//
|
|
234
|
-
// unresolved defect. `orDefault(height, 'auto')` is safe to share with
|
|
235
|
-
// `SkeletonIndicator` (unlike width's `'100%'`, deliberately NOT
|
|
236
|
-
// shared — see `emptyToUnset`): a PERCENTAGE height against an
|
|
237
|
-
// auto-height containing block computes to `auto` by spec regardless,
|
|
238
|
-
// so whether `sx.height` is omitted (falling through to
|
|
239
|
-
// `FallbackContainer`'s own `height: '100%'`) or set to the literal
|
|
240
|
-
// `'auto'` here, an unset-height container gives the same result
|
|
241
|
-
// either way — nothing for this fallback to protect against.
|
|
301
|
+
// FUT-2774 #4 (see `emptyToUnset`); the collapse is fixed by
|
|
302
|
+
// `LazyImage` giving `ImageContainer` ITSELF a size now
|
|
303
|
+
// (`containerAxisFor`) — this `position: absolute` box just fills it.
|
|
242
304
|
width: innerLength(theme, emptyToUnset(width)),
|
|
243
305
|
height: innerLength(theme, orDefault(height, 'auto')),
|
|
244
306
|
borderRadius: styleLength(theme, borderRadius),
|
|
@@ -285,13 +347,16 @@ export const LazyImage = React.memo<LazyImageProps>(function LazyImage(rawProps)
|
|
|
285
347
|
borderRadius: styleLength(theme, borderRadius),
|
|
286
348
|
};
|
|
287
349
|
|
|
350
|
+
const containerAxis = containerAxisFor(theme, props, state.hasError);
|
|
351
|
+
|
|
288
352
|
return (
|
|
289
353
|
<ImageContainer
|
|
290
354
|
ref={containerRef}
|
|
291
355
|
className={props.className}
|
|
292
356
|
sx={{
|
|
293
|
-
width:
|
|
294
|
-
height:
|
|
357
|
+
width: containerAxis.width,
|
|
358
|
+
height: containerAxis.height,
|
|
359
|
+
aspectRatio: containerAxis.aspectRatio,
|
|
295
360
|
// The same px length the image gets (`metrics`): in `sx` a bare number
|
|
296
361
|
// would be a multiple of `shape.borderRadius`, and this box clips (FUT-2656).
|
|
297
362
|
borderRadius: metrics.borderRadius,
|
|
@@ -20,6 +20,15 @@ export interface LazyImageProps extends React.ImgHTMLAttributes<HTMLImageElement
|
|
|
20
20
|
* container's parent, as in `sx`: `0.5` is `50%`. The container takes it and
|
|
21
21
|
* the image, its skeleton and its fallback fill the container.
|
|
22
22
|
* A string is any CSS length and passes through untouched.
|
|
23
|
+
*
|
|
24
|
+
* Left unset, the real (loaded) image sizes itself naturally — but the
|
|
25
|
+
* loading skeleton and a `ReactNode` `fallback` never leave BOTH axes
|
|
26
|
+
* unresolved: an unset axis borrows the one that IS set (a square
|
|
27
|
+
* placeholder) when it is a DEFINITE length; a RELATIVE one (a fraction, or
|
|
28
|
+
* a `%` string) squares up through CSS `aspectRatio` instead, since copying
|
|
29
|
+
* a percentage onto the other axis would measure against the wrong box.
|
|
30
|
+
* With neither set, both take the theme's field height (FUT-2805; see
|
|
31
|
+
* `LazyImage.md`'s own note for the exact rule).
|
|
23
32
|
*/
|
|
24
33
|
width?: number | string;
|
|
25
34
|
|
|
@@ -30,6 +39,10 @@ export interface LazyImageProps extends React.ImgHTMLAttributes<HTMLImageElement
|
|
|
30
39
|
* the image, its skeleton and its fallback fill the container. A fractional
|
|
31
40
|
* height takes effect only when the parent has a definite height.
|
|
32
41
|
* A string is any CSS length and passes through untouched.
|
|
42
|
+
*
|
|
43
|
+
* Left unset, the real (loaded) image sizes itself naturally (`'auto'`) —
|
|
44
|
+
* the loading skeleton and a `ReactNode` `fallback` follow `width`'s own
|
|
45
|
+
* note instead (FUT-2805).
|
|
33
46
|
*/
|
|
34
47
|
height?: number | string;
|
|
35
48
|
|
|
@@ -5,16 +5,28 @@
|
|
|
5
5
|
* and a native consumer has no `@mui/material` to resolve a type import
|
|
6
6
|
* against. See `Button.base.ts`.
|
|
7
7
|
*/
|
|
8
|
+
import type { SizeValue } from '../../../tokens/vocabulary';
|
|
9
|
+
|
|
8
10
|
export type CheckboxVariant = 'default' | 'rounded' | 'toggle';
|
|
9
11
|
|
|
12
|
+
/**
|
|
13
|
+
* MUI's own words for the glyph's size — `CHECKBOX_GLYPH_SIZES` in
|
|
14
|
+
* `Checkbox.metrics.ts` gives each one's pixel value.
|
|
15
|
+
*
|
|
16
|
+
* @deprecated `size` now also accepts `SizeValue` (`'xs' | 'sm' | 'md' | 'lg' |
|
|
17
|
+
* 'xl'`), the house vocabulary every other sized component in the package
|
|
18
|
+
* speaks — translated to this at the MUI boundary by `resolveCheckboxSize`
|
|
19
|
+
* (`Checkbox.metrics.ts`). These words keep working for one release; pass
|
|
20
|
+
* `SizeValue` instead. Removed in the next major version.
|
|
21
|
+
*/
|
|
22
|
+
export type CheckboxSize = 'small' | 'medium' | 'large';
|
|
23
|
+
|
|
10
24
|
/**
|
|
11
25
|
* The contract both renderers honour.
|
|
12
26
|
*
|
|
13
27
|
* `checked`, `defaultChecked` and `onChange` are NOT here: MUI types the change
|
|
14
28
|
* as a DOM `ChangeEvent` and the native side synthesises the
|
|
15
|
-
* `{ target: { checked } }` shape those handlers already read.
|
|
16
|
-
* `size`, which this component takes in MUI's own words (`small | medium`)
|
|
17
|
-
* rather than the house scale — narrowing it would reject the callers it has.
|
|
29
|
+
* `{ target: { checked } }` shape those handlers already read.
|
|
18
30
|
*/
|
|
19
31
|
export interface CheckboxBaseProps {
|
|
20
32
|
variant?: CheckboxVariant;
|
|
@@ -31,6 +43,16 @@ export interface CheckboxBaseProps {
|
|
|
31
43
|
/** The third state: neither checked nor unchecked, drawn as a dash. */
|
|
32
44
|
indeterminate?: boolean;
|
|
33
45
|
disabled?: boolean;
|
|
46
|
+
/**
|
|
47
|
+
* The size of the box and glyph.
|
|
48
|
+
*
|
|
49
|
+
* `SizeValue` (`'xs' | 'sm' | 'md' | 'lg' | 'xl'`) is the documented house
|
|
50
|
+
* vocabulary every other sized component in the package speaks, translated
|
|
51
|
+
* to MUI's own word at the MUI boundary (`resolveCheckboxSize`, in
|
|
52
|
+
* `Checkbox.metrics.ts`) — the same pattern `Chip`/`Button` already follow.
|
|
53
|
+
* MUI's own words (`CheckboxSize`) still work; see its `@deprecated` note.
|
|
54
|
+
*/
|
|
55
|
+
size?: SizeValue | CheckboxSize;
|
|
34
56
|
testID?: string;
|
|
35
57
|
dataTestId?: string;
|
|
36
58
|
}
|
|
@@ -55,6 +55,7 @@ import { Checkbox } from '@/components/form/Checkbox';
|
|
|
55
55
|
| glow | `boolean` | `false` | Enable glow animation |
|
|
56
56
|
| pulse | `boolean` | `false` | Enable pulse animation |
|
|
57
57
|
| disabled | `boolean` | `false` | Disable the checkbox |
|
|
58
|
+
| size | `'xs' \| 'sm' \| 'md' \| 'lg' \| 'xl'` | MUI's `'medium'` | The house size vocabulary, translated to MUI's own size at the boundary. MUI's own words (`'small' \| 'medium' \| 'large'`) still work but are **deprecated** — migrate to the house vocabulary; removed in the next major version. |
|
|
58
59
|
| data-testid | `string` | - | Custom test ID for testing |
|
|
59
60
|
|
|
60
61
|
Plus all standard MUI Checkbox props (checked, onChange, indeterminate, etc.)
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import { alpha } from '../../../tokens/color';
|
|
2
2
|
import { EFFECT_GLOW } from '../../../tokens/ink.core';
|
|
3
|
+
import { muiSize } from '../../../tokens/vocabulary';
|
|
4
|
+
import type { SizeValue } from '../../../tokens/vocabulary';
|
|
3
5
|
|
|
4
|
-
import type { CheckboxVariant } from './Checkbox.base';
|
|
6
|
+
import type { CheckboxSize, CheckboxVariant } from './Checkbox.base';
|
|
7
|
+
|
|
8
|
+
export type { CheckboxSize } from './Checkbox.base';
|
|
5
9
|
|
|
6
10
|
/**
|
|
7
11
|
* THE NUMBERS BOTH `Checkbox` RENDERERS DRAW WITH.
|
|
@@ -17,13 +21,37 @@ import type { CheckboxVariant } from './Checkbox.base';
|
|
|
17
21
|
/** MUI's `SwitchBase`: 9px of padding around the glyph, at every size. */
|
|
18
22
|
export const CHECKBOX_PADDING = 9;
|
|
19
23
|
|
|
20
|
-
/**
|
|
21
|
-
|
|
22
|
-
|
|
24
|
+
/**
|
|
25
|
+
* `SvgIcon`'s own three steps. MUI passes the `size` prop straight to the
|
|
26
|
+
* glyph. Typed against {@link CheckboxSize} (declared in `Checkbox.base.ts`,
|
|
27
|
+
* the contract file, since it is part of the public prop type) rather than the
|
|
28
|
+
* other way around, so a step added to one and not the other fails here.
|
|
29
|
+
*/
|
|
30
|
+
export const CHECKBOX_GLYPH_SIZES: Record<CheckboxSize, number> = { small: 20, medium: 24, large: 35 };
|
|
23
31
|
|
|
24
32
|
export const glyphSize = (size: CheckboxSize | undefined): number =>
|
|
25
33
|
CHECKBOX_GLYPH_SIZES[size ?? 'medium'] ?? CHECKBOX_GLYPH_SIZES.medium;
|
|
26
34
|
|
|
35
|
+
/**
|
|
36
|
+
* `size` at the MUI boundary.
|
|
37
|
+
*
|
|
38
|
+
* A `SizeValue` is translated via `muiSize()` — the pattern `Chip`/`Button`/
|
|
39
|
+
* `Carousel` already follow. MUI's own deprecated word (see
|
|
40
|
+
* {@link CheckboxSize}) is passed through UNCHANGED rather than re-derived,
|
|
41
|
+
* so a caller who has not migrated renders at exactly the size it always has —
|
|
42
|
+
* translating it through `muiSize()` too would not change any of the three
|
|
43
|
+
* words' own geometry, but passing it through untouched is the one route that
|
|
44
|
+
* cannot drift from what "unchanged" already means. `undefined` stays
|
|
45
|
+
* `undefined`, so a caller who names no size keeps MUI's own default.
|
|
46
|
+
*/
|
|
47
|
+
export const resolveCheckboxSize = (
|
|
48
|
+
size: SizeValue | CheckboxSize | undefined,
|
|
49
|
+
): CheckboxSize | undefined => {
|
|
50
|
+
if (size === undefined) return undefined;
|
|
51
|
+
if (size === 'small' || size === 'medium' || size === 'large') return size;
|
|
52
|
+
return muiSize(size);
|
|
53
|
+
};
|
|
54
|
+
|
|
27
55
|
/**
|
|
28
56
|
* The three glyphs MUI draws, by state.
|
|
29
57
|
*
|
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
CHECKBOX_VARIANT,
|
|
23
23
|
effectInk,
|
|
24
24
|
glyphSize,
|
|
25
|
+
resolveCheckboxSize,
|
|
25
26
|
} from './Checkbox.metrics';
|
|
26
27
|
import type { CheckboxProps } from './Checkbox.types.native';
|
|
27
28
|
import { Icon } from '../../../icons/Icon.native';
|
|
@@ -199,7 +200,7 @@ export const Checkbox = React.forwardRef<View, CheckboxProps>((rawProps, ref) =>
|
|
|
199
200
|
glow={glow}
|
|
200
201
|
ripple={ripple}
|
|
201
202
|
variant={variant}
|
|
202
|
-
size={glyphSize(size)}
|
|
203
|
+
size={glyphSize(resolveCheckboxSize(size))}
|
|
203
204
|
label={label}
|
|
204
205
|
onToggle={toggle}
|
|
205
206
|
rest={withoutTestIdProps(others) as Record<string, unknown>}
|
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
CHECKBOX_TRANSITION_MS,
|
|
18
18
|
CHECKBOX_VARIANT,
|
|
19
19
|
effectInk,
|
|
20
|
+
resolveCheckboxSize,
|
|
20
21
|
} from './Checkbox.metrics';
|
|
21
22
|
import type { CheckboxProps } from './Checkbox.types';
|
|
22
23
|
import { splitTestId } from '../../../platform/test-id';
|
|
@@ -126,6 +127,7 @@ export const Checkbox = React.forwardRef<HTMLButtonElement, CheckboxProps>(
|
|
|
126
127
|
glow,
|
|
127
128
|
pulse,
|
|
128
129
|
disabled,
|
|
130
|
+
size,
|
|
129
131
|
...others
|
|
130
132
|
} = resolveCheckboxProps(rawProps);
|
|
131
133
|
const theme = useTheme();
|
|
@@ -142,6 +144,7 @@ export const Checkbox = React.forwardRef<HTMLButtonElement, CheckboxProps>(
|
|
|
142
144
|
glow={glow}
|
|
143
145
|
pulse={pulse}
|
|
144
146
|
disabled={isDisabled}
|
|
147
|
+
size={resolveCheckboxSize(size)}
|
|
145
148
|
data-testid={dataTestId || 'checkbox'}
|
|
146
149
|
{...props}
|
|
147
150
|
/>
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import type { GestureResponderEvent, StyleProp, ViewProps, ViewStyle } from 'react-native';
|
|
2
2
|
|
|
3
3
|
import type { CheckboxBaseProps } from './Checkbox.base';
|
|
4
|
-
import type { CheckboxSize } from './Checkbox.metrics';
|
|
5
4
|
|
|
6
|
-
export type { CheckboxBaseProps, CheckboxVariant } from './Checkbox.base';
|
|
7
|
-
export type { CheckboxSize } from './Checkbox.metrics';
|
|
5
|
+
export type { CheckboxBaseProps, CheckboxSize, CheckboxVariant } from './Checkbox.base';
|
|
8
6
|
|
|
9
7
|
/**
|
|
10
8
|
* The change a native `Checkbox` reports: the `{ target: { checked } }` shape
|
|
@@ -19,8 +17,6 @@ export type CheckboxProps = CheckboxBaseProps &
|
|
|
19
17
|
Omit<ViewProps, keyof CheckboxBaseProps | 'style'> & {
|
|
20
18
|
checked?: boolean;
|
|
21
19
|
defaultChecked?: boolean;
|
|
22
|
-
/** MUI's own words for the glyph's size, which is what this component takes. */
|
|
23
|
-
size?: CheckboxSize;
|
|
24
20
|
onChange?: (event: CheckboxChangeEvent, checked: boolean) => void;
|
|
25
21
|
/** Both spellings fire, as `Button`'s do. */
|
|
26
22
|
onClick?: (event: GestureResponderEvent) => void;
|
|
@@ -3,12 +3,15 @@ import type { ChangeEvent, FocusEventHandler, MouseEventHandler } from 'react';
|
|
|
3
3
|
|
|
4
4
|
import type { CheckboxBaseProps } from './Checkbox.base';
|
|
5
5
|
|
|
6
|
-
export type { CheckboxBaseProps, CheckboxVariant } from './Checkbox.base';
|
|
6
|
+
export type { CheckboxBaseProps, CheckboxSize, CheckboxVariant } from './Checkbox.base';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* The web `Checkbox`: the shared contract, plus everything MUI's `Checkbox`
|
|
10
10
|
* accepts that the contract does not already name — `checked`,
|
|
11
|
-
* `defaultChecked`, `
|
|
11
|
+
* `defaultChecked`, `color`, `icon`, `inputProps`, `sx`. `size` IS already
|
|
12
|
+
* named by the contract (`CheckboxBaseProps`), which is why it is omitted
|
|
13
|
+
* from what flows through here — MUI's own `size` type would otherwise win
|
|
14
|
+
* over the house `SizeValue` the contract adds.
|
|
12
15
|
*/
|
|
13
16
|
export interface CheckboxProps
|
|
14
17
|
extends CheckboxBaseProps,
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/form/Checkbox/Checkbox.tsx"],"sourcesContent":["import MuiCheckbox from '@mui/material/Checkbox/index.js';\nimport CircularProgress from '@mui/material/CircularProgress/index.js';\nimport FormControlLabel from '@mui/material/FormControlLabel/index.js';\nimport FormHelperText from '@mui/material/FormHelperText/index.js';\nimport { alpha, keyframes, styled, useTheme } from '@mui/material/styles/index.js';\nimport type { Theme } from '@mui/material/styles/index.js';\nimport React from 'react';\n\nimport { makeTestId, resolveCheckboxProps } from './Checkbox.helpers';\nimport {\n CHECKBOX_GLOW,\n CHECKBOX_HELPER,\n CHECKBOX_HOVER_ALPHA,\n CHECKBOX_LABEL,\n CHECKBOX_PULSE,\n CHECKBOX_SPINNER,\n CHECKBOX_TRANSITION_MS,\n CHECKBOX_VARIANT,\n effectInk,\n} from './Checkbox.metrics';\nimport type { CheckboxProps } from './Checkbox.types';\nimport { splitTestId } from '../../../platform/test-id';\nimport { rem, sxRem } from '../../../tokens/relative';\n\nconst pulse = (theme: Theme) => keyframes`\n 0% {\n box-shadow: 0 0 0 0 ${effectInk(CHECKBOX_PULSE.alpha)};\n }\n 70% {\n box-shadow: 0 0 0 ${rem(theme, CHECKBOX_PULSE.spread)} ${effectInk(0)};\n }\n 100% {\n box-shadow: 0 0 0 0 ${effectInk(0)};\n }\n`;\n\nconst glow = (theme: Theme) => keyframes`\n 0%, 100% {\n box-shadow: 0 0 ${rem(theme, CHECKBOX_GLOW.blur.from)} ${effectInk(CHECKBOX_GLOW.alpha.from)};\n }\n 50% {\n box-shadow: 0 0 ${rem(theme, CHECKBOX_GLOW.blur.to)} ${effectInk(CHECKBOX_GLOW.alpha.to)};\n }\n`;\n\n/** A variant's corner: a design-px number through the type scale, a percentage as it is. */\nconst cornerOf = (theme: Theme, radius: number | string | undefined): string | undefined =>\n typeof radius === 'number' ? rem(theme, radius) : radius;\n\nconst StyledCheckbox = styled(MuiCheckbox, {\n shouldForwardProp: (prop) => !['customVariant', 'ripple', 'glow', 'pulse'].includes(prop as string),\n})<{ \n customVariant?: CheckboxProps['variant'];\n ripple?: boolean;\n glow?: boolean;\n pulse?: boolean;\n}>(\n ({ theme, customVariant, ripple, glow: glowProp, pulse: pulseProp }) => ({\n ...(customVariant === 'rounded' && {\n '& .MuiSvgIcon-root': {\n borderRadius: cornerOf(theme, CHECKBOX_VARIANT.rounded.radius),\n },\n }),\n \n ...(customVariant === 'toggle' && {\n '& .MuiSvgIcon-root': {\n borderRadius: cornerOf(theme, CHECKBOX_VARIANT.toggle.radius),\n transform: `scale(${CHECKBOX_VARIANT.toggle.scale})`,\n },\n }),\n \n '&.MuiCheckbox-root': {\n transition: `all ${CHECKBOX_TRANSITION_MS / 1000}s ease`,\n ...(ripple === false && {\n '& .MuiTouchRipple-root': {\n display: 'none',\n },\n }),\n ...(glowProp && {\n animation: `${glow(theme)} ${CHECKBOX_GLOW.ms / 1000}s ease-in-out infinite`,\n }),\n ...(pulseProp && {\n animation: `${pulse(theme)} ${CHECKBOX_PULSE.ms / 1000}s infinite`,\n }),\n '&:hover': {\n backgroundColor: alpha(theme.palette.primary.main, CHECKBOX_HOVER_ALPHA),\n },\n },\n \n '&.Mui-checked': {\n color: theme.palette.primary.main,\n },\n \n '&.MuiCheckbox-indeterminate': {\n color: theme.palette.primary.main,\n },\n })\n);\n\nconst StyledFormControlLabel = styled(FormControlLabel, {\n shouldForwardProp: (prop) => prop !== 'error',\n})<{ error?: boolean }>(\n ({ theme, error }) => ({\n marginLeft: rem(theme, CHECKBOX_LABEL.marginLeft),\n '& .MuiFormControlLabel-label': {\n color: error ? theme.palette.error.main : theme.palette.text.primary,\n fontSize: rem(theme, CHECKBOX_LABEL.fontSize),\n },\n })\n);\n\nconst StyledFormHelperText = styled(FormHelperText)(({ theme }) => ({\n marginLeft: theme.spacing(CHECKBOX_HELPER.marginLeftUnits),\n marginTop: theme.spacing(CHECKBOX_HELPER.marginTopUnits),\n}));\n\nexport const Checkbox = React.forwardRef<HTMLButtonElement, CheckboxProps>(\n (rawProps, ref) => {\n const {\n variant,\n label,\n error,\n helperText,\n loading,\n ripple,\n glow,\n pulse,\n disabled,\n ...others\n } = resolveCheckboxProps(rawProps);\n const theme = useTheme();\n const isDisabled = disabled || loading;\n const { testId: dataTestId, rest: props } = splitTestId(others);\n const testId = makeTestId(dataTestId);\n\n const checkbox = (\n <div style={{ position: 'relative', display: 'inline-flex' }}>\n <StyledCheckbox\n ref={ref}\n customVariant={variant}\n ripple={ripple}\n glow={glow}\n pulse={pulse}\n disabled={isDisabled}\n data-testid={dataTestId || 'checkbox'}\n {...props}\n />\n {loading && (\n <CircularProgress\n size={rem(theme, CHECKBOX_SPINNER.size)}\n sx={{\n position: 'absolute',\n top: '50%',\n left: '50%',\n marginTop: sxRem(CHECKBOX_SPINNER.offset),\n marginLeft: sxRem(CHECKBOX_SPINNER.offset),\n color: 'primary.main',\n }}\n />\n )}\n </div>\n );\n \n if (label) {\n return (\n <div data-testid={testId('container')}>\n <StyledFormControlLabel\n control={checkbox}\n label={label}\n error={error}\n disabled={isDisabled}\n />\n {helperText && (\n <StyledFormHelperText \n error={error}\n data-testid={testId('helper')}\n >\n {helperText}\n </StyledFormHelperText>\n )}\n </div>\n );\n }\n \n return checkbox;\n }\n);\n\nCheckbox.displayName = 'Checkbox';"],"mappings":"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","names":["glow","pulse"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/data-display/LazyImage/LazyImage.tsx"],"sourcesContent":["import Box from '@mui/material/Box/index.js';\nimport CircularProgress from '@mui/material/CircularProgress/index.js';\nimport { styled, useTheme, type Theme } from '@mui/material/styles/index.js';\nimport React from 'react';\nimport { Skeleton } from '../../layout/Skeleton';\nimport type { ResolvedLazyImageProps } from './LazyImage.hooks';\nimport { imgPassThrough, resolveLazyImageProps, useLazyImage } from './LazyImage.hooks';\nimport type { LazyImageProps } from './LazyImage.types';\nimport { sheen } from '../../../tokens/ink';\nimport { rem } from '../../../tokens/relative';\n\nconst ImageContainer = styled(Box)(() => ({\n position: 'relative',\n display: 'inline-block',\n overflow: 'hidden',\n}));\n\nconst StyledImage = styled('img')<{\n fadeIn?: boolean;\n fadeInDuration?: number;\n isLoaded?: boolean;\n}>(({ fadeIn, fadeInDuration = 300, isLoaded }) => ({\n display: 'block',\n maxWidth: '100%',\n height: 'auto',\n opacity: fadeIn ? (isLoaded ? 1 : 0) : 1,\n transition: fadeIn ? `opacity ${fadeInDuration}ms ease-in-out` : 'none',\n}));\n\nconst SpinnerOverlay = styled(Box)(({ theme }) => ({\n position: 'absolute',\n top: '50%',\n left: '50%',\n transform: 'translate(-50%, -50%)',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n backgroundColor: sheen(theme, 0.8),\n borderRadius: '50%',\n padding: theme.spacing(1),\n}));\n\nconst FallbackContainer = styled(Box)(({ theme }) => ({\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n backgroundColor: theme.palette.action.disabledBackground,\n color: theme.palette.text.secondary,\n width: '100%',\n height: '100%',\n position: 'absolute',\n top: 0,\n left: 0,\n}));\n\ntype Length = number | string | undefined;\n\n/** A length prop as React `style` and MUI's `Skeleton` read it: a number is design px. */\nconst styleLength = (theme: Theme, value: Length): string | undefined =>\n typeof value === 'number' ? rem(theme, value) : value;\n\n/**\n * A width or height above 0 and up to 1 is a fraction of the container's parent, as in `sx`.\n * A negative number is no fraction: it stays a (negative, so invalid) length.\n */\nconst isFraction = (value: Length): value is number =>\n typeof value === 'number' && value > 0 && value <= 1;\n\n/**\n * A length prop as `sx` reads it on a width or height: a number of 1 or less is\n * a fraction of the parent, and that stays so; any other number is design px.\n */\nconst sxLength = (theme: Theme, value: Length): string | undefined =>\n isFraction(value) ? `${value * 100}%` : styleLength(theme, value);\n\n/**\n * A width or height for what is drawn inside the container. The container alone\n * takes a fraction, so everything in it fills it: a percentage here is measured\n * against the container and would stack (FUT-2666). Otherwise the same as `styleLength`.\n */\nconst innerLength = (theme: Theme, value: Length): string | undefined =>\n isFraction(value) ? '100%' : styleLength(theme, value);\n\n/**\n * A width or height with its default. Only an unset or empty size takes it: `0`\n * is a size, which `||` read as unset (FUT-2669), and `sx` reads `''` as `0%`.\n */\nconst orDefault = (value: Length, fallback: number | string): number | string =>\n value === undefined || value === '' ? fallback : value;\n\n/**\n * A width/height as `metrics` and the `ReactNode` `ErrorFallback` read it\n * (FUT-2774 #4): an explicit empty string is normalized to \"as if unset\"\n * rather than mapped to a component-wide default. Unlike `SkeletonIndicator`\n * (a content-less box with nothing else to size by, hence its own deliberate\n * `'100%'`/`'auto'` fallback), these draw the real image or a `ReactNode`\n * fallback — both have their own intrinsic size, so leaving an unset width\n * unset lets it size naturally (`brand-link.tsx`'s logo relies on exactly\n * this: an unset width capped by its own `sx.maxWidth`).\n */\nconst emptyToUnset = (value: Length): Length => (value === '' ? undefined : value);\n\n/** The box the image occupies, as CSS, shared by the real image and every stand-in for it. */\ninterface BoxMetrics {\n width?: string;\n height?: string;\n objectFit: NonNullable<LazyImageProps['objectFit']>;\n objectPosition: string;\n borderRadius?: string;\n}\n\ninterface IndicatorProps {\n props: ResolvedLazyImageProps;\n metrics: BoxMetrics;\n}\n\nconst SkeletonIndicator: React.FC<IndicatorProps> = ({ props }) => {\n const theme = useTheme();\n return (\n <Skeleton\n // FUT-2774 #5: `skeletonProps.variant` was typed but never read — the\n // variant was hard-coded regardless of what a caller passed.\n variant={props.skeletonProps.variant ?? 'rectangular'}\n width={innerLength(theme, orDefault(props.width, '100%'))}\n height={innerLength(theme, orDefault(props.height, 'auto'))}\n // FUT-2774 #5: `||` reads a caller's `false` the same as \"unset\", so\n // `skeletonProps.animation={false}` could never turn the animation off.\n animation={props.skeletonProps.animation ?? 'pulse'}\n intensity={props.skeletonProps.intensity}\n borderRadius={styleLength(theme, props.borderRadius)}\n data-testid={`${props['data-testid']}-skeleton`}\n />\n );\n};\n\nconst SpinnerIndicator: React.FC<IndicatorProps> = ({ props }) => {\n const theme = useTheme();\n const { spinnerProps } = props;\n\n return (\n <SpinnerOverlay>\n <CircularProgress\n // FUT-2774 #5: `||` read `spinnerProps.size`/`thickness` of `0` the\n // same as unset — the same class of bug FUT-2669 fixed for\n // `width`/`height`, missed here.\n size={styleLength(theme, spinnerProps.size ?? 40)}\n thickness={spinnerProps.thickness ?? 4}\n sx={{ color: spinnerProps.color || theme.palette.primary.main }}\n data-testid={`${props['data-testid']}-spinner`}\n />\n </SpinnerOverlay>\n );\n};\n\n/**\n * The placeholder sits in the box's flow and gives the box its size: the box's\n * own when one is set, the placeholder's when not. The real image is drawn over\n * it (`OVER_PLACEHOLDER`) until it has faded in. Its load and error are not the\n * image's, so neither reaches the caller; a failing one just retires.\n */\nconst PlaceholderIndicator: React.FC<IndicatorProps & { onError: () => void }> = ({\n props,\n metrics,\n onError,\n}) => {\n if (!props.placeholder) return null;\n\n return (\n <StyledImage\n src={props.placeholder}\n alt={`${props.alt} (loading)`}\n style={metrics}\n decoding={props.decoding}\n loading={props.lazy ? 'lazy' : props.loading}\n onError={onError}\n data-testid={`${props['data-testid']}-placeholder`}\n />\n );\n};\n\nconst LoadingIndicator: React.FC<\n IndicatorProps & { kind: NonNullable<LazyImageProps['loadingState']>; onPlaceholderError: () => void }\n> = ({ kind, onPlaceholderError, ...rest }) => {\n switch (kind) {\n case 'skeleton':\n return <SkeletonIndicator {...rest} />;\n case 'spinner':\n return <SpinnerIndicator {...rest} />;\n case 'placeholder':\n return <PlaceholderIndicator {...rest} onError={onPlaceholderError} />;\n default:\n return null;\n }\n};\n\n/**\n * The real image while the placeholder is up: laid over it and cropped by\n * `objectFit`, so the box never grows to hold both. It takes the flow back\n * when the placeholder goes.\n */\nconst OVER_PLACEHOLDER: React.CSSProperties = {\n position: 'absolute',\n inset: 0,\n width: '100%',\n height: '100%',\n};\n\nconst ErrorFallback: React.FC<IndicatorProps> = ({ props, metrics }) => {\n const theme = useTheme();\n const { alt, fallback, width, height, borderRadius } = props;\n const testId = props['data-testid'];\n\n if (!fallback) return null;\n\n if (typeof fallback === 'string') {\n return (\n <StyledImage\n src={fallback}\n alt={`${alt} (fallback)`}\n style={metrics}\n fadeIn={false}\n isLoaded={true}\n data-testid={`${testId}-fallback`}\n />\n );\n }\n\n return (\n <FallbackContainer\n sx={{\n // FUT-2774 #4 (see `emptyToUnset`). Does NOT change the confirmed\n // height collapse — `ImageContainer`'s own height was already `'auto'`\n // either way; see `LazyImage.test.stories.tsx` for that separate,\n // unresolved defect. `orDefault(height, 'auto')` is safe to share with\n // `SkeletonIndicator` (unlike width's `'100%'`, deliberately NOT\n // shared — see `emptyToUnset`): a PERCENTAGE height against an\n // auto-height containing block computes to `auto` by spec regardless,\n // so whether `sx.height` is omitted (falling through to\n // `FallbackContainer`'s own `height: '100%'`) or set to the literal\n // `'auto'` here, an unset-height container gives the same result\n // either way — nothing for this fallback to protect against.\n width: innerLength(theme, emptyToUnset(width)),\n height: innerLength(theme, orDefault(height, 'auto')),\n borderRadius: styleLength(theme, borderRadius),\n }}\n data-testid={`${testId}-fallback`}\n >\n {fallback}\n </FallbackContainer>\n );\n};\n\n/**\n * LazyImage component with enhanced features for optimized image loading\n * Supports lazy loading, placeholders, error handling, and various loading states\n */\nexport const LazyImage = React.memo<LazyImageProps>(function LazyImage(rawProps) {\n const props = resolveLazyImageProps(rawProps);\n const theme = useTheme();\n const {\n state,\n containerRef,\n effectiveLoadingState,\n handleImageLoad,\n handleImageError,\n handlePlaceholderError,\n showImage,\n showLoading,\n showPlaceholder,\n } = useLazyImage(props);\n\n const { width, height, borderRadius, alt } = props;\n const testId = props['data-testid'];\n const metrics: BoxMetrics = {\n // FUT-2774 #4 (see `emptyToUnset`): `metrics` also sizes the REAL `<img>`,\n // where an unset width means \"the image's own intrinsic size\" —\n // `brand-link.tsx`'s logo relies on exactly that. `height`'s `'auto'`\n // fallback has no such live case to protect: a percentage height against\n // an auto-height containing block computes to `auto` regardless, so\n // sharing `SkeletonIndicator`'s fallback changes nothing observable.\n width: innerLength(theme, emptyToUnset(width)),\n height: innerLength(theme, orDefault(height, 'auto')),\n objectFit: props.objectFit,\n objectPosition: props.objectPosition,\n borderRadius: styleLength(theme, borderRadius),\n };\n\n return (\n <ImageContainer\n ref={containerRef}\n className={props.className}\n sx={{\n width: sxLength(theme, orDefault(width, 'auto')),\n height: sxLength(theme, orDefault(height, 'auto')),\n // The same px length the image gets (`metrics`): in `sx` a bare number\n // would be a multiple of `shape.borderRadius`, and this box clips (FUT-2656).\n borderRadius: metrics.borderRadius,\n }}\n data-testid={testId}\n >\n {showLoading && (\n <LoadingIndicator\n kind={effectiveLoadingState}\n props={props}\n metrics={metrics}\n onPlaceholderError={handlePlaceholderError}\n />\n )}\n\n {showImage && (\n <StyledImage\n src={state.currentSrc || undefined}\n alt={alt}\n onLoad={handleImageLoad}\n onError={handleImageError}\n fadeIn={props.fadeIn}\n fadeInDuration={props.fadeInDuration}\n isLoaded={!state.isLoading}\n decoding={props.decoding}\n loading={props.lazy ? 'lazy' : props.loading}\n style={{ ...metrics, ...props.sx, ...(showPlaceholder && OVER_PLACEHOLDER) }}\n aria-label={props['aria-label'] || alt}\n aria-describedby={props['aria-describedby']}\n role={props.role}\n data-testid={`${testId}-img`}\n {...imgPassThrough(props)}\n />\n )}\n\n {state.hasError && <ErrorFallback props={props} metrics={metrics} />}\n </ImageContainer>\n );\n});\n\nLazyImage.displayName = 'LazyImage';\n"],"mappings":"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","names":["LazyImage"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/form/Checkbox/Checkbox.metrics.ts"],"sourcesContent":["import { alpha } from '../../../tokens/color';\nimport { EFFECT_GLOW } from '../../../tokens/ink.core';\n\nimport type { CheckboxVariant } from './Checkbox.base';\n\n/**\n * THE NUMBERS BOTH `Checkbox` RENDERERS DRAW WITH.\n *\n * `Checkbox.tsx` (web, emotion over MUI's `Checkbox`) and\n * `Checkbox.native.tsx` (React Native) read this one table. As with the other\n * fields, some of it is what the web writes itself — the two variants' corner\n * treatment, the glow and pulse keyframes, the helper text's inset — and some\n * is what MUI supplies underneath: `SwitchBase`'s 9px padding, `SvgIcon`'s\n * three sizes, and the ink a box is drawn in.\n */\n\n/** MUI's `SwitchBase`: 9px of padding around the glyph, at every size. */\nexport const CHECKBOX_PADDING = 9;\n\n/** `SvgIcon`'s own three steps. MUI passes the `size` prop straight to the glyph. */\nexport const CHECKBOX_GLYPH_SIZES = { small: 20, medium: 24, large: 35 } as const;\nexport type CheckboxSize = keyof typeof CHECKBOX_GLYPH_SIZES;\n\nexport const glyphSize = (size: CheckboxSize | undefined): number =>\n CHECKBOX_GLYPH_SIZES[size ?? 'medium'] ?? CHECKBOX_GLYPH_SIZES.medium;\n\n/**\n * The three glyphs MUI draws, by state.\n *\n * `@12-apps/ui/icons` generates their paths from the same\n * `@mui/icons-material` package MUI's own `Checkbox` imports them from, so the\n * native box is the web box rather than a hand-drawn square.\n */\nexport const CHECKBOX_GLYPH = {\n checked: 'CheckBox',\n unchecked: 'CheckBoxOutlineBlank',\n indeterminate: 'IndeterminateCheckBox',\n} as const;\n\n/**\n * The corner each variant gives the glyph's box.\n *\n * `rounded` is a circle and `toggle` a 12px radius at 1.2 scale — both applied\n * to the SVG's own box on the web, where they show only through what the box\n * clips. Native gives them the same radius and the same scale.\n */\nexport const CHECKBOX_VARIANT: Record<CheckboxVariant, { radius?: number | string; scale?: number }> = {\n default: {},\n rounded: { radius: '50%' },\n toggle: { radius: 12, scale: 1.2 },\n};\n\n/** The wash a hovered box takes, and the 0.3s every state change runs over. */\nexport const CHECKBOX_HOVER_ALPHA = 0.1;\nexport const CHECKBOX_TRANSITION_MS = 300;\n\n/** `loading`: MUI's 20px `CircularProgress`, centred over the box. */\nexport const CHECKBOX_SPINNER = { size: 20, offset: -10 } as const;\n\n/**\n * `glow` and `pulse` are keyed to MUI's DEFAULT blue rather than the theme's\n * primary — `EFFECT_GLOW.muiBlue` (`tokens/ink.core`) — on both renderers.\n */\nexport const CHECKBOX_GLOW = { blur: { from: 5, to: 20 }, alpha: { from: 0.5, to: 0.8 }, ms: 2000 } as const;\nexport const CHECKBOX_PULSE = { spread: 10, alpha: 0.7, ms: 2000 } as const;\n\n/** MUI's default blue at an alpha, as the web's keyframes spell it. */\nexport const effectInk = (alphaValue: number): string => alpha(EFFECT_GLOW.muiBlue, alphaValue);\n\n/** `FormControlLabel`: flush left, its label at MUI's `body2` size. */\nexport const CHECKBOX_LABEL = { marginLeft: 0, fontSize: 14 } as const;\n\n/** `FormHelperText`: four spacing units in, half a unit down. */\nexport const CHECKBOX_HELPER = { marginLeftUnits: 4, marginTopUnits: 0.5, fontSize: 12, lineHeight: 1.66 } as const;\n"],"mappings":";;;;;;;;AAiBO,IAAM,mBAAmB;AAGzB,IAAM,uBAAuB,EAAE,OAAO,IAAI,QAAQ,IAAI,OAAO,GAAG;AAGhE,IAAM,YAAY,CAAC,SACxB,qBAAqB,QAAQ,QAAQ,KAAK,qBAAqB;AAS1D,IAAM,iBAAiB;AAAA,EAC5B,SAAS;AAAA,EACT,WAAW;AAAA,EACX,eAAe;AACjB;AASO,IAAM,mBAA0F;AAAA,EACrG,SAAS,CAAC;AAAA,EACV,SAAS,EAAE,QAAQ,MAAM;AAAA,EACzB,QAAQ,EAAE,QAAQ,IAAI,OAAO,IAAI;AACnC;AAGO,IAAM,uBAAuB;AAC7B,IAAM,yBAAyB;AAG/B,IAAM,mBAAmB,EAAE,MAAM,IAAI,QAAQ,IAAI;AAMjD,IAAM,gBAAgB,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,GAAG,OAAO,EAAE,MAAM,KAAK,IAAI,IAAI,GAAG,IAAI,IAAK;AAC3F,IAAM,iBAAiB,EAAE,QAAQ,IAAI,OAAO,KAAK,IAAI,IAAK;AAG1D,IAAM,YAAY,CAAC,eAA+B,MAAM,YAAY,SAAS,UAAU;AAGvF,IAAM,iBAAiB,EAAE,YAAY,GAAG,UAAU,GAAG;AAGrD,IAAM,kBAAkB,EAAE,iBAAiB,GAAG,gBAAgB,KAAK,UAAU,IAAI,YAAY,KAAK;","names":[]}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|