@12-apps/ui 6.39.6 → 6.39.8

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 (44) hide show
  1. package/dist/_internal/components/data-display/LazyImage/LazyImage.hooks.js +1 -1
  2. package/dist/_internal/components/data-display/LazyImage/LazyImage.js +2 -2
  3. package/dist/_internal/components/data-display/index.js +2 -2
  4. package/dist/_internal/components/form/Autocomplete/Autocomplete.helpers.js +1 -1
  5. package/dist/_internal/components/form/Autocomplete/Autocomplete.js +5 -5
  6. package/dist/_internal/components/form/Autocomplete/Autocomplete.keys.js +2 -2
  7. package/dist/_internal/components/form/Autocomplete/Autocomplete.parts.js +2 -2
  8. package/dist/_internal/components/form/Autocomplete/useAutocomplete.js +3 -3
  9. package/dist/_internal/components/form/index.js +5 -5
  10. package/dist/_internal/components/index.js +7 -7
  11. package/dist/{chunk-LW2LQHIM.js → chunk-BVF2BSWV.js} +20 -9
  12. package/dist/chunk-BVF2BSWV.js.map +1 -0
  13. package/dist/{chunk-PH36XEB3.js → chunk-DQZCQV2C.js} +4 -4
  14. package/dist/{chunk-HCT5ELST.js → chunk-GMUYCN33.js} +47 -12
  15. package/dist/chunk-GMUYCN33.js.map +1 -0
  16. package/dist/{chunk-BN742L2M.js → chunk-H6AWKLOV.js} +13 -5
  17. package/dist/chunk-H6AWKLOV.js.map +1 -0
  18. package/dist/{chunk-362OGE5J.js → chunk-HBRKA67N.js} +3 -3
  19. package/dist/{chunk-XBHQT4BQ.js → chunk-JXS56EW5.js} +3 -3
  20. package/dist/{chunk-XBHQT4BQ.js.map → chunk-JXS56EW5.js.map} +1 -1
  21. package/dist/{chunk-JDFTIK4L.js → chunk-PELRSZFU.js} +2 -2
  22. package/dist/data-display/LazyImage.js +2 -2
  23. package/dist/form/Autocomplete.js +5 -5
  24. package/dist/types/components/data-display/LazyImage/LazyImage.d.ts.map +1 -1
  25. package/dist/types/components/data-display/LazyImage/LazyImage.hooks.d.ts +4 -1
  26. package/dist/types/components/data-display/LazyImage/LazyImage.hooks.d.ts.map +1 -1
  27. package/dist/types/components/data-display/LazyImage/LazyImage.types.d.ts +19 -3
  28. package/dist/types/components/data-display/LazyImage/LazyImage.types.d.ts.map +1 -1
  29. package/dist/types/components/form/Autocomplete/Autocomplete.helpers.d.ts +16 -3
  30. package/dist/types/components/form/Autocomplete/Autocomplete.helpers.d.ts.map +1 -1
  31. package/dist/types/components/form/Autocomplete/Autocomplete.parts.d.ts.map +1 -1
  32. package/package.json +1 -1
  33. package/src/components/data-display/LazyImage/LazyImage.hooks.ts +78 -16
  34. package/src/components/data-display/LazyImage/LazyImage.md +23 -3
  35. package/src/components/data-display/LazyImage/LazyImage.tsx +31 -10
  36. package/src/components/data-display/LazyImage/LazyImage.types.ts +19 -3
  37. package/src/components/form/Autocomplete/Autocomplete.helpers.ts +23 -5
  38. package/src/components/form/Autocomplete/Autocomplete.parts.tsx +17 -3
  39. package/dist/chunk-BN742L2M.js.map +0 -1
  40. package/dist/chunk-HCT5ELST.js.map +0 -1
  41. package/dist/chunk-LW2LQHIM.js.map +0 -1
  42. /package/dist/{chunk-PH36XEB3.js.map → chunk-DQZCQV2C.js.map} +0 -0
  43. /package/dist/{chunk-362OGE5J.js.map → chunk-HBRKA67N.js.map} +0 -0
  44. /package/dist/{chunk-JDFTIK4L.js.map → chunk-PELRSZFU.js.map} +0 -0
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  firstGhostSuggestion
3
- } from "./chunk-XBHQT4BQ.js";
3
+ } from "./chunk-JXS56EW5.js";
4
4
 
5
5
  // src/components/form/Autocomplete/Autocomplete.keys.ts
