@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.
- package/dist/_internal/components/data-display/LazyImage/LazyImage.hooks.js +1 -1
- package/dist/_internal/components/data-display/LazyImage/LazyImage.js +2 -2
- package/dist/_internal/components/data-display/index.js +2 -2
- package/dist/_internal/components/form/Autocomplete/Autocomplete.helpers.js +1 -1
- package/dist/_internal/components/form/Autocomplete/Autocomplete.js +5 -5
- package/dist/_internal/components/form/Autocomplete/Autocomplete.keys.js +2 -2
- package/dist/_internal/components/form/Autocomplete/Autocomplete.parts.js +2 -2
- package/dist/_internal/components/form/Autocomplete/useAutocomplete.js +3 -3
- package/dist/_internal/components/form/index.js +5 -5
- package/dist/_internal/components/index.js +7 -7
- package/dist/{chunk-LW2LQHIM.js → chunk-BVF2BSWV.js} +20 -9
- package/dist/chunk-BVF2BSWV.js.map +1 -0
- package/dist/{chunk-PH36XEB3.js → chunk-DQZCQV2C.js} +4 -4
- package/dist/{chunk-HCT5ELST.js → chunk-GMUYCN33.js} +47 -12
- package/dist/chunk-GMUYCN33.js.map +1 -0
- package/dist/{chunk-BN742L2M.js → chunk-H6AWKLOV.js} +13 -5
- package/dist/chunk-H6AWKLOV.js.map +1 -0
- package/dist/{chunk-362OGE5J.js → chunk-HBRKA67N.js} +3 -3
- package/dist/{chunk-XBHQT4BQ.js → chunk-JXS56EW5.js} +3 -3
- package/dist/{chunk-XBHQT4BQ.js.map → chunk-JXS56EW5.js.map} +1 -1
- package/dist/{chunk-JDFTIK4L.js → chunk-PELRSZFU.js} +2 -2
- package/dist/data-display/LazyImage.js +2 -2
- package/dist/form/Autocomplete.js +5 -5
- package/dist/types/components/data-display/LazyImage/LazyImage.d.ts.map +1 -1
- package/dist/types/components/data-display/LazyImage/LazyImage.hooks.d.ts +4 -1
- package/dist/types/components/data-display/LazyImage/LazyImage.hooks.d.ts.map +1 -1
- package/dist/types/components/data-display/LazyImage/LazyImage.types.d.ts +19 -3
- package/dist/types/components/data-display/LazyImage/LazyImage.types.d.ts.map +1 -1
- package/dist/types/components/form/Autocomplete/Autocomplete.helpers.d.ts +16 -3
- package/dist/types/components/form/Autocomplete/Autocomplete.helpers.d.ts.map +1 -1
- package/dist/types/components/form/Autocomplete/Autocomplete.parts.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/data-display/LazyImage/LazyImage.hooks.ts +78 -16
- package/src/components/data-display/LazyImage/LazyImage.md +23 -3
- package/src/components/data-display/LazyImage/LazyImage.tsx +31 -10
- package/src/components/data-display/LazyImage/LazyImage.types.ts +19 -3
- package/src/components/form/Autocomplete/Autocomplete.helpers.ts +23 -5
- package/src/components/form/Autocomplete/Autocomplete.parts.tsx +17 -3
- package/dist/chunk-BN742L2M.js.map +0 -1
- package/dist/chunk-HCT5ELST.js.map +0 -1
- package/dist/chunk-LW2LQHIM.js.map +0 -1
- /package/dist/{chunk-PH36XEB3.js.map → chunk-DQZCQV2C.js.map} +0 -0
- /package/dist/{chunk-362OGE5J.js.map → chunk-HBRKA67N.js.map} +0 -0
- /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-
|
|
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-
|
|
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-
|
|
5
|
-
import "../chunk-
|
|
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-
|
|
6
|
-
import "../chunk-
|
|
7
|
-
import "../chunk-
|
|
8
|
-
import "../chunk-
|
|
9
|
-
import "../chunk-
|
|
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;
|
|
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,
|
|
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;
|
|
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
|
-
*
|
|
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":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,MAAM,MAAM,kBAAkB,GAAG,MAAM,GAAG,SAAS,GAAG,OAAO,GAAG,MAAM,GAAG,YAAY,CAAC;AACtF,MAAM,MAAM,qBAAqB,GAAG,UAAU,GAAG,SAAS,GAAG,aAAa,GAAG,MAAM,CAAC;AAEpF,MAAM,WAAW,cAAe,SAAQ,KAAK,CAAC,iBAAiB,CAAC,gBAAgB,CAAC;IAC/E;;OAEG;IACH,GAAG,EAAE,MAAM,CAAC;IAEZ;;OAEG;IACH,GAAG,EAAE,MAAM,CAAC;IAEZ;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAExB;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAEzB
|
|
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
|
-
*
|
|
24
|
-
*
|
|
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):
|
|
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
|
|
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":"AAaA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,EAAkB,KAAK,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACxE,OAAO,KAAK,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAChF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAgDtD,qFAAqF;AACrF,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,cAAc,GAAG,SAAS,GAAG,KAAK,CAAC,SAAS,CAKpF;AAYD,MAAM,WAAW,sBAAsB;IACrC,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAC;IACnD,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,OAAO,CAAC;IACnB,cAAc,EAAE,OAAO,CAAC;IACxB,IAAI,EAAE,OAAO,CAAC;IACd,gBAAgB,EAAE,MAAM,GAAG,SAAS,CAAC;IACrC,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,OAAO,CAAC;IAClB,SAAS,EAAE,OAAO,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACjC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,EAAE,CAAC,CAAC,EAAE,KAAK,CAAC,WAAW,CAAC,gBAAgB,CAAC,KAAK,IAAI,CAAC;IAC3D,SAAS,EAAE,CAAC,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC,gBAAgB,CAAC,KAAK,IAAI,CAAC;IAC9D,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,kBAAkB,EAAE,MAAM,IAAI,CAAC;IAC/B,gBAAgB,EAAE,MAAM,IAAI,CAAC;CAC9B;AAED,kEAAkE;AAClE,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,sBAAsB,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,CAqClF;AAED,2CAA2C;AAC3C,wBAAgB,QAAQ,CAAC,CAAC,EAAE,EAC1B,KAAK,EACL,MAAM,EACN,QAAQ,EACR,QAAQ,EACR,aAAa,GACd,EAAE;IACD,KAAK,EAAE,CAAC,EAAE,CAAC;IACX,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,MAAM,CAAC;IAC5B,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,MAAM,CAAC;IAC9B,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,IAAI,CAAC;IAC5B,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,CAepB;AAED,wFAAwF;AACxF,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,EACnC,IAAI,EACJ,KAAK,EACL,QAAQ,EACR,cAAc,EACd,iBAAiB,EACjB,SAAS,GACV,EAAE;IACD,IAAI,EAAE,CAAC,CAAC;IACR,KAAK,EAAE,mBAAmB,CAAC;IAC3B,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,MAAM,CAAC;IAC9B,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,MAAM,GAAG,SAAS,CAAC;IACjD,iBAAiB,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,cAAc,GAAG,SAAS,CAAC;IAC3D,SAAS,EAAE,SAAS,CAAC;CACtB,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,
|
|
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
|
@@ -115,8 +115,8 @@ const useVisibility = (
|
|
|
115
115
|
}, [lazy, src, rootMargin, threshold, isVisible, onLoadStart, onVisible, containerRef]);
|
|
116
116
|
};
|
|
117
117
|
|
|
118
|
-
/** A pending
|
|
119
|
-
const
|
|
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
|
-
*
|
|
136
|
-
*
|
|
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
|
-
|
|
139
|
-
|
|
140
|
-
|
|
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
|
|
143
|
-
const retryTimeoutRef = useRetryTimeout();
|
|
152
|
+
const retire = useCallback(() => setRetired(true), []);
|
|
144
153
|
|
|
145
|
-
//
|
|
146
|
-
const
|
|
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 ?
|
|
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
|
-
|
|
206
|
-
|
|
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` | - |
|
|
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
|
-
|
|
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
|
-
|
|
145
|
+
onError,
|
|
140
146
|
}) => {
|
|
141
|
-
|
|
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']>;
|
|
157
|
-
> = ({ kind,
|
|
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}
|
|
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
|
-
|
|
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
|
-
*
|
|
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
|
-
*
|
|
109
|
-
*
|
|
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):
|
|
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
|
|
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
|
-
|
|
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
|
|
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={
|
|
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>
|