@xhsreds/reds-rn-next 0.9.1-test.1 → 0.10.1-beta202511041913
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/coverage/.tmp/coverage-0.json +1 -1
- package/coverage/.tmp/coverage-1.json +1 -1
- package/coverage/.tmp/coverage-10.json +1 -1
- package/coverage/.tmp/coverage-11.json +1 -1
- package/coverage/.tmp/coverage-12.json +1 -1
- package/coverage/.tmp/coverage-13.json +1 -1
- package/coverage/.tmp/coverage-14.json +1 -1
- package/coverage/.tmp/coverage-15.json +1 -1
- package/coverage/.tmp/coverage-16.json +1 -1
- package/coverage/.tmp/coverage-17.json +1 -1
- package/coverage/.tmp/coverage-18.json +1 -1
- package/coverage/.tmp/coverage-19.json +1 -1
- package/coverage/.tmp/coverage-2.json +1 -1
- package/coverage/.tmp/coverage-20.json +1 -1
- package/coverage/.tmp/coverage-21.json +1 -1
- package/coverage/.tmp/coverage-22.json +1 -1
- package/coverage/.tmp/coverage-23.json +1 -1
- package/coverage/.tmp/coverage-24.json +1 -1
- package/coverage/.tmp/coverage-25.json +1 -1
- package/coverage/.tmp/coverage-26.json +1 -1
- package/coverage/.tmp/coverage-27.json +1 -1
- package/coverage/.tmp/coverage-33.json +1 -1
- package/coverage/.tmp/coverage-35.json +1 -1
- package/coverage/.tmp/coverage-37.json +1 -1
- package/coverage/.tmp/coverage-38.json +1 -1
- package/coverage/.tmp/coverage-39.json +1 -1
- package/coverage/.tmp/coverage-4.json +1 -1
- package/coverage/.tmp/coverage-41.json +1 -1
- package/coverage/.tmp/coverage-5.json +1 -1
- package/coverage/.tmp/coverage-6.json +1 -1
- package/coverage/.tmp/coverage-7.json +1 -1
- package/coverage/.tmp/coverage-8.json +1 -1
- package/coverage/.tmp/coverage-9.json +1 -1
- package/lib/cjs/_chunks/CwkpVXDI.js.map +1 -1
- package/lib/cjs/_chunks/{CIr5S84J.js → DUU5wf2f.js} +4 -2
- package/lib/cjs/_chunks/DUU5wf2f.js.map +1 -0
- package/lib/cjs/components/Carousel/Carousel.js +9 -2
- package/lib/cjs/components/Carousel/Carousel.js.map +1 -1
- package/lib/cjs/components/Image/Image.js +29 -92
- package/lib/cjs/components/Image/Image.js.map +1 -1
- package/lib/cjs/components/Image/VisibilitySensor.js +1 -1
- package/lib/cjs/components/Image/VisibilitySensor.js.map +1 -1
- package/lib/cjs/components/Image/index.js +4 -3
- package/lib/cjs/components/Image/index.js.map +1 -1
- package/lib/cjs/components/Image/queryCacheBatcher.js +96 -0
- package/lib/cjs/components/Image/queryCacheBatcher.js.map +1 -0
- package/lib/cjs/components/Popover/Popover.js +4 -3
- package/lib/cjs/components/Popover/Popover.js.map +1 -1
- package/lib/cjs/components/PullRefresh/PullRefresh.js +104 -40
- package/lib/cjs/components/PullRefresh/PullRefresh.js.map +1 -1
- package/lib/cjs/components/PullRefresh/component.js +1 -1
- package/lib/cjs/components/PullRefresh/index.js +1 -1
- package/lib/cjs/components/PullRefresh/interface/index.js +1 -1
- package/lib/cjs/components/Radio/Radio.js +6 -5
- package/lib/cjs/components/Radio/Radio.js.map +1 -1
- package/lib/cjs/components/Radio/index.js +3 -2
- package/lib/cjs/components/Radio/index.js.map +1 -1
- package/lib/cjs/components/Radio/styles.js +4 -4
- package/lib/cjs/components/Radio/styles.js.map +1 -1
- package/lib/cjs/components/Sheets/styles.js +2 -2
- package/lib/cjs/components/Sheets/styles.js.map +1 -1
- package/lib/cjs/components/StatusBar/hook/getStatusHeight.js +14 -1
- package/lib/cjs/components/StatusBar/hook/getStatusHeight.js.map +1 -1
- package/lib/cjs/index.js +3 -2
- package/lib/cjs/index.js.map +1 -1
- package/lib/cjs/pvCount/pvData.js +1 -1
- package/lib/esm/_chunks/Da9cW8JG.js.map +1 -1
- package/lib/esm/_chunks/{CZtagXcj.js → gC9g4Fr_.js} +4 -2
- package/lib/esm/_chunks/gC9g4Fr_.js.map +1 -0
- package/lib/esm/components/Carousel/Carousel.js +9 -2
- package/lib/esm/components/Carousel/Carousel.js.map +1 -1
- package/lib/esm/components/Image/Image.js +31 -94
- package/lib/esm/components/Image/Image.js.map +1 -1
- package/lib/esm/components/Image/VisibilitySensor.js +1 -1
- package/lib/esm/components/Image/VisibilitySensor.js.map +1 -1
- package/lib/esm/components/Image/index.js +4 -3
- package/lib/esm/components/Image/index.js.map +1 -1
- package/lib/esm/components/Image/queryCacheBatcher.js +92 -0
- package/lib/esm/components/Image/queryCacheBatcher.js.map +1 -0
- package/lib/esm/components/Popover/Popover.js +4 -3
- package/lib/esm/components/Popover/Popover.js.map +1 -1
- package/lib/esm/components/PullRefresh/PullRefresh.js +105 -41
- package/lib/esm/components/PullRefresh/PullRefresh.js.map +1 -1
- package/lib/esm/components/PullRefresh/component.js +1 -1
- package/lib/esm/components/PullRefresh/index.js +1 -1
- package/lib/esm/components/PullRefresh/interface/index.js +1 -1
- package/lib/esm/components/Radio/Radio.js +6 -5
- package/lib/esm/components/Radio/Radio.js.map +1 -1
- package/lib/esm/components/Radio/index.js +3 -2
- package/lib/esm/components/Radio/index.js.map +1 -1
- package/lib/esm/components/Radio/styles.js +4 -4
- package/lib/esm/components/Radio/styles.js.map +1 -1
- package/lib/esm/components/Sheets/styles.js +2 -2
- package/lib/esm/components/Sheets/styles.js.map +1 -1
- package/lib/esm/components/StatusBar/hook/getStatusHeight.js +14 -2
- package/lib/esm/components/StatusBar/hook/getStatusHeight.js.map +1 -1
- package/lib/esm/index.js +3 -2
- package/lib/esm/index.js.map +1 -1
- package/lib/esm/pvCount/pvData.js +1 -1
- package/lib/src/components/Image/queryCacheBatcher.d.ts +5 -0
- package/lib/src/components/Popover/Popover.d.ts +1 -1
- package/lib/src/components/Popover/interface/index.d.ts +1 -0
- package/lib/src/components/PullRefresh/PullRefresh.d.ts +2 -0
- package/lib/src/components/PullRefresh/interface/index.d.ts +5 -0
- package/lib/src/components/StatusBar/hook/getStatusHeight.d.ts +1 -0
- package/lib/types/components/Image/queryCacheBatcher.d.ts +5 -0
- package/lib/types/components/Popover/Popover.d.ts +1 -1
- package/lib/types/components/Popover/interface/index.d.ts +1 -0
- package/lib/types/components/PullRefresh/PullRefresh.d.ts +2 -0
- package/lib/types/components/PullRefresh/interface/index.d.ts +5 -0
- package/lib/types/components/StatusBar/hook/getStatusHeight.d.ts +1 -0
- package/package.json +2 -2
- package/src/components/Carousel/Carousel.tsx +15 -3
- package/src/components/Carousel/demo/index.tsx +14 -2
- package/src/components/Image/Image.tsx +53 -115
- package/src/components/Image/VisibilitySensor.tsx +1 -2
- package/src/components/Image/queryCacheBatcher.ts +84 -0
- package/src/components/Popover/Popover.tsx +4 -3
- package/src/components/Popover/doc/index.mdx +22 -21
- package/src/components/Popover/interface/index.ts +1 -0
- package/src/components/PullRefresh/PullRefresh.tsx +146 -55
- package/src/components/PullRefresh/doc/index.mdx +19 -16
- package/src/components/PullRefresh/interface/index.ts +6 -0
- package/src/components/Radio/Radio.tsx +3 -3
- package/src/components/Radio/styles.ts +3 -3
- package/src/components/Sheets/styles.ts +2 -2
- package/src/components/StatusBar/hook/getStatusHeight.ts +20 -1
- package/src/index.ts +0 -1
- package/CHANGELOG.md +0 -13
- package/lib/cjs/_chunks/CIr5S84J.js.map +0 -1
- package/lib/esm/_chunks/CZtagXcj.js.map +0 -1
|
@@ -27,8 +27,8 @@ import { useColorMode } from "../ConfigProvider";
|
|
|
27
27
|
import useMounted from "../../pvCount/useUnmountedProcess";
|
|
28
28
|
import { lightColor } from "@xhs/reds-token-next";
|
|
29
29
|
import { formatUri, isDataUri, isLocalFile, convertKeysToSnakeCase } from "./utils";
|
|
30
|
-
import VisibilitySensor from "./VisibilitySensor";
|
|
31
30
|
import { apmPush } from "./apm";
|
|
31
|
+
import { queryCacheBatched } from "./queryCacheBatcher";
|
|
32
32
|
|
|
33
33
|
const imageReLoad = {
|
|
34
34
|
light: "https://picasso-static.xiaohongshu.com/fe-platform/06ae169b310c2926e541903b828486a80fcac404.png",
|
|
@@ -56,16 +56,14 @@ const Image = ({
|
|
|
56
56
|
onFirstDrawFinished,
|
|
57
57
|
...props
|
|
58
58
|
}: RedsImage & Omit<ImageProps, keyof RedsImage>) => {
|
|
59
|
-
useMounted("Image");
|
|
59
|
+
// useMounted("Image");
|
|
60
60
|
const [imageStatus, setImageStatus] = useState(IMAGE_STATUS.LOADING);
|
|
61
61
|
const [progress, setProgress] = useState(0);
|
|
62
|
-
|
|
63
|
-
const [loadedTime, setLoadedTime] = useState(0);
|
|
64
|
-
const [loadStartTime, setLoadStartTime] = useState(0);
|
|
62
|
+
const mountTimeRef = useRef<number>(Date.now());
|
|
65
63
|
|
|
66
64
|
const formatSource = useMemo(() => {
|
|
67
65
|
// @ts-ignore
|
|
68
|
-
if (_source) return _source as
|
|
66
|
+
if (_source) return _source as ImageSourcePropType;
|
|
69
67
|
// @ts-ignore
|
|
70
68
|
return { uri: src } as ImageSourcePropType;
|
|
71
69
|
}, [src, _source]);
|
|
@@ -78,21 +76,11 @@ const Image = ({
|
|
|
78
76
|
|
|
79
77
|
const blurSource = useBlur({ source: formatSource });
|
|
80
78
|
|
|
81
|
-
|
|
82
|
-
const hasInit = useRef(false);
|
|
83
|
-
const checkIsVisible = (isVisible: boolean) => {
|
|
84
|
-
if (abortApmCollection) return;
|
|
85
|
-
// 判断首屏
|
|
86
|
-
if (!hasInit.current) {
|
|
87
|
-
hasInit.current = true;
|
|
88
|
-
apmDataRef.current.isFirstScreen = isVisible ? "true" : "false";
|
|
89
|
-
}
|
|
90
|
-
setViewable(isVisible);
|
|
91
|
-
};
|
|
79
|
+
// Visibility tracking removed: no longer needed for APM
|
|
92
80
|
const apmDataRef = useRef<ImageTrackerProps>({
|
|
93
81
|
imageUrl: "",
|
|
94
82
|
imageHost: "",
|
|
95
|
-
intersectTime:
|
|
83
|
+
intersectTime: 0,
|
|
96
84
|
imageSize: 0,
|
|
97
85
|
isFirstScreen: "false",
|
|
98
86
|
isPrefetch: "",
|
|
@@ -103,7 +91,7 @@ const Image = ({
|
|
|
103
91
|
loadDuration: 0,
|
|
104
92
|
viewDuration: 0,
|
|
105
93
|
});
|
|
106
|
-
const
|
|
94
|
+
const apmDoneRef = useRef(false);
|
|
107
95
|
|
|
108
96
|
useEffect(() => {
|
|
109
97
|
if (abortApmCollection) return;
|
|
@@ -126,17 +114,19 @@ const Image = ({
|
|
|
126
114
|
useEffect(() => {
|
|
127
115
|
if (abortApmCollection) return;
|
|
128
116
|
const imageUrl = (formatSource as ImageURISource)?.uri || "";
|
|
129
|
-
if (
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
117
|
+
if (!imageUrl) return;
|
|
118
|
+
let alive = true;
|
|
119
|
+
queryCacheBatched(imageUrl)
|
|
120
|
+
.then((isPrefetched) => {
|
|
121
|
+
if (!alive) return;
|
|
122
|
+
apmDataRef.current.isPrefetch = String(isPrefetched);
|
|
123
|
+
})
|
|
124
|
+
.catch(() => {
|
|
125
|
+
if (!alive) return;
|
|
126
|
+
apmDataRef.current.isPrefetch = "false";
|
|
137
127
|
});
|
|
138
|
-
}
|
|
139
128
|
return () => {
|
|
129
|
+
alive = false;
|
|
140
130
|
apmDataRef.current.isPrefetch = "";
|
|
141
131
|
};
|
|
142
132
|
}, [formatSource, abortApmCollection]);
|
|
@@ -144,8 +134,9 @@ const Image = ({
|
|
|
144
134
|
const pushApmData = useCallback(() => {
|
|
145
135
|
if (abortApmCollection) return;
|
|
146
136
|
const apmData = apmDataRef.current;
|
|
147
|
-
|
|
148
|
-
apmData.
|
|
137
|
+
// Simplify: do not track view duration/intersect time
|
|
138
|
+
apmData.intersectTime = 0;
|
|
139
|
+
apmData.viewDuration = 0;
|
|
149
140
|
/**
|
|
150
141
|
* 如果Image.queryCache未完成时,通过imageSize来判断
|
|
151
142
|
* 如果image_size是-1,那么认为图片已有缓存
|
|
@@ -154,40 +145,19 @@ const Image = ({
|
|
|
154
145
|
if (!apmDataRef.current.isPrefetch) {
|
|
155
146
|
apmDataRef.current.isPrefetch = String((imageSize || 0) <= 0);
|
|
156
147
|
}
|
|
157
|
-
if (!apmDoneRef && apmData.imageUrl && !abortApmCollection) {
|
|
158
|
-
|
|
148
|
+
if (!apmDoneRef.current && apmData.imageUrl && !abortApmCollection) {
|
|
149
|
+
apmDoneRef.current = true;
|
|
159
150
|
apmPush(convertKeysToSnakeCase(apmData));
|
|
160
151
|
}
|
|
161
152
|
}, [abortApmCollection]);
|
|
162
153
|
|
|
163
154
|
// 判断首屏
|
|
164
|
-
|
|
165
|
-
(!apmDataRef.current.isFirstScreen || apmDataRef.current.isFirstScreen === "false") &&
|
|
166
|
-
viewable &&
|
|
167
|
-
!abortApmCollection
|
|
168
|
-
) {
|
|
169
|
-
apmDataRef.current.isFirstScreen = "true";
|
|
170
|
-
}
|
|
171
|
-
useEffect(() => {
|
|
172
|
-
if (abortApmCollection) return;
|
|
173
|
-
const apmData = apmDataRef.current;
|
|
174
|
-
if (!apmData.intersectTime && viewable) {
|
|
175
|
-
apmData.intersectTime = Date.now();
|
|
176
|
-
// 进入视窗前加载完成/失败
|
|
177
|
-
if (apmData.loadedTime || apmData.errorReason) {
|
|
178
|
-
pushApmData();
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
}, [viewable, pushApmData, abortApmCollection]);
|
|
182
|
-
|
|
155
|
+
// isFirstScreen stays "false" and intersectTime stays 0 by design
|
|
183
156
|
const onLoadEnd = () => {
|
|
184
157
|
props?.onLoadEnd?.();
|
|
185
158
|
if (abortApmCollection) return;
|
|
186
|
-
|
|
187
|
-
// 进入视窗后加载成功/失败
|
|
188
|
-
// if (apmData.intersectTime) {
|
|
159
|
+
// Push regardless of visibility/intersect time
|
|
189
160
|
pushApmData();
|
|
190
|
-
// }
|
|
191
161
|
};
|
|
192
162
|
|
|
193
163
|
const onProgress = (e: NativeSyntheticEvent<ImageProgressEventDataIOS>) => {
|
|
@@ -203,8 +173,10 @@ const Image = ({
|
|
|
203
173
|
const onLoad = (e: NativeSyntheticEvent<ImageLoadEventData>) => {
|
|
204
174
|
if (!abortApmCollection) {
|
|
205
175
|
const now = Date.now();
|
|
206
|
-
|
|
207
|
-
|
|
176
|
+
let start = apmDataRef.current.loadedStartTime || mountTimeRef.current;
|
|
177
|
+
if (start > now) start = now;
|
|
178
|
+
apmDataRef.current.loadedStartTime = start;
|
|
179
|
+
const loadDuration = now - start;
|
|
208
180
|
apmDataRef.current.loadedTime = now;
|
|
209
181
|
apmDataRef.current.loadDuration = loadDuration < 0 ? 0 : loadDuration;
|
|
210
182
|
apmDataRef.current.isSuccess = "true";
|
|
@@ -213,17 +185,8 @@ const Image = ({
|
|
|
213
185
|
setImageStatus(IMAGE_STATUS.LOADED);
|
|
214
186
|
};
|
|
215
187
|
|
|
216
|
-
useEffect(() => {
|
|
217
|
-
const loadDuration = loadedTime - loadStartTime;
|
|
218
|
-
apmDataRef.current.loadDuration = loadDuration < 0 ? 0 : loadDuration;
|
|
219
|
-
}, [loadedTime, loadStartTime]);
|
|
220
|
-
|
|
221
188
|
const onLoadStart = () => {
|
|
222
|
-
|
|
223
|
-
const now = Date.now();
|
|
224
|
-
apmDataRef.current.loadedStartTime = now;
|
|
225
|
-
setLoadStartTime(now);
|
|
226
|
-
}
|
|
189
|
+
!abortApmCollection && (apmDataRef.current.loadedStartTime = Date.now());
|
|
227
190
|
};
|
|
228
191
|
|
|
229
192
|
const onError = (e: NativeSyntheticEvent<ImageErrorEventData>) => {
|
|
@@ -246,54 +209,29 @@ const Image = ({
|
|
|
246
209
|
|
|
247
210
|
return (
|
|
248
211
|
<View style={{ borderRadius, height, width, ...(style as {}) }}>
|
|
249
|
-
{imageStatus !== IMAGE_STATUS.HIDE &&
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
></RNImage>
|
|
273
|
-
</VisibilitySensor>
|
|
274
|
-
) : (
|
|
275
|
-
<RNImage
|
|
276
|
-
{...props}
|
|
277
|
-
// @ts-ignore
|
|
278
|
-
source={formatSource}
|
|
279
|
-
onLoadStart={onLoadStart}
|
|
280
|
-
onLoad={onLoad}
|
|
281
|
-
onProgress={onProgress}
|
|
282
|
-
onError={onError}
|
|
283
|
-
onLoadEnd={onLoadEnd}
|
|
284
|
-
fadeDuration={fadeDuration}
|
|
285
|
-
// @ts-ignore
|
|
286
|
-
onFirstDrawFinished={onFirstDrawFinished}
|
|
287
|
-
style={{
|
|
288
|
-
width: width || style?.width,
|
|
289
|
-
height: height || style?.height,
|
|
290
|
-
borderRadius,
|
|
291
|
-
// opacity: imageStatus === IMAGE_STATUS.LOADED ? 1 : 0,
|
|
292
|
-
}}
|
|
293
|
-
// @ts-ignore
|
|
294
|
-
apmBiz={props.apmBiz}
|
|
295
|
-
></RNImage>
|
|
296
|
-
))}
|
|
212
|
+
{imageStatus !== IMAGE_STATUS.HIDE && (
|
|
213
|
+
<RNImage
|
|
214
|
+
{...props}
|
|
215
|
+
// @ts-ignore
|
|
216
|
+
source={formatSource}
|
|
217
|
+
onLoadStart={onLoadStart}
|
|
218
|
+
onLoad={onLoad}
|
|
219
|
+
onProgress={onProgress}
|
|
220
|
+
onError={onError}
|
|
221
|
+
onLoadEnd={onLoadEnd}
|
|
222
|
+
fadeDuration={fadeDuration}
|
|
223
|
+
// @ts-ignore
|
|
224
|
+
onFirstDrawFinished={(e) => onFirstDrawFinished(e)}
|
|
225
|
+
style={{
|
|
226
|
+
width: width || style?.width,
|
|
227
|
+
height: height || style?.height,
|
|
228
|
+
borderRadius,
|
|
229
|
+
// opacity: imageStatus === IMAGE_STATUS.LOADED ? 1 : 0,
|
|
230
|
+
}}
|
|
231
|
+
// @ts-ignore
|
|
232
|
+
apmBiz={props.apmBiz}
|
|
233
|
+
></RNImage>
|
|
234
|
+
)}
|
|
297
235
|
{imageStatus === IMAGE_STATUS.LOADING && loadStyle !== TLoadStyle.NONE && (
|
|
298
236
|
<View
|
|
299
237
|
style={{
|
|
@@ -87,8 +87,7 @@ const VisibilitySensor = forwardRef<VisibilitySensorRef, VisibilitySensorProps>(
|
|
|
87
87
|
stopWatching();
|
|
88
88
|
}
|
|
89
89
|
}
|
|
90
|
-
|
|
91
|
-
}, [rectDimensions, lastValue]);
|
|
90
|
+
}, [rectDimensions, lastValue, threshold, onChange, triggerOnce, stopWatching]);
|
|
92
91
|
return (
|
|
93
92
|
<View ref={localRef} {...rest} onLayout={() => measureInnerView()}>
|
|
94
93
|
{children}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { Image as RNImage } from "react-native";
|
|
2
|
+
|
|
3
|
+
declare const global: any;
|
|
4
|
+
|
|
5
|
+
type Resolver = (value: boolean) => void;
|
|
6
|
+
|
|
7
|
+
// Runtime switch: set global.REDS_IMAGE_QUERYCACHE_BATCHING_ENABLED = false to disable batching
|
|
8
|
+
let enabled: boolean = global?.REDS_IMAGE_QUERYCACHE_BATCHING_ENABLED !== false;
|
|
9
|
+
let FLUSH_MS = 50;
|
|
10
|
+
|
|
11
|
+
const pending = new Map<string, Resolver[]>();
|
|
12
|
+
let timer: ReturnType<typeof setTimeout> | null = null;
|
|
13
|
+
|
|
14
|
+
function directQuery(url: string): Promise<boolean> {
|
|
15
|
+
try {
|
|
16
|
+
if (typeof (RNImage as any).queryCache !== "function") {
|
|
17
|
+
return Promise.resolve(false);
|
|
18
|
+
}
|
|
19
|
+
return (RNImage as any)
|
|
20
|
+
.queryCache([url])
|
|
21
|
+
.then((res: Record<string, any>) => !!(res?.[url] || res?.[encodeURI(url)]))
|
|
22
|
+
.catch(() => false);
|
|
23
|
+
} catch (_) {
|
|
24
|
+
return Promise.resolve(false);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function flush() {
|
|
29
|
+
const entries = Array.from(pending.entries());
|
|
30
|
+
pending.clear();
|
|
31
|
+
timer = null;
|
|
32
|
+
|
|
33
|
+
const urls = entries.map(([url]) => url);
|
|
34
|
+
if (!urls.length) return;
|
|
35
|
+
|
|
36
|
+
if (typeof (RNImage as any).queryCache !== "function") {
|
|
37
|
+
entries.forEach(([, resolvers]) => resolvers.forEach((r) => r(false)));
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
(RNImage as any)
|
|
42
|
+
.queryCache(urls)
|
|
43
|
+
.then((res: Record<string, any>) => {
|
|
44
|
+
entries.forEach(([url, resolvers]) => {
|
|
45
|
+
const ok = !!(res?.[url] || res?.[encodeURI(url)]);
|
|
46
|
+
resolvers.forEach((r) => r(ok));
|
|
47
|
+
});
|
|
48
|
+
})
|
|
49
|
+
.catch(() => {
|
|
50
|
+
entries.forEach(([, resolvers]) => resolvers.forEach((r) => r(false)));
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function schedule() {
|
|
55
|
+
if (!timer) {
|
|
56
|
+
timer = setTimeout(flush, FLUSH_MS);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function setQueryCacheBatchingEnabled(v: boolean) {
|
|
61
|
+
enabled = v;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function configureQueryCacheBatching(options: { flushMs?: number } = {}) {
|
|
65
|
+
if (typeof options.flushMs === "number" && options.flushMs >= 0) {
|
|
66
|
+
FLUSH_MS = options.flushMs;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Main entry: batched query with quick toggle to direct query
|
|
71
|
+
export function queryCacheBatched(url: string): Promise<boolean> {
|
|
72
|
+
if (!url) return Promise.resolve(false);
|
|
73
|
+
if (!enabled) return directQuery(url);
|
|
74
|
+
|
|
75
|
+
return new Promise<boolean>((resolve) => {
|
|
76
|
+
const list = pending.get(url);
|
|
77
|
+
if (list) {
|
|
78
|
+
list.push(resolve);
|
|
79
|
+
} else {
|
|
80
|
+
pending.set(url, [resolve]);
|
|
81
|
+
}
|
|
82
|
+
schedule();
|
|
83
|
+
});
|
|
84
|
+
}
|
|
@@ -24,6 +24,7 @@ export default ({
|
|
|
24
24
|
arrowStyles: propsArrowStyles,
|
|
25
25
|
offset,
|
|
26
26
|
duration,
|
|
27
|
+
animation_duration,
|
|
27
28
|
hostName,
|
|
28
29
|
maxWidth,
|
|
29
30
|
arrowOffset,
|
|
@@ -121,18 +122,18 @@ export default ({
|
|
|
121
122
|
if (layout.x !== defaultValue) {
|
|
122
123
|
Animated.timing(progress, {
|
|
123
124
|
toValue: 1,
|
|
124
|
-
duration:
|
|
125
|
+
duration: animation_duration ?? 400,
|
|
125
126
|
useNativeDriver: false,
|
|
126
127
|
}).start();
|
|
127
128
|
}
|
|
128
129
|
} else {
|
|
129
130
|
Animated.timing(progress, {
|
|
130
131
|
toValue: 0,
|
|
131
|
-
duration:
|
|
132
|
+
duration: animation_duration ?? 400,
|
|
132
133
|
useNativeDriver: false,
|
|
133
134
|
}).start();
|
|
134
135
|
}
|
|
135
|
-
}, [_visible, layout]);
|
|
136
|
+
}, [_visible, layout, animation_duration]);
|
|
136
137
|
|
|
137
138
|
const onMaskChange = (event: GestureResponderEvent, value: boolean) => {
|
|
138
139
|
if (isControl) return;
|
|
@@ -8,27 +8,28 @@ import BaseDemo from "!!raw-loader!../demo/index.tsx";
|
|
|
8
8
|
|
|
9
9
|
### Props
|
|
10
10
|
|
|
11
|
-
| 属性
|
|
12
|
-
|
|
|
13
|
-
| visible
|
|
14
|
-
| placement
|
|
15
|
-
| content
|
|
16
|
-
| opacity
|
|
17
|
-
| mode
|
|
18
|
-
| trigger
|
|
19
|
-
| duration
|
|
20
|
-
|
|
|
21
|
-
|
|
|
22
|
-
|
|
|
23
|
-
|
|
|
24
|
-
|
|
|
25
|
-
|
|
|
26
|
-
|
|
|
27
|
-
|
|
|
28
|
-
|
|
|
29
|
-
|
|
|
30
|
-
|
|
|
31
|
-
|
|
|
11
|
+
| 属性 | 描述 | 类型 | 默认值 |
|
|
12
|
+
| ------------------ | --------------------------------- | --------------------------------------------------------------------------------------------------- | --------------- |
|
|
13
|
+
| visible | 是否显示气泡 | boolean | undefined |
|
|
14
|
+
| placement | 气泡方向 | "top-start" | "top" | "top-end" | "bottom-start" | "bottom" | "bottom-end" | top |
|
|
15
|
+
| content | 弹出内容 | ReactNode | (() => ReactNode) | |
|
|
16
|
+
| opacity | 遮罩透明度 | number | 0 |
|
|
17
|
+
| mode | 主题色 | "light" | "dark" | "alwaysLight" | "alwaysDark" | light |
|
|
18
|
+
| trigger | 触发方式 | "click" | "touch" | click |
|
|
19
|
+
| duration | 固定时间后popover消失 | number | |
|
|
20
|
+
| animation_duration | animation_duration 动画时间 | number | |
|
|
21
|
+
| zIndex | 层级 | number | 1000 |
|
|
22
|
+
| popContainerStyle | 气泡框的样式 | ViewStyle | `{}` |
|
|
23
|
+
| closeOnClickAgain | 再次点击关闭 | boolean | true |
|
|
24
|
+
| children | popover 作用的目标节点 | ReactNode | |
|
|
25
|
+
| floatingStyles | floating style | ViewStyle | `{}` |
|
|
26
|
+
| arrowStyles | arrow style | ImageStyle | `{}` |
|
|
27
|
+
| isControl | 是否受控 | boolean | false |
|
|
28
|
+
| offset | 气泡偏移位置 用于特殊场景适配 | Offset | `{"x":0,"y":0}` |
|
|
29
|
+
| arrowOffset | 气泡箭头偏移位置 用于特殊场景适配 | Offset | `{"x":0,"y":0}` |
|
|
30
|
+
| hostName | hostName | string | root |
|
|
31
|
+
| maxWidth | popover最大宽度 | number | 280 |
|
|
32
|
+
| layoutChange | 布局是否变化 | any | `{}` |
|
|
32
33
|
|
|
33
34
|
### Events
|
|
34
35
|
|