6
6
  function canAcceptGhost(ctx) {
@@ -86,4 +86,4 @@ function handleAutocompleteKey(event, composition, ctx) {
86
86
  export {
87
87
  handleAutocompleteKey
88
88
  };
89
- //# sourceMappingURL=chunk-JDFTIK4L.js.map
89
+ //# sourceMappingURL=chunk-PELRSZFU.js.map
@@ -1,8 +1,8 @@
1
1
  import "../chunk-5ZFO7ROE.js";
2
2
  import {
3
3
  LazyImage
4
- } from "../chunk-LW2LQHIM.js";
5
- import "../chunk-HCT5ELST.js";
4
+ } from "../chunk-BVF2BSWV.js";
5
+ import "../chunk-GMUYCN33.js";
6
6
  import "../chunk-724ZONOL.js";
7
7
  import "../chunk-BEGTLELZ.js";
8
8
  import "../chunk-E2QIKJX2.js";
@@ -2,11 +2,11 @@ import "../chunk-2LXHUGYB.js";
2
2
  import {
3
3
  Autocomplete,
4
4
  Autocomplete_default
5
- } from "../chunk-PH36XEB3.js";
6
- import "../chunk-362OGE5J.js";
7
- import "../chunk-BN742L2M.js";
8
- import "../chunk-JDFTIK4L.js";
9
- import "../chunk-XBHQT4BQ.js";
5
+ } from "../chunk-DQZCQV2C.js";
6
+ import "../chunk-HBRKA67N.js";
7
+ import "../chunk-H6AWKLOV.js";
8
+ import "../chunk-PELRSZFU.js";
9
+ import "../chunk-JXS56EW5.js";
10
10
  import "../chunk-CGO452DC.js";
11
11
  import "../chunk-GDN2LUTZ.js";
12
12
  import "../chunk-7FDU3FIN.js";
