@xhsreds/reds-rn-next 0.9.1-test.1 → 0.10.0

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 (125) hide show
  1. package/coverage/.tmp/coverage-0.json +1 -1
  2. package/coverage/.tmp/coverage-1.json +1 -1
  3. package/coverage/.tmp/coverage-13.json +1 -1
  4. package/coverage/.tmp/coverage-14.json +1 -1
  5. package/coverage/.tmp/coverage-15.json +1 -1
  6. package/coverage/.tmp/coverage-16.json +1 -1
  7. package/coverage/.tmp/coverage-17.json +1 -1
  8. package/coverage/.tmp/coverage-18.json +1 -1
  9. package/coverage/.tmp/coverage-19.json +1 -1
  10. package/coverage/.tmp/coverage-2.json +1 -1
  11. package/coverage/.tmp/coverage-20.json +1 -1
  12. package/coverage/.tmp/coverage-21.json +1 -1
  13. package/coverage/.tmp/coverage-22.json +1 -1
  14. package/coverage/.tmp/coverage-23.json +1 -1
  15. package/coverage/.tmp/coverage-26.json +1 -1
  16. package/coverage/.tmp/coverage-27.json +1 -1
  17. package/coverage/.tmp/coverage-29.json +1 -1
  18. package/coverage/.tmp/coverage-3.json +1 -1
  19. package/coverage/.tmp/coverage-30.json +1 -1
  20. package/coverage/.tmp/coverage-33.json +1 -1
  21. package/coverage/.tmp/coverage-34.json +1 -1
  22. package/coverage/.tmp/coverage-35.json +1 -1
  23. package/coverage/.tmp/coverage-36.json +1 -1
  24. package/coverage/.tmp/coverage-37.json +1 -1
  25. package/coverage/.tmp/coverage-38.json +1 -1
  26. package/coverage/.tmp/coverage-39.json +1 -1
  27. package/coverage/.tmp/coverage-40.json +1 -1
  28. package/coverage/.tmp/coverage-41.json +1 -1
  29. package/coverage/.tmp/coverage-6.json +1 -1
  30. package/coverage/.tmp/coverage-7.json +1 -1
  31. package/coverage/.tmp/coverage-8.json +1 -1
  32. package/lib/cjs/_chunks/CwkpVXDI.js.map +1 -1
  33. package/lib/cjs/_chunks/{CIr5S84J.js → DUU5wf2f.js} +4 -2
  34. package/lib/cjs/_chunks/DUU5wf2f.js.map +1 -0
  35. package/lib/cjs/components/Carousel/Carousel.js +9 -2
  36. package/lib/cjs/components/Carousel/Carousel.js.map +1 -1
  37. package/lib/cjs/components/Image/Image.js +29 -92
  38. package/lib/cjs/components/Image/Image.js.map +1 -1
  39. package/lib/cjs/components/Image/VisibilitySensor.js +1 -1
  40. package/lib/cjs/components/Image/VisibilitySensor.js.map +1 -1
  41. package/lib/cjs/components/Image/index.js +4 -3
  42. package/lib/cjs/components/Image/index.js.map +1 -1
  43. package/lib/cjs/components/Image/queryCacheBatcher.js +96 -0
  44. package/lib/cjs/components/Image/queryCacheBatcher.js.map +1 -0
  45. package/lib/cjs/components/Popover/Popover.js +4 -3
  46. package/lib/cjs/components/Popover/Popover.js.map +1 -1
  47. package/lib/cjs/components/PullRefresh/PullRefresh.js +104 -40
  48. package/lib/cjs/components/PullRefresh/PullRefresh.js.map +1 -1
  49. package/lib/cjs/components/PullRefresh/component.js +1 -1
  50. package/lib/cjs/components/PullRefresh/index.js +1 -1
  51. package/lib/cjs/components/PullRefresh/interface/index.js +1 -1
  52. package/lib/cjs/components/Radio/Radio.js +3 -2
  53. package/lib/cjs/components/Radio/Radio.js.map +1 -1
  54. package/lib/cjs/components/Radio/index.js +3 -2
  55. package/lib/cjs/components/Radio/index.js.map +1 -1
  56. package/lib/cjs/components/Sheets/styles.js +2 -2
  57. package/lib/cjs/components/Sheets/styles.js.map +1 -1
  58. package/lib/cjs/components/StatusBar/hook/getStatusHeight.js +14 -1
  59. package/lib/cjs/components/StatusBar/hook/getStatusHeight.js.map +1 -1
  60. package/lib/cjs/index.js +3 -2
  61. package/lib/cjs/index.js.map +1 -1
  62. package/lib/cjs/pvCount/pvData.js +1 -1
  63. package/lib/esm/_chunks/Da9cW8JG.js.map +1 -1
  64. package/lib/esm/_chunks/{CZtagXcj.js → gC9g4Fr_.js} +4 -2
  65. package/lib/esm/_chunks/gC9g4Fr_.js.map +1 -0
  66. package/lib/esm/components/Carousel/Carousel.js +9 -2
  67. package/lib/esm/components/Carousel/Carousel.js.map +1 -1
  68. package/lib/esm/components/Image/Image.js +31 -94
  69. package/lib/esm/components/Image/Image.js.map +1 -1
  70. package/lib/esm/components/Image/VisibilitySensor.js +1 -1
  71. package/lib/esm/components/Image/VisibilitySensor.js.map +1 -1
  72. package/lib/esm/components/Image/index.js +4 -3
  73. package/lib/esm/components/Image/index.js.map +1 -1
  74. package/lib/esm/components/Image/queryCacheBatcher.js +92 -0
  75. package/lib/esm/components/Image/queryCacheBatcher.js.map +1 -0
  76. package/lib/esm/components/Popover/Popover.js +4 -3
  77. package/lib/esm/components/Popover/Popover.js.map +1 -1
  78. package/lib/esm/components/PullRefresh/PullRefresh.js +105 -41
  79. package/lib/esm/components/PullRefresh/PullRefresh.js.map +1 -1
  80. package/lib/esm/components/PullRefresh/component.js +1 -1
  81. package/lib/esm/components/PullRefresh/index.js +1 -1
  82. package/lib/esm/components/PullRefresh/interface/index.js +1 -1
  83. package/lib/esm/components/Radio/Radio.js +3 -2
  84. package/lib/esm/components/Radio/Radio.js.map +1 -1
  85. package/lib/esm/components/Radio/index.js +3 -2
  86. package/lib/esm/components/Radio/index.js.map +1 -1
  87. package/lib/esm/components/Sheets/styles.js +2 -2
  88. package/lib/esm/components/Sheets/styles.js.map +1 -1
  89. package/lib/esm/components/StatusBar/hook/getStatusHeight.js +14 -2
  90. package/lib/esm/components/StatusBar/hook/getStatusHeight.js.map +1 -1
  91. package/lib/esm/index.js +3 -2
  92. package/lib/esm/index.js.map +1 -1
  93. package/lib/esm/pvCount/pvData.js +1 -1
  94. package/lib/src/components/Image/queryCacheBatcher.d.ts +5 -0
  95. package/lib/src/components/Popover/Popover.d.ts +1 -1
  96. package/lib/src/components/Popover/interface/index.d.ts +1 -0
  97. package/lib/src/components/PullRefresh/PullRefresh.d.ts +2 -0
  98. package/lib/src/components/PullRefresh/interface/index.d.ts +5 -0
  99. package/lib/src/components/StatusBar/hook/getStatusHeight.d.ts +1 -0
  100. package/lib/types/components/Image/queryCacheBatcher.d.ts +5 -0
  101. package/lib/types/components/Popover/Popover.d.ts +1 -1
  102. package/lib/types/components/Popover/interface/index.d.ts +1 -0
  103. package/lib/types/components/PullRefresh/PullRefresh.d.ts +2 -0
  104. package/lib/types/components/PullRefresh/interface/index.d.ts +5 -0
  105. package/lib/types/components/StatusBar/hook/getStatusHeight.d.ts +1 -0
  106. package/package.json +2 -2
  107. package/src/components/Carousel/Carousel.tsx +15 -3
  108. package/src/components/Carousel/demo/index.tsx +14 -2
  109. package/src/components/Image/Image.tsx +53 -115
  110. package/src/components/Image/VisibilitySensor.tsx +1 -2
  111. package/src/components/Image/queryCacheBatcher.ts +84 -0
  112. package/src/components/Popover/Popover.tsx +4 -3
  113. package/src/components/Popover/doc/index.mdx +22 -21
  114. package/src/components/Popover/interface/index.ts +1 -0
  115. package/src/components/PullRefresh/PullRefresh.tsx +146 -55
  116. package/src/components/PullRefresh/doc/index.mdx +19 -16
  117. package/src/components/PullRefresh/interface/index.ts +6 -0
  118. package/src/components/Sheets/styles.ts +2 -2
  119. package/src/components/StatusBar/hook/getStatusHeight.ts +20 -1
  120. package/src/i18n/@types/resources.d.ts +27 -27
  121. package/src/i18n/index.json +31 -31
  122. package/src/index.ts +0 -1
  123. package/CHANGELOG.md +0 -13
  124. package/lib/cjs/_chunks/CIr5S84J.js.map +0 -1
  125. 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 ImageSourcePropTyp;
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
- const [viewable, setViewable] = useState(false);
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: viewable ? Date.now() : 0,
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 [apmDoneRef, setApmDoneRef] = useState(false);
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 (typeof RNImage.queryCache === "function" && !!imageUrl) {
130
- const uriEncodeImageUrl = encodeURI(imageUrl);
131
- RNImage.queryCache([imageUrl]).then((res) => {
132
- if (res?.[uriEncodeImageUrl]) {
133
- apmDataRef.current.isPrefetch = "true";
134
- } else {
135
- apmDataRef.current.isPrefetch = "false";
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
- const viewDuration = apmData.loadedTime - apmData.intersectTime;
148
- apmData.viewDuration = viewDuration < 0 ? 0 : viewDuration;
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
- setApmDoneRef(true);
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
- if (
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
- const apmData = apmDataRef.current;
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
- setLoadedTime(now);
207
- const loadDuration = now - apmDataRef.current.loadedStartTime;
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
- if (!abortApmCollection) {
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
- (!abortApmCollection ? (
251
- <VisibilitySensor onChange={checkIsVisible} disabled={apmDoneRef}>
252
- <RNImage
253
- {...props}
254
- // @ts-ignore
255
- source={formatSource}
256
- onLoadStart={onLoadStart}
257
- onLoad={onLoad}
258
- onProgress={onProgress}
259
- onError={onError}
260
- onLoadEnd={onLoadEnd}
261
- fadeDuration={fadeDuration}
262
- // @ts-ignore
263
- onFirstDrawFinished={onFirstDrawFinished}
264
- style={{
265
- width: width || style?.width,
266
- height: height || style?.height,
267
- borderRadius,
268
- // opacity: imageStatus === IMAGE_STATUS.LOADED ? 1 : 0,
269
- }}
270
- // @ts-ignore
271
- apmBiz={props.apmBiz}
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
- // eslint-disable-next-line react-hooks/exhaustive-deps
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: duration ?? 400,
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: duration ?? 400,
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 | 是否显示气泡 | boolean | undefined |
14
- | placement | 气泡方向 | "top-start" &#124; "top" &#124; "top-end" &#124; "bottom-start" &#124; "bottom" &#124; "bottom-end" | top |
15
- | content | 弹出内容 | ReactNode &#124; (() => ReactNode) | |
16
- | opacity | 遮罩透明度 | number | 0 |
17
- | mode | 主题色 | "light" &#124; "dark" &#124; "alwaysLight" &#124; "alwaysDark" | light |
18
- | trigger | 触发方式 | "click" &#124; "touch" | click |
19
- | duration | 固定时间后popover消失 | number | |
20
- | zIndex | 层级 | number | 1000 |
21
- | popContainerStyle | 气泡框的样式 | ViewStyle | `{}` |
22
- | closeOnClickAgain | 再次点击关闭 | boolean | true |
23
- | children | popover 作用的目标节点 | ReactNode | |
24
- | floatingStyles | floating style | ViewStyle | `{}` |
25
- | arrowStyles | arrow style | ImageStyle | `{}` |
26
- | isControl | 是否受控 | boolean | false |
27
- | offset | 气泡偏移位置 用于特殊场景适配 | Offset | `{"x":0,"y":0}` |
28
- | arrowOffset | 气泡箭头偏移位置 用于特殊场景适配 | Offset | `{"x":0,"y":0}` |
29
- | hostName | hostName | string | root |
30
- | maxWidth | popover最大宽度 | number | 280 |
31
- | layoutChange | 布局是否变化 | any | `{}` |
11
+ | 属性 | 描述 | 类型 | 默认值 |
12
+ | ------------------ | --------------------------------- | --------------------------------------------------------------------------------------------------- | --------------- |
13
+ | visible | 是否显示气泡 | boolean | undefined |
14
+ | placement | 气泡方向 | "top-start" &#124; "top" &#124; "top-end" &#124; "bottom-start" &#124; "bottom" &#124; "bottom-end" | top |
15
+ | content | 弹出内容 | ReactNode &#124; (() => ReactNode) | |
16
+ | opacity | 遮罩透明度 | number | 0 |
17
+ | mode | 主题色 | "light" &#124; "dark" &#124; "alwaysLight" &#124; "alwaysDark" | light |
18
+ | trigger | 触发方式 | "click" &#124; "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
 
@@ -12,6 +12,7 @@ export interface RedsPopover {
12
12
  mode?: "light" | "dark" | "alwaysLight" | "alwaysDark";
13
13
  trigger?: "click" | "touch";
14
14
  duration?: number;
15
+ animation_duration?: number;
15
16
  zIndex?: number;
16
17
  popContainerStyle?: ViewStyle;
17
18
  closeOnClickAgain?: boolean;