@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.
Files changed (59) hide show
  1. package/NATIVE.md +1 -1
  2. package/dist/_internal/components/data-display/DataViews/base-card.js +4 -3
  3. package/dist/_internal/components/data-display/DataViews/base-list-card-gutters.js +5 -3
  4. package/dist/_internal/components/data-display/DataViews/base-list-card.js +5 -4
  5. package/dist/_internal/components/data-display/LazyImage/LazyImage.js +7 -1
  6. package/dist/_internal/components/data-display/index.js +1 -1
  7. package/dist/_internal/components/form/Checkbox/Checkbox.js +4 -2
  8. package/dist/_internal/components/form/Checkbox/Checkbox.metrics.js +7 -3
  9. package/dist/_internal/components/form/index.js +2 -2
  10. package/dist/_internal/components/index.js +3 -3
  11. package/dist/{chunk-B2ZITMYG.js → chunk-26QYVCDK.js} +2 -2
  12. package/dist/{chunk-CEJTGG6J.js → chunk-CIBTYBBN.js} +2 -2
  13. package/dist/{chunk-FOM7OMJR.js → chunk-DDL6W7JS.js} +6 -3
  14. package/dist/chunk-DDL6W7JS.js.map +1 -0
  15. package/dist/{chunk-QWFAEUFV.js → chunk-EO6LCGGQ.js} +40 -16
  16. package/dist/chunk-EO6LCGGQ.js.map +1 -0
  17. package/dist/{chunk-KMC6WP66.js → chunk-KPZ53JY6.js} +2 -2
  18. package/dist/{chunk-TIQXDPIX.js → chunk-RIJGMRJK.js} +10 -1
  19. package/dist/chunk-RIJGMRJK.js.map +1 -0
  20. package/dist/data-display/DataViews.js +5 -5
  21. package/dist/data-display/LazyImage.js +7 -1
  22. package/dist/form/Checkbox.js +4 -2
  23. package/dist/native/form/Checkbox.js +9 -2
  24. package/dist/native/form/Checkbox.js.map +1 -1
  25. package/dist/types/components/data-display/LazyImage/LazyImage.d.ts.map +1 -1
  26. package/dist/types/components/data-display/LazyImage/LazyImage.types.d.ts +13 -0
  27. package/dist/types/components/data-display/LazyImage/LazyImage.types.d.ts.map +1 -1
  28. package/dist/types/components/form/Checkbox/Checkbox.base.d.ts +23 -3
  29. package/dist/types/components/form/Checkbox/Checkbox.base.d.ts.map +1 -1
  30. package/dist/types/components/form/Checkbox/Checkbox.d.ts.map +1 -1
  31. package/dist/types/components/form/Checkbox/Checkbox.metrics.d.ts +23 -8
  32. package/dist/types/components/form/Checkbox/Checkbox.metrics.d.ts.map +1 -1
  33. package/dist/types/components/form/Checkbox/Checkbox.types.d.ts +5 -2
  34. package/dist/types/components/form/Checkbox/Checkbox.types.d.ts.map +1 -1
  35. package/dist/types-native/components/form/Checkbox/Checkbox.base.d.ts +23 -3
  36. package/dist/types-native/components/form/Checkbox/Checkbox.base.d.ts.map +1 -1
  37. package/dist/types-native/components/form/Checkbox/Checkbox.metrics.d.ts +23 -8
  38. package/dist/types-native/components/form/Checkbox/Checkbox.metrics.d.ts.map +1 -1
  39. package/dist/types-native/components/form/Checkbox/Checkbox.native.d.ts +0 -1
  40. package/dist/types-native/components/form/Checkbox/Checkbox.native.d.ts.map +1 -1
  41. package/dist/types-native/components/form/Checkbox/Checkbox.types.native.d.ts +1 -5
  42. package/dist/types-native/components/form/Checkbox/Checkbox.types.native.d.ts.map +1 -1
  43. package/package.json +1 -1
  44. package/src/components/data-display/LazyImage/LazyImage.md +23 -1
  45. package/src/components/data-display/LazyImage/LazyImage.tsx +80 -15
  46. package/src/components/data-display/LazyImage/LazyImage.types.ts +13 -0
  47. package/src/components/form/Checkbox/Checkbox.base.ts +25 -3
  48. package/src/components/form/Checkbox/Checkbox.md +1 -0
  49. package/src/components/form/Checkbox/Checkbox.metrics.ts +32 -4
  50. package/src/components/form/Checkbox/Checkbox.native.tsx +2 -1
  51. package/src/components/form/Checkbox/Checkbox.tsx +3 -0
  52. package/src/components/form/Checkbox/Checkbox.types.native.ts +1 -5
  53. package/src/components/form/Checkbox/Checkbox.types.ts +5 -2
  54. package/dist/chunk-FOM7OMJR.js.map +0 -1
  55. package/dist/chunk-QWFAEUFV.js.map +0 -1
  56. package/dist/chunk-TIQXDPIX.js.map +0 -1
  57. /package/dist/{chunk-B2ZITMYG.js.map → chunk-26QYVCDK.js.map} +0 -0
  58. /package/dist/{chunk-CEJTGG6J.js.map → chunk-CIBTYBBN.js.map} +0 -0
  59. /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={innerLength(theme, orDefault(props.width, '100%'))}
