@unif/react-native-design 0.32.2 → 0.33.1
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/lib/module/components/ui/Avatar/Avatar.js +3 -3
- package/lib/module/components/ui/Avatar/Avatar.js.map +1 -1
- package/lib/module/components/ui/DrawerHeader/DrawerHeader.js +3 -3
- package/lib/module/components/ui/DrawerHeader/DrawerHeader.js.map +1 -1
- package/lib/module/components/ui/TextField/TextFieldBase.js +7 -1
- package/lib/module/components/ui/TextField/TextFieldBase.js.map +1 -1
- package/lib/module/components/ui/TextField/constants.js +2 -1
- package/lib/module/components/ui/TextField/constants.js.map +1 -1
- package/lib/module/components/ui/TextField/styles.js +5 -0
- package/lib/module/components/ui/TextField/styles.js.map +1 -1
- package/lib/module/components/ui/TextField/useMultilineLayout.js +4 -4
- package/lib/module/components/ui/TextField/useMultilineLayout.js.map +1 -1
- package/lib/module/components/ui/TextField/useMultilineLayout.web.js +4 -4
- package/lib/module/components/ui/TextField/useMultilineLayout.web.js.map +1 -1
- package/lib/module/components/ui/Textarea/Textarea.js +7 -3
- package/lib/module/components/ui/Textarea/Textarea.js.map +1 -1
- package/lib/module/components/ui/Thumbnail/Thumbnail.js +3 -3
- package/lib/module/components/ui/Thumbnail/Thumbnail.js.map +1 -1
- package/lib/module/components/ui/shared/ImageAttempt.js.map +1 -1
- package/lib/module/components/ui/shared/selectImageAttemptSource.shared.js +6 -5
- package/lib/module/components/ui/shared/selectImageAttemptSource.shared.js.map +1 -1
- package/lib/module/components/ui/shared/useImageSource.js +11 -0
- package/lib/module/components/ui/shared/useImageSource.js.map +1 -0
- package/lib/module/utils/imageSource.js +47 -13
- package/lib/module/utils/imageSource.js.map +1 -1
- package/lib/typescript/src/components/ui/TextField/TextFieldBase.d.ts.map +1 -1
- package/lib/typescript/src/components/ui/TextField/constants.d.ts +1 -0
- package/lib/typescript/src/components/ui/TextField/constants.d.ts.map +1 -1
- package/lib/typescript/src/components/ui/TextField/styles.d.ts +5 -0
- package/lib/typescript/src/components/ui/TextField/styles.d.ts.map +1 -1
- package/lib/typescript/src/components/ui/TextField/types.d.ts +3 -0
- package/lib/typescript/src/components/ui/TextField/types.d.ts.map +1 -1
- package/lib/typescript/src/components/ui/TextField/useMultilineLayout.d.ts +1 -1
- package/lib/typescript/src/components/ui/TextField/useMultilineLayout.d.ts.map +1 -1
- package/lib/typescript/src/components/ui/TextField/useMultilineLayout.web.d.ts +1 -1
- package/lib/typescript/src/components/ui/TextField/useMultilineLayout.web.d.ts.map +1 -1
- package/lib/typescript/src/components/ui/Textarea/Textarea.d.ts +1 -1
- package/lib/typescript/src/components/ui/Textarea/Textarea.d.ts.map +1 -1
- package/lib/typescript/src/components/ui/Textarea/types.d.ts +4 -2
- package/lib/typescript/src/components/ui/Textarea/types.d.ts.map +1 -1
- package/lib/typescript/src/components/ui/shared/ImageAttempt.d.ts +3 -2
- package/lib/typescript/src/components/ui/shared/ImageAttempt.d.ts.map +1 -1
- package/lib/typescript/src/components/ui/shared/selectImageAttemptSource.shared.d.ts +3 -2
- package/lib/typescript/src/components/ui/shared/selectImageAttemptSource.shared.d.ts.map +1 -1
- package/lib/typescript/src/components/ui/shared/useImageSource.d.ts +6 -0
- package/lib/typescript/src/components/ui/shared/useImageSource.d.ts.map +1 -0
- package/lib/typescript/src/utils/imageSource.d.ts +3 -2
- package/lib/typescript/src/utils/imageSource.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/ui/Avatar/Avatar.tsx +3 -3
- package/src/components/ui/DrawerHeader/DrawerHeader.tsx +3 -3
- package/src/components/ui/TextField/TextFieldBase.tsx +13 -1
- package/src/components/ui/TextField/constants.ts +2 -1
- package/src/components/ui/TextField/styles.ts +5 -0
- package/src/components/ui/TextField/types.ts +3 -0
- package/src/components/ui/TextField/useMultilineLayout.ts +3 -3
- package/src/components/ui/TextField/useMultilineLayout.web.ts +4 -5
- package/src/components/ui/Textarea/Textarea.tsx +8 -3
- package/src/components/ui/Textarea/types.ts +4 -2
- package/src/components/ui/Thumbnail/Thumbnail.tsx +3 -3
- package/src/components/ui/shared/ImageAttempt.tsx +3 -2
- package/src/components/ui/shared/selectImageAttemptSource.shared.ts +7 -5
- package/src/components/ui/shared/useImageSource.ts +8 -0
- package/src/utils/imageSource.ts +78 -15
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { useCallback, useLayoutEffect, useState } from 'react';
|
|
2
|
-
import { TEXTAREA_VERTICAL_INSET } from './constants';
|
|
3
2
|
import type {
|
|
4
3
|
MultilineLayout,
|
|
5
4
|
MultilineLayoutInput,
|
|
@@ -12,17 +11,16 @@ export function useMultilineLayout({
|
|
|
12
11
|
value,
|
|
13
12
|
minHeight,
|
|
14
13
|
maxHeight,
|
|
14
|
+
verticalInset,
|
|
15
15
|
fontSize,
|
|
16
16
|
placeholder,
|
|
17
17
|
inputRef,
|
|
18
18
|
}: MultilineLayoutInput): MultilineLayout {
|
|
19
19
|
const [contentHeight, setContentHeight] = useState(0);
|
|
20
20
|
const [width, setWidth] = useState(0);
|
|
21
|
-
const minimum = Math.max(0, minHeight -
|
|
21
|
+
const minimum = Math.max(0, minHeight - verticalInset);
|
|
22
22
|
const maximum =
|
|
23
|
-
maxHeight === undefined
|
|
24
|
-
? Infinity
|
|
25
|
-
: Math.max(0, maxHeight - TEXTAREA_VERTICAL_INSET);
|
|
23
|
+
maxHeight === undefined ? Infinity : Math.max(0, maxHeight - verticalInset);
|
|
26
24
|
useLayoutEffect(() => {
|
|
27
25
|
if (!enabled || inputRef.current === null) return;
|
|
28
26
|
const node = inputRef.current as unknown as WebTextInputMeasurement;
|
|
@@ -43,6 +41,7 @@ export function useMultilineLayout({
|
|
|
43
41
|
placeholder,
|
|
44
42
|
minHeight,
|
|
45
43
|
maxHeight,
|
|
44
|
+
verticalInset,
|
|
46
45
|
inputRef,
|
|
47
46
|
]);
|
|
48
47
|
const onLayout = useCallback<NonNullable<MultilineLayout['onLayout']>>(
|
|
@@ -5,7 +5,7 @@ import type { TextFieldHandle } from '../TextField/types';
|
|
|
5
5
|
import type { TextareaProps } from './types';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
-
* 多行文本输入框 ——
|
|
8
|
+
* 多行文本输入框 —— 默认视觉与 Input 一致;plain 供外层已提供表面的组合使用。
|
|
9
9
|
*
|
|
10
10
|
* 跟 Input 区别:
|
|
11
11
|
* - 内置 `multiline=true`,业务不用传
|
|
@@ -17,12 +17,17 @@ import type { TextareaProps } from './types';
|
|
|
17
17
|
* Ref:forwardRef<TextFieldHandle>,业务只可调 `focus()` / `blur()`。
|
|
18
18
|
*/
|
|
19
19
|
export const Textarea = forwardRef<TextFieldHandle, TextareaProps>(
|
|
20
|
-
function Textarea(props, ref) {
|
|
20
|
+
function Textarea({ surface = 'default', ...props }, ref) {
|
|
21
21
|
const { props: safeProps } = sanitizeTextFieldWrapperProps(
|
|
22
22
|
props as TextareaProps & Record<string, unknown>
|
|
23
23
|
);
|
|
24
24
|
return (
|
|
25
|
-
<TextFieldBase
|
|
25
|
+
<TextFieldBase
|
|
26
|
+
ref={ref}
|
|
27
|
+
{...(safeProps as TextareaProps)}
|
|
28
|
+
multiline
|
|
29
|
+
plainSurface={surface === 'plain'}
|
|
30
|
+
/>
|
|
26
31
|
);
|
|
27
32
|
}
|
|
28
33
|
);
|
|
@@ -14,9 +14,11 @@ import type {
|
|
|
14
14
|
export type TextareaProps = Omit<TextInputProps, RemovedTextInputProps> &
|
|
15
15
|
TextFieldCommonProps &
|
|
16
16
|
TextFieldValueProps &
|
|
17
|
-
|
|
17
|
+
TextareaLayoutProps;
|
|
18
18
|
|
|
19
|
-
interface
|
|
19
|
+
interface TextareaLayoutProps {
|
|
20
|
+
/** default 绘制独立输入表面;plain 嵌入复合输入,保留文字内边距但不绘制表面。 */
|
|
21
|
+
surface?: 'default' | 'plain';
|
|
20
22
|
/** 最小高度,默认 96(约 5 行) */
|
|
21
23
|
minHeight?: number;
|
|
22
24
|
/** 最大高度(超过后内部滚动),默认不限制 */
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React, { useEffect } from 'react';
|
|
2
2
|
import { StyleSheet, View } from 'react-native';
|
|
3
3
|
import { useColors, useThemedStyles } from '../../../theme';
|
|
4
|
-
import {
|
|
4
|
+
import { useImageSource } from '../shared/useImageSource';
|
|
5
5
|
import { createLogger } from '../../../utils/logger';
|
|
6
6
|
import { ImageAttempt } from '../shared/ImageAttempt';
|
|
7
7
|
import { A11Y_HIDDEN_PROPS } from '../shared/a11y';
|
|
@@ -45,7 +45,7 @@ export function Thumbnail(props: ThumbnailProps): React.JSX.Element {
|
|
|
45
45
|
? { uri: uri.trim() }
|
|
46
46
|
: source
|
|
47
47
|
: undefined;
|
|
48
|
-
const resolvedSource =
|
|
48
|
+
const resolvedSource = useImageSource(sourceCandidate);
|
|
49
49
|
const sanitizedImageStyle = sanitizeThumbnailImageStyle(imageStyle);
|
|
50
50
|
const diagnostics = [
|
|
51
51
|
...normalizedDimensions.diagnostics,
|
|
@@ -94,7 +94,7 @@ export function Thumbnail(props: ThumbnailProps): React.JSX.Element {
|
|
|
94
94
|
<ImageAttempt
|
|
95
95
|
{...imageAccessibilityProps}
|
|
96
96
|
key={resolvedSource.key}
|
|
97
|
-
source={resolvedSource
|
|
97
|
+
source={resolvedSource}
|
|
98
98
|
fallback={fallbackContent}
|
|
99
99
|
style={[sanitizedImageStyle.style, StyleSheet.absoluteFill]}
|
|
100
100
|
resizeMode={resizeMode}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import React, { useCallback, useState } from 'react';
|
|
2
2
|
import { Image } from 'react-native';
|
|
3
|
-
import type { ImageProps
|
|
3
|
+
import type { ImageProps } from 'react-native';
|
|
4
|
+
import type { ResolvedImageSource } from '../../../utils/imageSource';
|
|
4
5
|
import { selectImageAttemptSource } from './selectImageAttemptSource';
|
|
5
6
|
|
|
6
7
|
type ImageAttemptProps = Omit<ImageProps, 'onError' | 'source'> & {
|
|
7
|
-
source:
|
|
8
|
+
source: ResolvedImageSource;
|
|
8
9
|
fallback: React.ReactNode;
|
|
9
10
|
};
|
|
10
11
|
|
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
import type { ImageSourcePropType } from 'react-native';
|
|
2
|
-
import {
|
|
2
|
+
import type { ResolvedImageSource } from '../../../utils/imageSource';
|
|
3
3
|
|
|
4
|
+
// Input validation belongs to the media component's resolver. Its frozen result
|
|
5
|
+
// is safe to select here without reading or serializing the original input again.
|
|
4
6
|
export function selectNativeImageAttemptSource(
|
|
5
|
-
|
|
7
|
+
resolved: ResolvedImageSource | undefined
|
|
6
8
|
): ImageSourcePropType | undefined {
|
|
7
|
-
return
|
|
9
|
+
return resolved?.source;
|
|
8
10
|
}
|
|
9
11
|
|
|
10
12
|
export function selectWebImageAttemptSource(
|
|
11
|
-
|
|
13
|
+
resolved: ResolvedImageSource | undefined
|
|
12
14
|
): ImageSourcePropType | undefined {
|
|
13
|
-
|
|
15
|
+
const source = resolved?.source;
|
|
14
16
|
|
|
15
17
|
// RNW 0.21 不解析 native 的 candidate 数组;Web 明确使用第一候选项。
|
|
16
18
|
return Array.isArray(source) ? source[0] : source;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { useMemo } from 'react';
|
|
2
|
+
import { createImageSourceResolver } from '../../../utils/imageSource';
|
|
3
|
+
|
|
4
|
+
/** Reuse immutable image inputs without retaining a global source cache. */
|
|
5
|
+
export function useImageSource(source: unknown) {
|
|
6
|
+
const resolve = useMemo(createImageSourceResolver, []);
|
|
7
|
+
return resolve(source);
|
|
8
|
+
}
|
package/src/utils/imageSource.ts
CHANGED
|
@@ -20,13 +20,14 @@ type SnapshotResult =
|
|
|
20
20
|
valid: true;
|
|
21
21
|
snapshot: SnapshotValue;
|
|
22
22
|
canonical: string;
|
|
23
|
+
children?: ReadonlyMap<string, SnapshotResult>;
|
|
23
24
|
}
|
|
24
25
|
| {
|
|
25
26
|
valid: false;
|
|
26
27
|
canonical: string;
|
|
27
28
|
};
|
|
28
29
|
|
|
29
|
-
type ResolvedImageSource = Readonly<{
|
|
30
|
+
export type ResolvedImageSource = Readonly<{
|
|
30
31
|
source: ImageSourcePropType;
|
|
31
32
|
key: string;
|
|
32
33
|
}>;
|
|
@@ -34,8 +35,12 @@ type ResolvedImageSource = Readonly<{
|
|
|
34
35
|
const hasOwn = (value: object, key: PropertyKey): boolean =>
|
|
35
36
|
Object.prototype.hasOwnProperty.call(value, key);
|
|
36
37
|
|
|
37
|
-
function valid(
|
|
38
|
-
|
|
38
|
+
function valid(
|
|
39
|
+
snapshot: SnapshotValue,
|
|
40
|
+
canonical: string,
|
|
41
|
+
children?: ReadonlyMap<string, SnapshotResult>
|
|
42
|
+
): SnapshotResult {
|
|
43
|
+
return { valid: true, snapshot, canonical, children };
|
|
39
44
|
}
|
|
40
45
|
|
|
41
46
|
function invalid(kind: string): SnapshotResult {
|
|
@@ -45,7 +50,8 @@ function invalid(kind: string): SnapshotResult {
|
|
|
45
50
|
function visitArray(
|
|
46
51
|
value: object,
|
|
47
52
|
keys: readonly string[],
|
|
48
|
-
seen: WeakSet<object
|
|
53
|
+
seen: WeakSet<object>,
|
|
54
|
+
previous?: SnapshotResult
|
|
49
55
|
): SnapshotResult {
|
|
50
56
|
if (!keys.includes('length')) return invalid('descriptor');
|
|
51
57
|
|
|
@@ -67,6 +73,12 @@ function visitArray(
|
|
|
67
73
|
const keySet = new Set(keys);
|
|
68
74
|
const parts: string[] = [];
|
|
69
75
|
const snapshot: SnapshotValue[] = [];
|
|
76
|
+
const children = new Map<string, SnapshotResult>();
|
|
77
|
+
const previousChildren = previous?.valid ? previous.children : undefined;
|
|
78
|
+
let unchanged =
|
|
79
|
+
previous?.valid === true &&
|
|
80
|
+
Array.isArray(previous.snapshot) &&
|
|
81
|
+
previousChildren?.size === length;
|
|
70
82
|
let allValid = true;
|
|
71
83
|
|
|
72
84
|
for (let index = 0; index < length; index += 1) {
|
|
@@ -82,7 +94,10 @@ function visitArray(
|
|
|
82
94
|
return invalid('descriptor');
|
|
83
95
|
}
|
|
84
96
|
|
|
85
|
-
const
|
|
97
|
+
const priorItem = previousChildren?.get(key);
|
|
98
|
+
const item = visit(descriptor.value, seen, priorItem);
|
|
99
|
+
children.set(key, item);
|
|
100
|
+
unchanged = unchanged && item === priorItem;
|
|
86
101
|
parts.push(item.canonical);
|
|
87
102
|
if (item.valid) {
|
|
88
103
|
snapshot.push(item.snapshot);
|
|
@@ -91,19 +106,29 @@ function visitArray(
|
|
|
91
106
|
}
|
|
92
107
|
}
|
|
93
108
|
|
|
109
|
+
if (allValid && unchanged && previous?.valid) return previous;
|
|
94
110
|
const canonical = `array:[${parts.join(',')}]`;
|
|
95
111
|
return allValid
|
|
96
|
-
? valid(Object.freeze(snapshot), canonical)
|
|
112
|
+
? valid(Object.freeze(snapshot), canonical, children)
|
|
97
113
|
: { valid: false, canonical };
|
|
98
114
|
}
|
|
99
115
|
|
|
100
116
|
function visitObject(
|
|
101
117
|
value: object,
|
|
102
118
|
keys: readonly string[],
|
|
103
|
-
seen: WeakSet<object
|
|
119
|
+
seen: WeakSet<object>,
|
|
120
|
+
previous?: SnapshotResult
|
|
104
121
|
): SnapshotResult {
|
|
105
122
|
const snapshot: Record<string, SnapshotValue> = {};
|
|
106
123
|
const parts: string[] = [];
|
|
124
|
+
const children = new Map<string, SnapshotResult>();
|
|
125
|
+
const previousChildren = previous?.valid ? previous.children : undefined;
|
|
126
|
+
let unchanged =
|
|
127
|
+
previous?.valid === true &&
|
|
128
|
+
previous.snapshot !== null &&
|
|
129
|
+
typeof previous.snapshot === 'object' &&
|
|
130
|
+
!Array.isArray(previous.snapshot) &&
|
|
131
|
+
previousChildren?.size === keys.length;
|
|
107
132
|
let allValid = true;
|
|
108
133
|
|
|
109
134
|
for (const key of [...keys].sort()) {
|
|
@@ -112,7 +137,10 @@ function visitObject(
|
|
|
112
137
|
if (!descriptor.enumerable) return invalid('non-enumerable');
|
|
113
138
|
if (!hasOwn(descriptor, 'value')) return invalid('accessor');
|
|
114
139
|
|
|
115
|
-
const
|
|
140
|
+
const priorItem = previousChildren?.get(key);
|
|
141
|
+
const item = visit(descriptor.value, seen, priorItem);
|
|
142
|
+
children.set(key, item);
|
|
143
|
+
unchanged = unchanged && item === priorItem;
|
|
116
144
|
parts.push(`${JSON.stringify(key)}:${item.canonical}`);
|
|
117
145
|
if (item.valid) {
|
|
118
146
|
Object.defineProperty(snapshot, key, {
|
|
@@ -126,9 +154,10 @@ function visitObject(
|
|
|
126
154
|
}
|
|
127
155
|
}
|
|
128
156
|
|
|
157
|
+
if (allValid && unchanged && previous?.valid) return previous;
|
|
129
158
|
const canonical = `object:{${parts.join(',')}}`;
|
|
130
159
|
return allValid
|
|
131
|
-
? valid(Object.freeze(snapshot), canonical)
|
|
160
|
+
? valid(Object.freeze(snapshot), canonical, children)
|
|
132
161
|
: { valid: false, canonical };
|
|
133
162
|
}
|
|
134
163
|
|
|
@@ -136,7 +165,19 @@ function visitObject(
|
|
|
136
165
|
* 只从 own data descriptor 读取一次,绝不执行 getter。snapshot 与 canonical
|
|
137
166
|
* key 因而来自同一份值;任何 descriptor / Proxy 异常都失败关闭。
|
|
138
167
|
*/
|
|
139
|
-
function visit(
|
|
168
|
+
function visit(
|
|
169
|
+
value: unknown,
|
|
170
|
+
seen: WeakSet<object>,
|
|
171
|
+
previous?: SnapshotResult
|
|
172
|
+
): SnapshotResult {
|
|
173
|
+
// Objects are always re-read through their descriptors: callers can mutate
|
|
174
|
+
// an existing source or supply a Proxy. Only observed primitive values are reused.
|
|
175
|
+
if (
|
|
176
|
+
(value === null || typeof value !== 'object') &&
|
|
177
|
+
previous?.valid &&
|
|
178
|
+
Object.is(previous.snapshot, value)
|
|
179
|
+
)
|
|
180
|
+
return previous;
|
|
140
181
|
if (value === undefined) return valid(undefined, 'undefined:');
|
|
141
182
|
if (value === null) return valid(null, 'null:');
|
|
142
183
|
|
|
@@ -173,8 +214,8 @@ function visit(value: unknown, seen: WeakSet<object>): SnapshotResult {
|
|
|
173
214
|
seen.add(value);
|
|
174
215
|
try {
|
|
175
216
|
return isArray
|
|
176
|
-
? visitArray(value, keys, seen)
|
|
177
|
-
: visitObject(value, keys, seen);
|
|
217
|
+
? visitArray(value, keys, seen, previous)
|
|
218
|
+
: visitObject(value, keys, seen, previous);
|
|
178
219
|
} finally {
|
|
179
220
|
seen.delete(value);
|
|
180
221
|
}
|
|
@@ -183,9 +224,12 @@ function visit(value: unknown, seen: WeakSet<object>): SnapshotResult {
|
|
|
183
224
|
}
|
|
184
225
|
}
|
|
185
226
|
|
|
186
|
-
function snapshotValue(
|
|
227
|
+
function snapshotValue(
|
|
228
|
+
value: unknown,
|
|
229
|
+
previous?: SnapshotResult
|
|
230
|
+
): SnapshotResult {
|
|
187
231
|
try {
|
|
188
|
-
return visit(value, new WeakSet<object>());
|
|
232
|
+
return visit(value, new WeakSet<object>(), previous);
|
|
189
233
|
} catch {
|
|
190
234
|
return invalid('exception');
|
|
191
235
|
}
|
|
@@ -273,7 +317,12 @@ function asImageSource(
|
|
|
273
317
|
export function resolveImageSource(
|
|
274
318
|
source: unknown
|
|
275
319
|
): ResolvedImageSource | undefined {
|
|
276
|
-
|
|
320
|
+
return resolveSnapshot(snapshotValue(source));
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
function resolveSnapshot(
|
|
324
|
+
result: SnapshotResult
|
|
325
|
+
): ResolvedImageSource | undefined {
|
|
277
326
|
if (!result.valid) return undefined;
|
|
278
327
|
|
|
279
328
|
const snapshot = asImageSource(result.snapshot);
|
|
@@ -285,6 +334,20 @@ export function resolveImageSource(
|
|
|
285
334
|
});
|
|
286
335
|
}
|
|
287
336
|
|
|
337
|
+
/** One resolver belongs to one mounted media component; only its latest tree is kept. */
|
|
338
|
+
export function createImageSourceResolver() {
|
|
339
|
+
let previous: SnapshotResult | undefined;
|
|
340
|
+
let resolved: ResolvedImageSource | undefined;
|
|
341
|
+
return (source: unknown): ResolvedImageSource | undefined => {
|
|
342
|
+
const next = snapshotValue(source, previous);
|
|
343
|
+
if (next !== previous) {
|
|
344
|
+
resolved = resolveSnapshot(next);
|
|
345
|
+
previous = resolved === undefined ? undefined : next;
|
|
346
|
+
}
|
|
347
|
+
return resolved;
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
|
|
288
351
|
export function imageSourceKey(
|
|
289
352
|
source: ImageSourcePropType | undefined
|
|
290
353
|
): string {
|