@@ -1 +1 @@
1
- {"version":3,"file":"LazyImage.d.ts","sourceRoot":"","sources":["../../../../../src/components/data-display/LazyImage/LazyImage.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,MAAM,OAAO,CAAC;AAI1B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAoMxD;;;GAGG;AACH,eAAO,MAAM,SAAS,4CAoEpB,CAAC"}
1
+ {"version":3,"file":"LazyImage.d.ts","sourceRoot":"","sources":["../../../../../src/components/data-display/LazyImage/LazyImage.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,MAAM,OAAO,CAAC;AAI1B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAuNxD;;;GAGG;AACH,eAAO,MAAM,SAAS,4CAsEpB,CAAC"}
@@ -6,7 +6,8 @@ export declare const resolveLazyImageProps: (props: LazyImageProps) => ResolvedL
6
6
  export declare const imgPassThrough: (props: ResolvedLazyImageProps) => Record<string, unknown>;
7
7
  /**
8
8
  * Owns whether the real image has been requested yet and how it went: the
9
- * observer above, the load/error handlers, and the retry timer.
9
+ * observer above, the load/error handlers, the retry timer, and how long the
10
+ * placeholder stays up.
10
11
  */
11
12
  export declare const useLazyImage: (props: ResolvedLazyImageProps) => {
12
13
  state: LazyImageState;
@@ -14,8 +15,10 @@ export declare const useLazyImage: (props: ResolvedLazyImageProps) => {
14
15
  effectiveLoadingState: "placeholder" | "none" | "spinner" | "skeleton";
15
16
  handleImageLoad: (event: React.SyntheticEvent<HTMLImageElement>) => void;
16
17
  handleImageError: (event: React.SyntheticEvent<HTMLImageElement>) => void;
18
+ handlePlaceholderError: () => void;
17
19
  showImage: boolean;
18
20
  showLoading: boolean;
21
+ showPlaceholder: boolean;
19
22
  };
20
23
  export {};
21
24
  //# sourceMappingURL=LazyImage.hooks.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"LazyImage.hooks.d.ts","sourceRoot":"","sources":["../../../../../src/components/data-display/LazyImage/LazyImage.hooks.ts"],"names":[],"mappings":"AACA,OAAO,KAAmD,MAAM,OAAO,CAAC;AAIxE,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAExE,KAAK,sBAAsB,GACvB,QAAQ,GACR,cAAc,GACd,WAAW,GACX,gBAAgB,GAChB,MAAM,GACN,WAAW,GACX,QAAQ,GACR,gBAAgB,GAChB,cAAc,GACd,YAAY,GACZ,YAAY,GACZ,UAAU,GACV,eAAe,GACf,cAAc,GACd,IAAI,GACJ,MAAM,CAAC;AAEX,MAAM,MAAM,sBAAsB,GAAG,cAAc,GACjD,QAAQ,CAAC,IAAI,CAAC,cAAc,EAAE,sBAAsB,CAAC,CAAC,CAAC;AA4BzD,eAAO,MAAM,qBAAqB,GAAI,OAAO,cAAc,KAAG,sBACkB,CAAC;AAajF,eAAO,MAAM,cAAc,GAAI,OAAO,sBAAsB,KAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CACH,CAAC;AAgEnF;;;GAGG;AACH,eAAO,MAAM,YAAY,GAAI,OAAO,sBAAsB;;;;6BAkC9C,KAAK,CAAC,cAAc,CAAC,gBAAgB,CAAC;8BAQtC,KAAK,CAAC,cAAc,CAAC,gBAAgB,CAAC;;;CA4BjD,CAAC"}
1
+ {"version":3,"file":"LazyImage.hooks.d.ts","sourceRoot":"","sources":["../../../../../src/components/data-display/LazyImage/LazyImage.hooks.ts"],"names":[],"mappings":"AACA,OAAO,KAAmD,MAAM,OAAO,CAAC;AAIxE,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAExE,KAAK,sBAAsB,GACvB,QAAQ,GACR,cAAc,GACd,WAAW,GACX,gBAAgB,GAChB,MAAM,GACN,WAAW,GACX,QAAQ,GACR,gBAAgB,GAChB,cAAc,GACd,YAAY,GACZ,YAAY,GACZ,UAAU,GACV,eAAe,GACf,cAAc,GACd,IAAI,GACJ,MAAM,CAAC;AAEX,MAAM,MAAM,sBAAsB,GAAG,cAAc,GACjD,QAAQ,CAAC,IAAI,CAAC,cAAc,EAAE,sBAAsB,CAAC,CAAC,CAAC;AA4BzD,eAAO,MAAM,qBAAqB,GAAI,OAAO,cAAc,KAAG,sBACkB,CAAC;AAajF,eAAO,MAAM,cAAc,GAAI,OAAO,sBAAsB,KAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CACH,CAAC;AA0InF;;;;GAIG;AACH,eAAO,MAAM,YAAY,GAAI,OAAO,sBAAsB;;;;6BAgB9C,KAAK,CAAC,cAAc,CAAC,gBAAgB,CAAC;8BAStC,KAAK,CAAC,cAAc,CAAC,gBAAgB,CAAC;;;;;CAgCjD,CAAC"}
@@ -28,7 +28,17 @@ export interface LazyImageProps extends React.ImgHTMLAttributes<HTMLImageElement
28
28
  */
29
29
  height?: number | string;
30
30
  /**
31
- * Placeholder image URL to show while loading
31
+ * A low-resolution image URL shown while the real image loads. Only
32
+ * `loadingState="placeholder"` draws it; under any other loading state it is
33
+ * ignored. It shows from mount, lazy or not, until the real image has loaded
34
+ * and faded in (at once when `fadeIn` is off) or has finally failed. The real
35
+ * image is drawn over it and cropped by `objectFit`, so the box takes the
36
+ * placeholder's shape until the fade ends. A placeholder that fails to load is
37
+ * dropped, as if none were set, and is never reported to `onError`; neither
38
+ * does its load reach `onLoad`.
39
+ *
40
+ * Give LazyImage a `width` (or `"100%"`) in placeholder mode: with none, the
41
+ * placeholder shows at its own, usually tiny, size.
32
42
  */
33
43
  placeholder?: string;
34
44
  /**
@@ -39,8 +49,12 @@ export interface LazyImageProps extends React.ImgHTMLAttributes<HTMLImageElement
39
49
  * Loading state type
40
50
  * - 'skeleton': Shows a skeleton loader
41
51
  * - 'spinner': Shows a loading spinner
42
- * - 'placeholder': Shows placeholder image
52
+ * - 'placeholder': Shows the `placeholder` image, in the box's flow, until the
53
+ * real image has faded in over it; nothing when no `placeholder` is set.
54
+ * Set a `width` (or `"100%"`), or the placeholder shows at its own size.
43
55
  * - 'none': No loading indicator
56
+ *
57
+ * `placeholder` applies only to 'placeholder'.
44
58
  * @default 'skeleton'
45
59
  */
46
60
  loadingState?: LazyImageLoadingState;
@@ -151,7 +165,9 @@ export interface LazyImageProps extends React.ImgHTMLAttributes<HTMLImageElement
151
165
  * Inline CSS for the `<img>` (its `style`), not MUI's `sx`: theme values and
152
166
  * nested selectors such as `'&:hover'` do not apply. The image's box clips
153
167
  * anything painted outside the image, such as a shadow, so put those on a
154
- * wrapping element.
168
+ * wrapping element. While a placeholder is up (`loadingState="placeholder"`),
169
+ * the image is laid over it, so its `position`, `inset`, `width` and `height`
170
+ * are fixed until the placeholder retires; the rest applies throughout.
155
171
  */
156
172
  sx?: React.CSSProperties;
157
173
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"LazyImage.types.d.ts","sourceRoot":"","sources":["../../../../../src/components/data-display/LazyImage/LazyImage.types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"LazyImage.types.d.ts","sourceRoot":"","sources":["../../../../../src/components/data-display/LazyImage/LazyImage.types.ts"],"names":[],"mappings":"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"}
@@ -19,9 +19,22 @@ export declare function filterSuggestions<T>(suggestions: T[], query: string, ma
19
19
  * Shared by the ghost-text effect and ArrowRight completion.
20
20
  */
21
21
  export declare function firstGhostSuggestion<T>(list: T[], inputValue: string, getLabel: (item: T) => string, getSuggestionType: (item: T) => SuggestionType | undefined): T | undefined;
22
+ /** One run of a highlighted label: plain text, or text the query matched. */
23
+ export interface HighlightSegment {
24
+ text: string;
25
+ match: boolean;
26
+ }
22
27
  /**
23
- * Wrap query matches in `<mark>` for the default suggestion renderer. Skipped for
24
- * fuzzy mode (non-contiguous matches) and empty queries. Returns HTML.
28
+ * Split `label` into the runs the query matches and the runs between them, in
29
+ * order, for the default suggestion renderer to wrap the matches in `<mark>`.
30
+ * Every occurrence of the query matches, case-insensitively and literally (regex
31
+ * metacharacters are escaped). Fuzzy mode (non-contiguous matches) and an empty
32
+ * query yield the whole label as one unmatched run.
33
+ *
34
+ * Returns TEXT, never markup: a label is caller data (in a consumer, a user's
35
+ * own display name), so the renderer must hand each run to React as a text
36
+ * child, which escapes it. Building an HTML string here and injecting it is how
37
+ * a label of `<img onerror=…>` once ran script in the host page.
25
38
  */
26
- export declare function highlightLabel(label: string, query: string, matchMode: MatchMode): string;
39
+ export declare function highlightLabel(label: string, query: string, matchMode: MatchMode): HighlightSegment[];
27
40
  //# sourceMappingURL=Autocomplete.helpers.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Autocomplete.helpers.d.ts","sourceRoot":"","sources":["../../../../../src/components/form/Autocomplete/Autocomplete.helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE3D,gCAAgC;AAChC,MAAM,MAAM,SAAS,GAAG,YAAY,GAAG,UAAU,GAAG,OAAO,CAAC;AAE5D,mFAAmF;AACnF,eAAO,MAAM,aAAa,GAAI,CAAC,EAAG,MAAM,CAAC,KAAG,MAO3C,CAAC;AAEF,kFAAkF;AAClF,eAAO,MAAM,eAAe,GAAI,CAAC,EAAG,MAAM,CAAC,KAAG,MAO7C,CAAC;AAEF,kFAAkF;AAClF,eAAO,MAAM,wBAAwB,GAAI,CAAC,EAAG,MAAM,CAAC,KAAG,cAAc,GAAG,SAMvE,CAAC;AAEF,iFAAiF;AACjF,eAAO,MAAM,aAAa,GAAI,CAAC,EAAG,MAAM,CAAC,KAAG,MAAM,GAAG,SAMpD,CAAC;AAEF,8EAA8E;AAC9E,eAAO,MAAM,eAAe,GAAI,KAAK,MAAM,KAAG,IAI7C,CAAC;AAwBF,qFAAqF;AACrF,wBAAgB,iBAAiB,CAAC,CAAC,EACjC,WAAW,EAAE,CAAC,EAAE,EAChB,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,SAAS,EACpB,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,MAAM,GAC5B,CAAC,EAAE,CAGL;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,CAAC,EACpC,IAAI,EAAE,CAAC,EAAE,EACT,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,MAAM,EAC7B,iBAAiB,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,cAAc,GAAG,SAAS,GACzD,CAAC,GAAG,SAAS,CAMf;AAOD;;;GAGG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,GAAG,MAAM,CAIzF"}
1
+ {"version":3,"file":"Autocomplete.helpers.d.ts","sourceRoot":"","sources":["../../../../../src/components/form/Autocomplete/Autocomplete.helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE3D,gCAAgC;AAChC,MAAM,MAAM,SAAS,GAAG,YAAY,GAAG,UAAU,GAAG,OAAO,CAAC;AAE5D,mFAAmF;AACnF,eAAO,MAAM,aAAa,GAAI,CAAC,EAAG,MAAM,CAAC,KAAG,MAO3C,CAAC;AAEF,kFAAkF;AAClF,eAAO,MAAM,eAAe,GAAI,CAAC,EAAG,MAAM,CAAC,KAAG,MAO7C,CAAC;AAEF,kFAAkF;AAClF,eAAO,MAAM,wBAAwB,GAAI,CAAC,EAAG,MAAM,CAAC,KAAG,cAAc,GAAG,SAMvE,CAAC;AAEF,iFAAiF;AACjF,eAAO,MAAM,aAAa,GAAI,CAAC,EAAG,MAAM,CAAC,KAAG,MAAM,GAAG,SAMpD,CAAC;AAEF,8EAA8E;AAC9E,eAAO,MAAM,eAAe,GAAI,KAAK,MAAM,KAAG,IAI7C,CAAC;AAwBF,qFAAqF;AACrF,wBAAgB,iBAAiB,CAAC,CAAC,EACjC,WAAW,EAAE,CAAC,EAAE,EAChB,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,SAAS,EACpB,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,MAAM,GAC5B,CAAC,EAAE,CAGL;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,CAAC,EACpC,IAAI,EAAE,CAAC,EAAE,EACT,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,MAAM,EAC7B,iBAAiB,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,cAAc,GAAG,SAAS,GACzD,CAAC,GAAG,SAAS,CAMf;AAOD,6EAA6E;AAC7E,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,OAAO,CAAC;CAChB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,GAAG,gBAAgB,EAAE,CAQrG"}
@@ -1 +1 @@
1
- {"version":3,"file":"Autocomplete.parts.d.ts","sourceRoot":"","sources":["../../../../../src/components/form/Autocomplete/Autocomplete.parts.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Autocomplete.parts.d.ts","sourceRoot":"","sources":["../../../../../src/components/form/Autocomplete/Autocomplete.parts.tsx"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@12-apps/ui",
3
- "version": "6.39.6",
3
+ "version": "6.39.8",
4
4
  "license": "MIT",
5
5
  "packageManager": "pnpm@10.34.5",
6
6
  "type": "module",
@@ -115,8 +115,8 @@ const useVisibility = (
115
115
  }, [lazy, src, rootMargin, threshold, isVisible, onLoadStart, onVisible, containerRef]);
116
116
  };
117
117
 
118
- /** A pending retry has to be cancelled on unmount, or it sets state on a dead component. */
119
- const useRetryTimeout = () => {
118
+ /** A pending timer has to be cancelled on unmount, or it sets state on a dead component. */
119
+ const useTimeoutRef = () => {
120
120
  const ref = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);
121
121
 
122
122
  useEffect(
@@ -132,25 +132,59 @@ const useRetryTimeout = () => {
132
132
  };
133
133
 
134
134
  /**
135
- * Owns whether the real image has been requested yet and how it went: the
136
- * observer above, the load/error handlers, and the retry timer.
135
+ * Whether the placeholder image is up. It is a loading state: only
136
+ * `loadingState="placeholder"` with a `placeholder` set draws it, from mount
137
+ * until the real image settles — its `load` plus the fade, or its final error.
138
+ * A placeholder that fails to load retires at once, as if none were set.
137
139
  */
138
- export const useLazyImage = (props: ResolvedLazyImageProps) => {
139
- const { src, placeholder, lazy, loadingState, showSpinner } = props;
140
- const { onLoad, onError, retryOnError, maxRetries, retryDelay } = props;
140
+ const usePlaceholderPhase = (
141
+ props: ResolvedLazyImageProps,
142
+ loadingState: NonNullable<LazyImageProps['loadingState']>,
143
+ ) => {
144
+ const { placeholder, fadeIn, fadeInDuration } = props;
145
+ // Only this mode draws a placeholder. Otherwise the real image settling
146
+ // retires the phase at once — batched into the load's own render, with no
147
+ // timer — so a placeholder switched on afterwards never covers a loaded image.
148
+ const active = loadingState === 'placeholder' && Boolean(placeholder);
149
+ const [retired, setRetired] = useState(false);
150
+ const fadeTimeoutRef = useTimeoutRef();
141
151
 
142
- const containerRef = useRef<HTMLDivElement>(null);
143
- const retryTimeoutRef = useRetryTimeout();
152
+ const retire = useCallback(() => setRetired(true), []);
144
153
 
145
- // Handle deprecated showSpinner prop
146
- const effectiveLoadingState = showSpinner ? 'spinner' : loadingState;
154
+ // The real image fades in over the placeholder, so it goes when the fade ends.
155
+ const retireAfterFade = useCallback(() => {
156
+ if (!active || !fadeIn) {
157
+ setRetired(true);
158
+ return;
159
+ }
160
+ clearTimeout(fadeTimeoutRef.current);
161
+ fadeTimeoutRef.current = setTimeout(() => setRetired(true), fadeInDuration);
162
+ }, [active, fadeIn, fadeInDuration, fadeTimeoutRef]);
163
+
164
+ return {
165
+ showPlaceholder: active && !retired,
166
+ retirePlaceholder: retire,
167
+ retirePlaceholderAfterFade: retireAfterFade,
168
+ };
169
+ };
147
170
 
171
+ /**
172
+ * The real image's state, and when it is requested: at once when not lazy,
173
+ * else the first time the container nears the viewport. The main <img> only
174
+ * ever carries the real src — a placeholder is drawn by its own element, so its
175
+ * load and error are never the image's.
176
+ */
177
+ const useImageSource = (
178
+ props: ResolvedLazyImageProps,
179
+ containerRef: React.RefObject<HTMLDivElement | null>,
180
+ ) => {
181
+ const { src, lazy } = props;
148
182
  const [state, setState] = useState<LazyImageState>({
149
183
  isLoading: true,
150
184
  hasError: false,
151
185
  isVisible: !lazy, // If not lazy, load immediately
152
186
  retryCount: 0,
153
- currentSrc: lazy ? placeholder || null : src,
187
+ currentSrc: lazy ? null : src,
154
188
  });
155
189
 
156
190
  const markVisible = useCallback(
@@ -168,18 +202,43 @@ export const useLazyImage = (props: ResolvedLazyImageProps) => {
168
202
  setState((prev) => ({ ...prev, currentSrc: src, isLoading: true, hasError: false }));
169
203
  }, [state.isVisible, src, state.currentSrc]);
170
204
 
205
+ return [state, setState] as const;
206
+ };
207
+
208
+ /**
209
+ * Owns whether the real image has been requested yet and how it went: the
210
+ * observer above, the load/error handlers, the retry timer, and how long the
211
+ * placeholder stays up.
212
+ */
213
+ export const useLazyImage = (props: ResolvedLazyImageProps) => {
214
+ const { src, loadingState, showSpinner } = props;
215
+ const { onLoad, onError, retryOnError, maxRetries, retryDelay } = props;
216
+
217
+ const containerRef = useRef<HTMLDivElement>(null);
218
+ const retryTimeoutRef = useTimeoutRef();
219
+ const [state, setState] = useImageSource(props, containerRef);
220
+
221
+ // Handle deprecated showSpinner prop
222
+ const effectiveLoadingState = showSpinner ? 'spinner' : loadingState;
223
+ const { showPlaceholder, retirePlaceholder, retirePlaceholderAfterFade } = usePlaceholderPhase(
224
+ props,
225
+ effectiveLoadingState,
226
+ );
227
+
171
228
  const handleImageLoad = useCallback(
172
229
  (event: React.SyntheticEvent<HTMLImageElement>) => {
173
230
  setState((prev) => ({ ...prev, isLoading: false, hasError: false, retryCount: 0 }));
231
+ retirePlaceholderAfterFade();
174
232
  onLoad?.(event);
175
233
  },
176
- [onLoad],
234
+ [onLoad, retirePlaceholderAfterFade],
177
235
  );
178
236
 
179
237
  const handleImageError = useCallback(
180
238
  (event: React.SyntheticEvent<HTMLImageElement>) => {
181
239
  if (!retryOnError || state.retryCount >= maxRetries) {
182
240
  setState((prev) => ({ ...prev, isLoading: false, hasError: true }));
241
+ retirePlaceholder();
183
242
  onError?.(event);
184
243
  return;
185
244
  }
@@ -192,7 +251,7 @@ export const useLazyImage = (props: ResolvedLazyImageProps) => {
192
251
  }));
193
252
  }, retryDelay);
194
253
  },
195
- [onError, retryOnError, state.retryCount, maxRetries, retryDelay, src],
254
+ [onError, retryOnError, state.retryCount, maxRetries, retryDelay, src, retirePlaceholder],
196
255
  );
197
256
 
198
257
  return {
@@ -201,8 +260,11 @@ export const useLazyImage = (props: ResolvedLazyImageProps) => {
201
260
  effectiveLoadingState,
202
261
  handleImageLoad,
203
262
  handleImageError,
263
+ handlePlaceholderError: retirePlaceholder,
204
264
  showImage: Boolean(state.currentSrc) && !state.hasError,
205
- showLoading:
206
- state.isLoading && (!state.currentSrc || effectiveLoadingState !== 'placeholder'),
265
+ // In placeholder mode the placeholder outlasts `isLoading`: it stays under
266
+ // the real image until the fade has ended.
267
+ showLoading: effectiveLoadingState === 'placeholder' ? showPlaceholder : state.isLoading,
268
+ showPlaceholder,
207
269
  };
208
270
  };
@@ -43,6 +43,24 @@ function App() {
43
43
  />
44
44
  ```
45
45
 
46
+ The placeholder is a loading state, and only `loadingState="placeholder"` draws
47
+ it — under `skeleton`, `spinner` or `none` the `placeholder` prop is ignored.
48
+
49
+ - It shows from mount, lazy or not, until the real image settles: its `load`
50
+ plus `fadeInDuration` (at once when `fadeIn` is off), or its final error once
51
+ any retries are spent.
52
+ - It sits in the box's flow and gives the box its size. The real image is drawn
53
+ over it, cropped by `objectFit`, and fades in on top; when the placeholder
54
+ goes, the real image takes the flow. So a placeholder whose shape differs
55
+ from the real image's gives the box its shape until the fade ends.
56
+ - A placeholder that fails to load is dropped at once, as if none were set.
57
+ - `onLoad` and `onError` are the real image's only: the placeholder's load
58
+ never calls `onLoad`, and its failure never calls `onError`.
59
+ - An empty `src` keeps the placeholder up; no empty `<img>` is rendered.
60
+
61
+ **Give it a width.** With no `width` the placeholder shows at its own size,
62
+ which for a low-res placeholder is usually tiny. Set one, or `width="100%"`.
63
+
46
64
  ### With Error Fallback
47
65
 
48
66
  ```tsx
@@ -170,8 +188,8 @@ wrapping element, rounded to match the image:
170
188
  | Prop | Type | Default | Description |
171
189
  |------|------|---------|-------------|
172
190
  | `lazy` | `boolean` | `true` | Enable lazy loading |
173
- | `loadingState` | `'skeleton' \| 'spinner' \| 'placeholder' \| 'none'` | `'skeleton'` | Type of loading indicator |
174
- | `placeholder` | `string` | - | Placeholder image URL |
191
+ | `loadingState` | `'skeleton' \| 'spinner' \| 'placeholder' \| 'none'` | `'skeleton'` | Type of loading indicator. `'placeholder'` shows the `placeholder` image until the real image has faded in over it; set a `width` (or `"100%"`) with it |
192
+ | `placeholder` | `string` | - | Low-res image shown while loading, only under `loadingState="placeholder"` (ignored otherwise). See [With Placeholder](#with-placeholder) |
175
193
  | `rootMargin` | `string` | `'100px'` | Intersection Observer margin |
176
194
  | `threshold` | `number \| number[]` | `0` | Intersection Observer threshold |
177
195
 
@@ -263,11 +281,13 @@ wrapping element, rounded to match the image:
263
281
  height={48}
264
282
  />
265
283
 
266
- // For hero images with low-res placeholders
284
+ // For hero images with low-res placeholders — give it a width, or the
285
+ // placeholder shows at its own, tiny, size
267
286
  <LazyImage
268
287
  src="hero-hd.jpg"
269
288
  placeholder="hero-lowres.jpg"
270
289
  alt="Hero image"
290
+ width="100%"
271
291
  loadingState="placeholder"
272
292
  />
273
293
  ```
@@ -133,40 +133,59 @@ const SpinnerIndicator: React.FC<IndicatorProps> = ({ props }) => {
133
133
  );
134
134
  };
135
135
 
136
- const PlaceholderIndicator: React.FC<IndicatorProps & { hasSrc: boolean }> = ({
136
+ /**
137
+ * The placeholder sits in the box's flow and gives the box its size: the box's
138
+ * own when one is set, the placeholder's when not. The real image is drawn over
139
+ * it (`OVER_PLACEHOLDER`) until it has faded in. Its load and error are not the
140
+ * image's, so neither reaches the caller; a failing one just retires.
141
+ */
142
+ const PlaceholderIndicator: React.FC<IndicatorProps & { onError: () => void }> = ({
137
143
  props,
138
144
  metrics,
139
- hasSrc,
145
+ onError,
140
146
  }) => {
141
- // Once a src has been chosen the real <img> is on screen, so the placeholder
142
- // would only stack behind it.
143
- if (!props.placeholder || hasSrc) return null;
147
+ if (!props.placeholder) return null;
144
148
 
145
149
  return (
146
150
  <StyledImage
147
151
  src={props.placeholder}
148
152
  alt={`${props.alt} (loading)`}
149
153
  style={metrics}
154
+ decoding={props.decoding}
155
+ loading={props.lazy ? 'lazy' : props.loading}
156
+ onError={onError}
150
157
  data-testid={`${props['data-testid']}-placeholder`}
151
158
  />
152
159
  );
153
160
  };
154
161
 
155
162
  const LoadingIndicator: React.FC<
156
- IndicatorProps & { kind: NonNullable<LazyImageProps['loadingState']>; hasSrc: boolean }
157
- > = ({ kind, hasSrc, ...rest }) => {
163
+ IndicatorProps & { kind: NonNullable<LazyImageProps['loadingState']>; onPlaceholderError: () => void }
164
+ > = ({ kind, onPlaceholderError, ...rest }) => {
158
165
  switch (kind) {
159
166
  case 'skeleton':
160
167
  return <SkeletonIndicator {...rest} />;
161
168
  case 'spinner':
162
169
  return <SpinnerIndicator {...rest} />;
163
170
  case 'placeholder':
164
- return <PlaceholderIndicator {...rest} hasSrc={hasSrc} />;
171
+ return <PlaceholderIndicator {...rest} onError={onPlaceholderError} />;
165
172
  default:
166
173
  return null;
167
174
  }
168
175
  };
169
176
 
177
+ /**
178
+ * The real image while the placeholder is up: laid over it and cropped by
179
+ * `objectFit`, so the box never grows to hold both. It takes the flow back
180
+ * when the placeholder goes.
181
+ */
182
+ const OVER_PLACEHOLDER: React.CSSProperties = {
183
+ position: 'absolute',
184
+ inset: 0,
185
+ width: '100%',
186
+ height: '100%',
187
+ };
188
+
170
189
  const ErrorFallback: React.FC<IndicatorProps> = ({ props, metrics }) => {
171
190
  const theme = useTheme();
172
191
  const { alt, fallback, width, height, borderRadius } = props;
@@ -214,8 +233,10 @@ export const LazyImage = React.memo<LazyImageProps>(function LazyImage(rawProps)
214
233
  effectiveLoadingState,
215
234
  handleImageLoad,
216
235
  handleImageError,
236
+ handlePlaceholderError,
217
237
  showImage,
218
238
  showLoading,
239
+ showPlaceholder,
219
240
  } = useLazyImage(props);
220
241
 
221
242
  const { width, height, borderRadius, alt } = props;
@@ -246,7 +267,7 @@ export const LazyImage = React.memo<LazyImageProps>(function LazyImage(rawProps)
246
267
  kind={effectiveLoadingState}
247
268
  props={props}
248
269
  metrics={metrics}
249
- hasSrc={Boolean(state.currentSrc)}
270
+ onPlaceholderError={handlePlaceholderError}
250
271
  />
251
272
  )}
252
273
 
@@ -261,7 +282,7 @@ export const LazyImage = React.memo<LazyImageProps>(function LazyImage(rawProps)
261
282
  isLoaded={!state.isLoading}
262
283
  decoding={props.decoding}
263
284
  loading={props.lazy ? 'lazy' : props.loading}
264
- style={{ ...metrics, ...props.sx }}
285
+ style={{ ...metrics, ...props.sx, ...(showPlaceholder && OVER_PLACEHOLDER) }}
265
286
  aria-label={props['aria-label'] || alt}
266
287
  aria-describedby={props['aria-describedby']}
267
288
  role={props.role}
@@ -34,7 +34,17 @@ export interface LazyImageProps extends React.ImgHTMLAttributes<HTMLImageElement
34
34
  height?: number | string;
35
35
 
36
36
  /**
37
- * Placeholder image URL to show while loading
37
+ * A low-resolution image URL shown while the real image loads. Only
38
+ * `loadingState="placeholder"` draws it; under any other loading state it is
39
+ * ignored. It shows from mount, lazy or not, until the real image has loaded
40
+ * and faded in (at once when `fadeIn` is off) or has finally failed. The real
41
+ * image is drawn over it and cropped by `objectFit`, so the box takes the
42
+ * placeholder's shape until the fade ends. A placeholder that fails to load is
43
+ * dropped, as if none were set, and is never reported to `onError`; neither
44
+ * does its load reach `onLoad`.
45
+ *
46
+ * Give LazyImage a `width` (or `"100%"`) in placeholder mode: with none, the
47
+ * placeholder shows at its own, usually tiny, size.
38
48
  */
39
49
  placeholder?: string;
40
50
 
@@ -47,8 +57,12 @@ export interface LazyImageProps extends React.ImgHTMLAttributes<HTMLImageElement
47
57
  * Loading state type
48
58
  * - 'skeleton': Shows a skeleton loader
49
59
  * - 'spinner': Shows a loading spinner
50
- * - 'placeholder': Shows placeholder image
60
+ * - 'placeholder': Shows the `placeholder` image, in the box's flow, until the
61
+ * real image has faded in over it; nothing when no `placeholder` is set.
62
+ * Set a `width` (or `"100%"`), or the placeholder shows at its own size.
51
63
  * - 'none': No loading indicator
64
+ *
65
+ * `placeholder` applies only to 'placeholder'.
52
66
  * @default 'skeleton'
53
67
  */
54
68
  loadingState?: LazyImageLoadingState;
@@ -180,7 +194,9 @@ export interface LazyImageProps extends React.ImgHTMLAttributes<HTMLImageElement
180
194
  * Inline CSS for the `<img>` (its `style`), not MUI's `sx`: theme values and
181
195
  * nested selectors such as `'&:hover'` do not apply. The image's box clips
182
196
  * anything painted outside the image, such as a shadow, so put those on a
183
- * wrapping element.
197
+ * wrapping element. While a placeholder is up (`loadingState="placeholder"`),
198
+ * the image is laid over it, so its `position`, `inset`, `width` and `height`
199
+ * are fixed until the placeholder retires; the rest applies throughout.
184
200
  */
185
201
  sx?: React.CSSProperties;
186
202
 
@@ -104,12 +104,30 @@ function escapeRegExp(value: string): string {
104
104
  return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
105
105
  }
106
106
 
107
+ /** One run of a highlighted label: plain text, or text the query matched. */
108
+ export interface HighlightSegment {
109
+ text: string;
110
+ match: boolean;
111
+ }
112
+
107
113
  /**
108
- * Wrap query matches in `<mark>` for the default suggestion renderer. Skipped for
109
- * fuzzy mode (non-contiguous matches) and empty queries. Returns HTML.
114
+ * Split `label` into the runs the query matches and the runs between them, in
115
+ * order, for the default suggestion renderer to wrap the matches in `<mark>`.
116
+ * Every occurrence of the query matches, case-insensitively and literally (regex
117
+ * metacharacters are escaped). Fuzzy mode (non-contiguous matches) and an empty
118
+ * query yield the whole label as one unmatched run.
119
+ *
120
+ * Returns TEXT, never markup: a label is caller data (in a consumer, a user's
121
+ * own display name), so the renderer must hand each run to React as a text
122
+ * child, which escapes it. Building an HTML string here and injecting it is how
123
+ * a label of `<img onerror=…>` once ran script in the host page.
110
124
  */
111
- export function highlightLabel(label: string, query: string, matchMode: MatchMode): string {
112
- if (!query || matchMode === 'fuzzy') return label;
125
+ export function highlightLabel(label: string, query: string, matchMode: MatchMode): HighlightSegment[] {
126
+ if (!query || matchMode === 'fuzzy') return [{ text: label, match: false }];
127
+ // The capturing group makes `split` keep each match, at the odd indices.
113
128
  const regex = new RegExp(`(${escapeRegExp(query)})`, 'gi');
114
- return label.replace(regex, '<mark>$1</mark>');
129
+ return label
130
+ .split(regex)
131
+ .map((text, index) => ({ text, match: index % 2 === 1 }))
132
+ .filter((segment) => segment.text !== '');
115
133
  }
@@ -194,7 +194,8 @@ export function DefaultSuggestion<T>({
194
194
  }): React.JSX.Element {
195
195
  const label = getLabel(item);
196
196
  const description = getDescription?.(item);
197
- const displayLabel = highlightLabel(label, state.query, matchMode);
197
+ // Each run is a React text child, so the label is escaped however it reads.
198
+ const segments = highlightLabel(label, state.query, matchMode);
198
199
  return (
199
200
  <Box
200
201
  sx={{
@@ -202,13 +203,26 @@ export function DefaultSuggestion<T>({
202
203
  alignItems: 'center',
203
204
  gap: 1,
204
205
  width: '100%',
205
- // Bold the matched prefix (not a yellow <mark> highlight).
206
+ // Bold every run the query matched, in any match mode but fuzzy — not
207
+ // only a prefix, even under `startsWith` (not a yellow <mark> highlight).
206
208
  '& mark': { backgroundColor: 'transparent', color: 'inherit', fontWeight: 700 },
207
209
  }}
208
210
  >
209
211
  {suggestionTypeIcon(getSuggestionType(item))}
210
212
  <ListItemText
211
- primary={<span dangerouslySetInnerHTML={{ __html: displayLabel }} />}
213
+ primary={
214
+ <span>
215
+ {segments.map((segment, index) =>
216
+ // The runs are derived from one label and never reorder, so the
217
+ // position is a stable key.
218
+ segment.match ? (
219
+ <mark key={index}>{segment.text}</mark>
220
+ ) : (
221
+ <React.Fragment key={index}>{segment.text}</React.Fragment>
222
+ ),
223
+ )}
224
+ </span>
225
+ }
212
226
  secondary={description}
213
227
  />
214
228
  </Box>