125
- height={innerLength(theme, orDefault(props.height, 'auto'))}
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`). Does NOT change the confirmed
232
- // height collapse — `ImageContainer`'s own height was already `'auto'`
233
- // either way; see `LazyImage.test.stories.tsx` for that separate,
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: sxLength(theme, orDefault(width, 'auto')),
294
- height: sxLength(theme, orDefault(height, 'auto')),
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. Neither is
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
- /** `SvgIcon`'s own three steps. MUI passes the `size` prop straight to the glyph. */
21
- export const CHECKBOX_GLYPH_SIZES = { small: 20, medium: 24, large: 35 } as const;
22
- export type CheckboxSize = keyof typeof CHECKBOX_GLYPH_SIZES;
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`, `size`, `color`, `icon`, `inputProps`, `sx`.
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":";;;;;;;;;;;;;;;;;;;;;;;;AAAA,OAAO,iBAAiB;AACxB,OAAO,sBAAsB;AAC7B,OAAO,sBAAsB;AAC7B,OAAO,oBAAoB;AAC3B,SAAS,OAAO,WAAW,QAAQ,gBAAgB;AAEnD,OAAO,WAAW;AAkIZ,SACE,KADF;AAhHN,IAAM,QAAQ,CAAC,UAAiB;AAAA;AAAA,0BAEN,UAAU,eAAe,KAAK,CAAC;AAAA;AAAA;AAAA,wBAGjC,IAAI,OAAO,eAAe,MAAM,CAAC,IAAI,UAAU,CAAC,CAAC;AAAA;AAAA;AAAA,0BAG/C,UAAU,CAAC,CAAC;AAAA;AAAA;AAItC,IAAM,OAAO,CAAC,UAAiB;AAAA;AAAA,sBAET,IAAI,OAAO,cAAc,KAAK,IAAI,CAAC,IAAI,UAAU,cAAc,MAAM,IAAI,CAAC;AAAA;AAAA;AAAA,sBAG1E,IAAI,OAAO,cAAc,KAAK,EAAE,CAAC,IAAI,UAAU,cAAc,MAAM,EAAE,CAAC;AAAA;AAAA;AAK5F,IAAM,WAAW,CAAC,OAAc,WAC9B,OAAO,WAAW,WAAW,IAAI,OAAO,MAAM,IAAI;AAEpD,IAAM,iBAAiB,OAAO,aAAa;AAAA,EACzC,mBAAmB,CAAC,SAAS,CAAC,CAAC,iBAAiB,UAAU,QAAQ,OAAO,EAAE,SAAS,IAAc;AACpG,CAAC;AAAA,EAMC,CAAC,EAAE,OAAO,eAAe,QAAQ,MAAM,UAAU,OAAO,UAAU,OAAO;AAAA,IACvE,GAAI,kBAAkB,aAAa;AAAA,MACjC,sBAAsB;AAAA,QACpB,cAAc,SAAS,OAAO,iBAAiB,QAAQ,MAAM;AAAA,MAC/D;AAAA,IACF;AAAA,IAEA,GAAI,kBAAkB,YAAY;AAAA,MAChC,sBAAsB;AAAA,QACpB,cAAc,SAAS,OAAO,iBAAiB,OAAO,MAAM;AAAA,QAC5D,WAAW,SAAS,iBAAiB,OAAO,KAAK;AAAA,MACnD;AAAA,IACF;AAAA,IAEA,sBAAsB;AAAA,MACpB,YAAY,OAAO,yBAAyB,GAAI;AAAA,MAChD,GAAI,WAAW,SAAS;AAAA,QACtB,0BAA0B;AAAA,UACxB,SAAS;AAAA,QACX;AAAA,MACF;AAAA,MACA,GAAI,YAAY;AAAA,QACd,WAAW,GAAG,KAAK,KAAK,CAAC,IAAI,cAAc,KAAK,GAAI;AAAA,MACtD;AAAA,MACA,GAAI,aAAa;AAAA,QACf,WAAW,GAAG,MAAM,KAAK,CAAC,IAAI,eAAe,KAAK,GAAI;AAAA,MACxD;AAAA,MACA,WAAW;AAAA,QACT,iBAAiB,MAAM,MAAM,QAAQ,QAAQ,MAAM,oBAAoB;AAAA,MACzE;AAAA,IACF;AAAA,IAEA,iBAAiB;AAAA,MACf,OAAO,MAAM,QAAQ,QAAQ;AAAA,IAC/B;AAAA,IAEA,+BAA+B;AAAA,MAC7B,OAAO,MAAM,QAAQ,QAAQ;AAAA,IAC/B;AAAA,EACF;AACF;AAEA,IAAM,yBAAyB,OAAO,kBAAkB;AAAA,EACtD,mBAAmB,CAAC,SAAS,SAAS;AACxC,CAAC;AAAA,EACC,CAAC,EAAE,OAAO,MAAM,OAAO;AAAA,IACrB,YAAY,IAAI,OAAO,eAAe,UAAU;AAAA,IAChD,gCAAgC;AAAA,MAC9B,OAAO,QAAQ,MAAM,QAAQ,MAAM,OAAO,MAAM,QAAQ,KAAK;AAAA,MAC7D,UAAU,IAAI,OAAO,eAAe,QAAQ;AAAA,IAC9C;AAAA,EACF;AACF;AAEA,IAAM,uBAAuB,OAAO,cAAc,EAAE,CAAC,EAAE,MAAM,OAAO;AAAA,EAClE,YAAY,MAAM,QAAQ,gBAAgB,eAAe;AAAA,EACzD,WAAW,MAAM,QAAQ,gBAAgB,cAAc;AACzD,EAAE;AAEK,IAAM,WAAW,MAAM;AAAA,EAC5B,CAAC,UAAU,QAAQ;AACjB,UAAM;AAAA,MACJ;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,MAAAA;AAAA,MACA,OAAAC;AAAA,MACA;AAAA,MACA,GAAG;AAAA,IACL,IAAI,qBAAqB,QAAQ;AACjC,UAAM,QAAQ,SAAS;AACvB,UAAM,aAAa,YAAY;AAC/B,UAAM,EAAE,QAAQ,YAAY,MAAM,MAAM,IAAI,YAAY,MAAM;AAC9D,UAAM,SAAS,WAAW,UAAU;AAEpC,UAAM,WACJ,qBAAC,SAAI,OAAO,EAAE,UAAU,YAAY,SAAS,cAAc,GACzD;AAAA;AAAA,QAAC;AAAA;AAAA,UACC;AAAA,UACA,eAAe;AAAA,UACf;AAAA,UACA,MAAMD;AAAA,UACN,OAAOC;AAAA,UACP,UAAU;AAAA,UACV,eAAa,cAAc;AAAA,UAC1B,GAAG;AAAA;AAAA,MACN;AAAA,MACC,WACC;AAAA,QAAC;AAAA;AAAA,UACC,MAAM,IAAI,OAAO,iBAAiB,IAAI;AAAA,UACtC,IAAI;AAAA,YACF,UAAU;AAAA,YACV,KAAK;AAAA,YACL,MAAM;AAAA,YACN,WAAW,MAAM,iBAAiB,MAAM;AAAA,YACxC,YAAY,MAAM,iBAAiB,MAAM;AAAA,YACzC,OAAO;AAAA,UACT;AAAA;AAAA,MACF;AAAA,OAEJ;AAGF,QAAI,OAAO;AACT,aACE,qBAAC,SAAI,eAAa,OAAO,WAAW,GAClC;AAAA;AAAA,UAAC;AAAA;AAAA,YACC,SAAS;AAAA,YACT;AAAA,YACA;AAAA,YACA,UAAU;AAAA;AAAA,QACZ;AAAA,QACC,cACC;AAAA,UAAC;AAAA;AAAA,YACC;AAAA,YACA,eAAa,OAAO,QAAQ;AAAA,YAE3B;AAAA;AAAA,QACH;AAAA,SAEJ;AAAA,IAEJ;AAEA,WAAO;AAAA,EACT;AACF;AAEA,SAAS,cAAc;","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":[]}