react-native-refresh-list2 1.0.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 (95) hide show
  1. package/LICENSE +20 -0
  2. package/README.md +67 -0
  3. package/lib/module/Loading/Loading.js +19 -0
  4. package/lib/module/Loading/Loading.js.map +1 -0
  5. package/lib/module/Loading/index.js +5 -0
  6. package/lib/module/Loading/index.js.map +1 -0
  7. package/lib/module/RefreshControl/BottomContainer.js +88 -0
  8. package/lib/module/RefreshControl/BottomContainer.js.map +1 -0
  9. package/lib/module/RefreshControl/NormalControl.js +94 -0
  10. package/lib/module/RefreshControl/NormalControl.js.map +1 -0
  11. package/lib/module/RefreshControl/RefreshContainer.js +53 -0
  12. package/lib/module/RefreshControl/RefreshContainer.js.map +1 -0
  13. package/lib/module/RefreshControl/RefreshContext.js +32 -0
  14. package/lib/module/RefreshControl/RefreshContext.js.map +1 -0
  15. package/lib/module/RefreshControl/RefreshFlatList.js +291 -0
  16. package/lib/module/RefreshControl/RefreshFlatList.js.map +1 -0
  17. package/lib/module/RefreshControl/RefreshScrollView.js +279 -0
  18. package/lib/module/RefreshControl/RefreshScrollView.js.map +1 -0
  19. package/lib/module/RefreshControl/index.js +7 -0
  20. package/lib/module/RefreshControl/index.js.map +1 -0
  21. package/lib/module/RefreshControl/type.js +39 -0
  22. package/lib/module/RefreshControl/type.js.map +1 -0
  23. package/lib/module/icon/CommentIcon.js +32 -0
  24. package/lib/module/icon/CommentIcon.js.map +1 -0
  25. package/lib/module/icon/Icon.js +25 -0
  26. package/lib/module/icon/Icon.js.map +1 -0
  27. package/lib/module/icon/MoreIcon.js +40 -0
  28. package/lib/module/icon/MoreIcon.js.map +1 -0
  29. package/lib/module/icon/Praise.js +23 -0
  30. package/lib/module/icon/Praise.js.map +1 -0
  31. package/lib/module/icon/SearchIcon.js +35 -0
  32. package/lib/module/icon/SearchIcon.js.map +1 -0
  33. package/lib/module/icon/index.js +5 -0
  34. package/lib/module/icon/index.js.map +1 -0
  35. package/lib/module/icon/library.js +20 -0
  36. package/lib/module/icon/library.js.map +1 -0
  37. package/lib/module/index.js +4 -0
  38. package/lib/module/index.js.map +1 -0
  39. package/lib/module/package.json +1 -0
  40. package/lib/typescript/package.json +1 -0
  41. package/lib/typescript/src/Loading/Loading.d.ts +9 -0
  42. package/lib/typescript/src/Loading/Loading.d.ts.map +1 -0
  43. package/lib/typescript/src/Loading/index.d.ts +3 -0
  44. package/lib/typescript/src/Loading/index.d.ts.map +1 -0
  45. package/lib/typescript/src/RefreshControl/BottomContainer.d.ts +17 -0
  46. package/lib/typescript/src/RefreshControl/BottomContainer.d.ts.map +1 -0
  47. package/lib/typescript/src/RefreshControl/NormalControl.d.ts +20 -0
  48. package/lib/typescript/src/RefreshControl/NormalControl.d.ts.map +1 -0
  49. package/lib/typescript/src/RefreshControl/RefreshContainer.d.ts +17 -0
  50. package/lib/typescript/src/RefreshControl/RefreshContainer.d.ts.map +1 -0
  51. package/lib/typescript/src/RefreshControl/RefreshContext.d.ts +9 -0
  52. package/lib/typescript/src/RefreshControl/RefreshContext.d.ts.map +1 -0
  53. package/lib/typescript/src/RefreshControl/RefreshFlatList.d.ts +20 -0
  54. package/lib/typescript/src/RefreshControl/RefreshFlatList.d.ts.map +1 -0
  55. package/lib/typescript/src/RefreshControl/RefreshScrollView.d.ts +20 -0
  56. package/lib/typescript/src/RefreshControl/RefreshScrollView.d.ts.map +1 -0
  57. package/lib/typescript/src/RefreshControl/index.d.ts +5 -0
  58. package/lib/typescript/src/RefreshControl/index.d.ts.map +1 -0
  59. package/lib/typescript/src/RefreshControl/type.d.ts +70 -0
  60. package/lib/typescript/src/RefreshControl/type.d.ts.map +1 -0
  61. package/lib/typescript/src/icon/CommentIcon.d.ts +3 -0
  62. package/lib/typescript/src/icon/CommentIcon.d.ts.map +1 -0
  63. package/lib/typescript/src/icon/Icon.d.ts +10 -0
  64. package/lib/typescript/src/icon/Icon.d.ts.map +1 -0
  65. package/lib/typescript/src/icon/MoreIcon.d.ts +3 -0
  66. package/lib/typescript/src/icon/MoreIcon.d.ts.map +1 -0
  67. package/lib/typescript/src/icon/Praise.d.ts +9 -0
  68. package/lib/typescript/src/icon/Praise.d.ts.map +1 -0
  69. package/lib/typescript/src/icon/SearchIcon.d.ts +9 -0
  70. package/lib/typescript/src/icon/SearchIcon.d.ts.map +1 -0
  71. package/lib/typescript/src/icon/index.d.ts +3 -0
  72. package/lib/typescript/src/icon/index.d.ts.map +1 -0
  73. package/lib/typescript/src/icon/library.d.ts +17 -0
  74. package/lib/typescript/src/icon/library.d.ts.map +1 -0
  75. package/lib/typescript/src/index.d.ts +2 -0
  76. package/lib/typescript/src/index.d.ts.map +1 -0
  77. package/package.json +168 -0
  78. package/src/Loading/Loading.tsx +15 -0
  79. package/src/Loading/index.tsx +5 -0
  80. package/src/RefreshControl/BottomContainer.tsx +112 -0
  81. package/src/RefreshControl/NormalControl.tsx +118 -0
  82. package/src/RefreshControl/RefreshContainer.tsx +74 -0
  83. package/src/RefreshControl/RefreshContext.tsx +30 -0
  84. package/src/RefreshControl/RefreshFlatList.tsx +372 -0
  85. package/src/RefreshControl/RefreshScrollView.tsx +359 -0
  86. package/src/RefreshControl/index.tsx +5 -0
  87. package/src/RefreshControl/type.ts +74 -0
  88. package/src/icon/CommentIcon.tsx +29 -0
  89. package/src/icon/Icon.tsx +26 -0
  90. package/src/icon/MoreIcon.tsx +38 -0
  91. package/src/icon/Praise.tsx +21 -0
  92. package/src/icon/SearchIcon.tsx +36 -0
  93. package/src/icon/index.tsx +3 -0
  94. package/src/icon/library.ts +30 -0
  95. package/src/index.tsx +1 -0
@@ -0,0 +1,359 @@
1
+ // RefreshFlatList.tsx
2
+
3
+ import React, { useCallback, useEffect, useRef } from 'react';
4
+ import { Dimensions, Platform, type ScrollViewProps } from 'react-native';
5
+ import { Gesture, GestureDetector, GestureHandlerRootView } from 'react-native-gesture-handler';
6
+ import Animated, {
7
+ runOnJS,
8
+ interpolate,
9
+ useAnimatedScrollHandler,
10
+ withTiming,
11
+ useAnimatedProps,
12
+ useAnimatedStyle,
13
+ useSharedValue,
14
+ useDerivedValue,
15
+ withSequence,
16
+ withDelay,
17
+ Easing,
18
+ useAnimatedReaction, type SharedValue,
19
+ } from 'react-native-reanimated';
20
+ import { RefreshContainerContext, RefreshStatus } from './type';
21
+ import RefreshContainer from './RefreshContainer';
22
+ // 假设您不需要单独的 BottomContainer 组件,而是将加载更多内容放在 ListFooterComponent 中
23
+
24
+ // ------------------- 常量定义 -------------------
25
+ const { height } = Dimensions.get('window');
26
+
27
+ const MAX_SCROLL_VELOCITY_Y = 20;
28
+ const MIN_SCROLL_VELOCITY_Y = 0.5;
29
+ const DEFAULT_TRIGGLE_HEIGHT = 100;
30
+ const RESET_TIMING_EASING = Easing.bezier(0.33, 1, 0.68, 1);
31
+ const MAX_BOUNCE_DISTANCE = 100
32
+
33
+ // ------------------- Props 定义 -------------------
34
+ interface RefreshFlatListProps extends ScrollViewProps {
35
+ refreshing: boolean;
36
+ refreshComponent: () => React.ReactNode;
37
+ onRefresh: () => void;
38
+ onScroll?: (event: any) => void;
39
+ handleOnLoadMore?: () => void;
40
+ triggleHeight?: number;
41
+ canOffset?: boolean;
42
+ bounces?: boolean;
43
+ offsetY?: number
44
+ children: React.ReactNode;
45
+ scrollViewParentTransitionY?: SharedValue<number>;
46
+ scrollParentRef?: any,
47
+ }
48
+
49
+ const RefreshScrollList = (props: RefreshFlatListProps) => {
50
+ const {
51
+ refreshing,
52
+ onRefresh,
53
+ refreshComponent,
54
+ triggleHeight = DEFAULT_TRIGGLE_HEIGHT,
55
+ canOffset = true,
56
+ bounces = true,
57
+ onScroll: onScrollHandler,
58
+ children,
59
+ offsetY = 0,
60
+ scrollViewParentTransitionY,
61
+ scrollParentRef,
62
+ ...restProps
63
+ } = props;
64
+
65
+ // ------------------- Shared Values 和 Refs -------------------
66
+ const panRef = useRef<any>(null);
67
+ const internalScrollRef = useRef<any>(null);
68
+ const scrollRef = scrollParentRef || internalScrollRef;
69
+
70
+ // @ts-ignore
71
+ const nativeRef: any = useRef();
72
+ const nativeGesture = Gesture.Native().withRef(nativeRef);
73
+ const internalScrollViewParentTransitionY = useSharedValue(0);
74
+ const scrollViewTransitionY = scrollViewParentTransitionY || internalScrollViewParentTransitionY;
75
+ const refreshTransitionY = useSharedValue(0);
76
+ const offset = useSharedValue(0);
77
+ const scrollBounse = useSharedValue(false);
78
+ const refreshStatus = useSharedValue<RefreshStatus>(RefreshStatus.Idle);
79
+ const scrollViewTotalHeight = useSharedValue(0);
80
+ const scrollBeginTop = useSharedValue(0)
81
+
82
+ // ------------------- Derived Values -------------------
83
+ const direction = useDerivedValue(() => {
84
+ return refreshTransitionY.value > 0 ? 1 : -1;
85
+ }, [refreshTransitionY]);
86
+
87
+ const canRefresh = useDerivedValue(() => {
88
+ const marginTop = scrollViewTransitionY.value;
89
+ // 核心:判断是否触顶 (下拉) 或触底 (上拉/加载更多)
90
+ return marginTop < 1
91
+ });
92
+
93
+ useAnimatedReaction(
94
+ () => refreshStatus.value,
95
+ () => {
96
+ }
97
+ );
98
+
99
+ // ------------------- Refreshing Side Effect -------------------
100
+ useEffect(() => {
101
+ if (refreshing) {
102
+ if (direction.value === 1) {
103
+ refreshStatus.value = RefreshStatus.Holding;
104
+ // 刷新中保持指示器在触发高度
105
+ refreshTransitionY.value = withTiming(triggleHeight * direction.value);
106
+ } else if (refreshStatus.value === RefreshStatus.Idle) {
107
+ // scrollRef.current?.scrollTo({
108
+ // y: 0,
109
+ // animated: false,
110
+ // });
111
+ refreshStatus.value = RefreshStatus.Holding;
112
+ refreshTransitionY.value = withTiming(triggleHeight);
113
+ }
114
+ } else if (refreshStatus.value !== RefreshStatus.Idle) {
115
+ if (direction.value === 1) {
116
+ // 下拉刷新完成,收起指示器
117
+ refreshStatus.value = RefreshStatus.Done;
118
+ refreshTransitionY.value = withDelay(
119
+ 500,
120
+ withTiming(
121
+ 0,
122
+ {
123
+ easing: RESET_TIMING_EASING,
124
+ },
125
+ () => {
126
+ refreshStatus.value = RefreshStatus.Idle;
127
+ }
128
+ )
129
+ );
130
+ }
131
+ }
132
+ }, [refreshing, direction.value, refreshStatus, refreshTransitionY, triggleHeight]);
133
+
134
+ const handleOnRefresh = useCallback(() => {
135
+ if (refreshing) return;
136
+ onRefresh && onRefresh();
137
+ }, [refreshing, onRefresh]);
138
+
139
+ // ------------------- Scroll Handler -------------------
140
+ const onScroll = useAnimatedScrollHandler<{
141
+ scrollBeginTime: number;
142
+ scrollBeginY: number;
143
+ }>({
144
+ onBeginDrag: (event, context) => {
145
+ context.scrollBeginTime = new Date().valueOf();
146
+ context.scrollBeginY = event.contentOffset.y;
147
+ },
148
+ onScroll: (event, context) => {
149
+ onScrollHandler && runOnJS(onScrollHandler)({
150
+ nativeEvent: event,
151
+ });
152
+ const { scrollBeginY, scrollBeginTime } = context;
153
+ scrollViewTransitionY.value = event.contentOffset.y;
154
+ // 滚动边界反弹动画逻辑
155
+ const marginTop = scrollViewTransitionY.value;
156
+
157
+ if (marginTop === 0 && !scrollBounse.value) {
158
+
159
+ const bounceDirection = marginTop === 0 ? 1 : -1;
160
+ const endTime = new Date().valueOf();
161
+ const velocityY = Math.min(
162
+ Math.abs(
163
+ (scrollViewTransitionY.value - scrollBeginY) /
164
+ (endTime - scrollBeginTime)
165
+ ),
166
+ MAX_SCROLL_VELOCITY_Y
167
+ );
168
+ if (!bounces || velocityY < MIN_SCROLL_VELOCITY_Y) return;
169
+
170
+ const ratio = (Math.PI / 2 / MAX_SCROLL_VELOCITY_Y) * velocityY;
171
+ const bounceDistance = (height / 4) * Math.sin(ratio);
172
+ const duration = 100 + 100 * Math.sin(ratio * 2);
173
+
174
+ scrollBounse.value = true;
175
+ refreshTransitionY.value = withSequence(
176
+ withTiming(bounceDistance * bounceDirection, { duration }),
177
+ withTiming(
178
+ 0,
179
+ {
180
+ duration,
181
+ easing: RESET_TIMING_EASING,
182
+ },
183
+ () => {
184
+ scrollBounse.value = false;
185
+ }
186
+ )
187
+ );
188
+ }
189
+ }
190
+ });
191
+
192
+
193
+ const scrollToTopJS = useCallback(() => {
194
+ // 检查 ref 是否存在
195
+ if (scrollRef.current) {
196
+ (scrollRef.current as any).scrollTo({
197
+ y: 0,
198
+ animated: false,
199
+ });
200
+ }
201
+ }, [scrollRef]);
202
+
203
+ // ------------------- Pan Gesture -------------------
204
+ const panGesture = Gesture.Pan()
205
+ .withRef(panRef)
206
+ .activeOffsetY(10)
207
+ .simultaneousWithExternalGesture(nativeRef)
208
+ // 移除 activeOffsetY 或设置为极小值,让优先级完全由 requireExternalGestureToFail 决定
209
+ // 关键:与原生滚动同时进行,以便在触顶时接管
210
+ .onBegin(() => {
211
+ offset.value = refreshTransitionY.value;
212
+ scrollBeginTop.value = scrollViewTransitionY.value;
213
+ })
214
+ .onUpdate(({ translationY }) => {
215
+ const shouldAllowModify = canRefresh.value || (refreshStatus.value !== RefreshStatus.Idle && (translationY - scrollBeginTop.value) >= 0);
216
+ if (!shouldAllowModify) {
217
+ refreshTransitionY.value = 0;
218
+ // 如果不在触顶区域且未处于刷新状态,则不修改位移
219
+ return;
220
+ }
221
+
222
+ if (Platform.OS !== 'ios') runOnJS(scrollToTopJS)()
223
+
224
+ let nextY =
225
+ offset.value +
226
+ interpolate(
227
+ (translationY - scrollBeginTop.value),
228
+ [0, height],
229
+ [0, height / 2],
230
+ // 确保超过 height 时不会失控,但这里通常不需要
231
+ );
232
+
233
+ refreshTransitionY.value = Math.min(nextY, MAX_BOUNCE_DISTANCE);
234
+
235
+ if (!refreshing) {
236
+ if (Math.abs(refreshTransitionY.value) >= triggleHeight) {
237
+ refreshStatus.value = RefreshStatus.Reached;
238
+ } else {
239
+ refreshStatus.value = RefreshStatus.Pulling;
240
+ }
241
+ }
242
+ })
243
+ .onEnd(() => {
244
+ if (refreshing) {
245
+ if (refreshTransitionY.value >= triggleHeight * direction.value) {
246
+ refreshTransitionY.value = withTiming(
247
+ triggleHeight * direction.value
248
+ );
249
+ } else {
250
+ ``
251
+ refreshTransitionY.value = withTiming(0);
252
+ }
253
+ return;
254
+ }
255
+ if (Math.abs(refreshTransitionY.value) >= triggleHeight) {
256
+ if (refreshTransitionY.value > 0) {
257
+ runOnJS(handleOnRefresh)();
258
+ }
259
+ } else {
260
+ refreshTransitionY.value = withTiming(
261
+ 0,
262
+ {
263
+ easing: RESET_TIMING_EASING,
264
+ },
265
+ () => {
266
+ refreshStatus.value = RefreshStatus.Idle;
267
+ }
268
+ );
269
+ }
270
+
271
+ })
272
+
273
+ // ------------------- Animated Styles and Props -------------------
274
+ const animatedStyle = useAnimatedStyle(() => {
275
+ // 🌟 核心修复:这个 style 将应用于外部 Animated.View,实现整个列表的平移
276
+ return {
277
+ transform: [
278
+ {
279
+ translateY: canOffset ? refreshTransitionY.value : 0,
280
+ },
281
+ ],
282
+ };
283
+ });
284
+
285
+ const animatedScrollEnabled = useDerivedValue(() => {
286
+ if (Platform.OS === 'ios') return true
287
+ return refreshTransitionY.value <= 0;
288
+ }, [refreshTransitionY]);
289
+
290
+ const animatedProps = useAnimatedProps(() => {
291
+ const top = -refreshTransitionY.value;
292
+ return {
293
+ scrollEnabled: animatedScrollEnabled.value,
294
+ scrollIndicatorInsets: {
295
+ top: top - 1,
296
+ left: 0,
297
+ bottom: 0,
298
+ right: 0,
299
+ }
300
+ };
301
+ });
302
+
303
+ const derivedTransitionY = useDerivedValue(() => {
304
+ return refreshTransitionY.value + offsetY;
305
+ }, [offsetY]); // 依赖 offsetY,如果 offsetY 变化,重新计算
306
+
307
+ // ------------------- JSX 渲染 -------------------
308
+ return (
309
+ <GestureHandlerRootView style={{
310
+ flex: 1
311
+ }}>
312
+ <RefreshContainerContext.Provider
313
+ value={{
314
+ transitionY: refreshTransitionY,
315
+ scrollBounse,
316
+ triggleHeight,
317
+ refreshing,
318
+ refreshStatus,
319
+ direction,
320
+ canRefresh,
321
+ }}
322
+ >
323
+ <GestureDetector gesture={panGesture}>
324
+ {/* 🌟 核心修复:使用 Animated.View 包裹 FlatList 并应用 animatedStyle */}
325
+ <GestureDetector gesture={nativeGesture}>
326
+ {/* @ts-ignore */}
327
+ <Animated.ScrollView
328
+ ref={scrollRef}
329
+ bounces={false}
330
+ overScrollMode={'never'}
331
+ scrollEventThrottle={16}
332
+ onScroll={onScroll}
333
+ animatedProps={animatedProps}
334
+ // 移除 style={animatedStyle}
335
+ onContentSizeChange={(_: number, h: number) => {
336
+ scrollViewTotalHeight.value = h;
337
+ }}
338
+ {...restProps}
339
+ style={[{ flex: 1 }, animatedStyle]} // 合并样式
340
+ >
341
+ {children}
342
+ </Animated.ScrollView>
343
+ </GestureDetector>
344
+ </GestureDetector>
345
+ {/* RefreshContainer 必须在列表之上,使用绝对定位 */}
346
+ <RefreshContainer
347
+ offsetTop={offsetY}
348
+ refreshStatus={refreshStatus}
349
+ transitionY={derivedTransitionY}
350
+ triggleHeight={triggleHeight + (offsetY)}
351
+ >
352
+ {refreshComponent && refreshComponent()}
353
+ </RefreshContainer>
354
+ </RefreshContainerContext.Provider>
355
+ </GestureHandlerRootView>
356
+ );
357
+ };
358
+
359
+ export default RefreshScrollList;
@@ -0,0 +1,5 @@
1
+ import NormalControl from './NormalControl';
2
+ import RefreshScrollView from './RefreshScrollView';
3
+ import RefreshFlatList from './RefreshFlatList';
4
+
5
+ export { RefreshScrollView, NormalControl, RefreshFlatList };
@@ -0,0 +1,74 @@
1
+ import { createContext, useContext } from 'react';
2
+ import { type SharedValue, type DerivedValue } from 'react-native-reanimated';
3
+
4
+ interface SkeletonContextProps {
5
+ /**
6
+ * Refresh Container transitionY
7
+ */
8
+ transitionY: SharedValue<number>;
9
+ /**
10
+ * ScrollView scroll to the top with velocity. It can continue scroll a little, scrollBounse is a marker to mark the progress
11
+ * If True, scrollView is bounceing, and refresh animation will not triggle
12
+ */
13
+ scrollBounse: SharedValue<boolean>;
14
+ /**
15
+ * Only transitionY bigger than triggleHeight, refresh animation will triggle
16
+ */
17
+ triggleHeight: number;
18
+ /**
19
+ * current RefreshContainer is refreshing
20
+ */
21
+ refreshing: boolean;
22
+ /**
23
+ * RefreshStatus by transitionY
24
+ * When transitionY.value reach some point, it will change
25
+ */
26
+ refreshStatus: SharedValue<RefreshStatus>;
27
+ /**
28
+ * ScrollView pulling direction
29
+ * 1: down
30
+ * -1: up
31
+ * */
32
+ direction: DerivedValue<1 | -1>;
33
+ /**
34
+ * inside scrollView wheather reach boundary
35
+ */
36
+ canRefresh: DerivedValue<boolean>;
37
+ }
38
+
39
+ export const RefreshContainerContext = createContext<SkeletonContextProps>(
40
+ {} as SkeletonContextProps
41
+ );
42
+ export const useRefreshScroll = () => useContext(RefreshContainerContext);
43
+
44
+ /**
45
+ * Once Refresh LifeCycle:
46
+ * Idle -> Pulling -> Idle: Not reach triggleHeight, fail to refresh
47
+ * Idle -> Pulling -> Reached -> Holding -> Done -> Idle: A compelete refresh
48
+ */
49
+ export enum RefreshStatus {
50
+ /**
51
+ * Refresh normal status
52
+ */
53
+ Idle,
54
+ /**
55
+ * Refresh is pulling down, and not reach triggleHeight
56
+ */
57
+ Pulling,
58
+ /**
59
+ * Refresh is pulling down continue, and reached triggleHeight
60
+ */
61
+ Reached,
62
+ /**
63
+ * Refresh is Refreshing
64
+ */
65
+ Holding,
66
+ /**
67
+ * Refresh is done
68
+ */
69
+ Done,
70
+ /**
71
+ * ScrollView Auto Load More
72
+ */
73
+ AutoLoad,
74
+ }
@@ -0,0 +1,29 @@
1
+ import Svg, {Defs, ClipPath, Rect, G, Path} from "react-native-svg";
2
+ /* SVGR has dropped some elements not supported by react-native-svg: style */
3
+ const SVGComponent = (props: any) => (
4
+ <Svg
5
+ xmlns="http://www.w3.org/2000/svg"
6
+ xmlnsXlink="http://www.w3.org/1999/xlink"
7
+ width={14}
8
+ height={14}
9
+ viewBox="0 0 14 14"
10
+ {...props}
11
+ >
12
+ <Defs>
13
+ <ClipPath id="a">
14
+ <Rect
15
+ width={14}
16
+ height={14}
17
+ transform="translate(126.35 43.8)"
18
+ />
19
+ </ClipPath>
20
+ </Defs>
21
+ <G className="b" transform="translate(-126.35 -43.8)">
22
+ <Path
23
+ d="M42.26.643H33.389a1.949,1.949,0,0,0-1.948,1.948V8.929a1.948,1.948,0,0,0,1.948,1.947h2.1a.139.139,0,0,1,.106.046l1.168,1.17a1.532,1.532,0,0,0,1.058.423,1.464,1.464,0,0,0,1.039-.425l1.167-1.167a.137.137,0,0,1,.107-.047h2.1A1.991,1.991,0,0,0,44.2,8.929V2.591A1.95,1.95,0,0,0,42.26.643M37.507,11.325,36.34,10.159a1.176,1.176,0,0,0-.877-.376h-2.1a.856.856,0,0,1-.854-.854V2.591a.856.856,0,0,1,.854-.854H42.24a.856.856,0,0,1,.854.854V8.929a.856.856,0,0,1-.854.854H40.09a1.271,1.271,0,0,0-.874.374l-1.167,1.168a.373.373,0,0,1-.527.016l-.015-.016"
24
+ transform="translate(95.559 44.357)"
25
+ />
26
+ </G>
27
+ </Svg>
28
+ );
29
+ export default SVGComponent;
@@ -0,0 +1,26 @@
1
+ import React from 'react';
2
+ import Svg, { Path } from 'react-native-svg';
3
+ import { Library } from './library';
4
+
5
+ interface IconProps {
6
+ name: keyof typeof Library;
7
+ size?: number;
8
+ color?: string;
9
+ }
10
+
11
+ const Icon: React.FC<IconProps> = (props) => {
12
+ const { name, size = 20, color = 'black' } = props;
13
+
14
+ return (
15
+ <Svg
16
+ testID="MAUI-ICON-ID"
17
+ width={size}
18
+ height={size}
19
+ viewBox="0 0 1024 1024"
20
+ >
21
+ <Path d={Library[name]} fill={color} />
22
+ </Svg>
23
+ );
24
+ };
25
+
26
+ export default Icon;
@@ -0,0 +1,38 @@
1
+ import Svg, {Defs, ClipPath, Rect, G, Path} from "react-native-svg";
2
+ /* SVGR has dropped some elements not supported by react-native-svg: style */
3
+ const SVGComponent = (props: any) => (
4
+ <Svg
5
+ xmlns="http://www.w3.org/2000/svg"
6
+ xmlnsXlink="http://www.w3.org/1999/xlink"
7
+ width={16}
8
+ height={16}
9
+ viewBox="0 0 16 16"
10
+ {...props}
11
+ >
12
+ <Defs>
13
+ <ClipPath id="a">
14
+ <Rect
15
+ width={16}
16
+ height={16}
17
+ transform="translate(314 477)"
18
+ />
19
+ </ClipPath>
20
+ </Defs>
21
+ <G className="b" transform="translate(-314 -477)">
22
+ <G transform="translate(225.396 64.866)">
23
+ <Path
24
+ d="M746.662,419.69a1.556,1.556,0,1,0,1.556-1.556A1.556,1.556,0,0,0,746.662,419.69Z"
25
+ transform="translate(-646.169)"
26
+ />
27
+ <Path
28
+ d="M89.6,419.69a1.556,1.556,0,1,0,1.556-1.556A1.556,1.556,0,0,0,89.6,419.69Z"
29
+ />
30
+ <Path
31
+ d="M418.133,419.69a1.556,1.556,0,1,0,1.556-1.556A1.555,1.555,0,0,0,418.133,419.69Z"
32
+ transform="translate(-323.084)"
33
+ />
34
+ </G>
35
+ </G>
36
+ </Svg>
37
+ );
38
+ export default SVGComponent;
@@ -0,0 +1,21 @@
1
+ import React from 'react';
2
+ import { Svg, Path } from 'react-native-svg';
3
+
4
+ interface CartIconProps {
5
+ width?: number;
6
+ height?: number;
7
+ fill?: string;
8
+ }
9
+
10
+ const CartIcon: React.FC<CartIconProps> = ({ width = 14, height = 14, fill = '#666' }) => {
11
+ return (
12
+ <Svg width={width} height={height} viewBox="0 0 14 14" fill="none">
13
+ <Path
14
+ d="M11.666 4.347H8.061V4.17l.137-1.064a3.2 3.2 0 00-.276-1.868 2.055 2.055 0 00-1.596-1.246l-.2-.034a1.4 1.4 0 00-1.263.176 1.348 1.348 0 00-.177.185l-.024.117a1.38 1.38 0 00-.276.427.342.342 0 00-.073.5 1.345 1.345 0 00-.1.244.608.608 0 00-.036.249V2.861A2.419 2.419 0 011.881 5.446H1.023a1.012 1.012 0 00-.745.318A1.027 1.027 0 000 6.537l.275 5.218A1.072 1.072 0 001.33 12.774h8.961a1.216 1.216 0 001.226-1.049l1.2-5.977a1.261 1.261 0 00-1.054-1.4zM5.408 1.356a.328.328 0 01.322-.256.32.32 0 01.061 0l.2.034a1.017 1.017 0 01.773.606 1.964 1.964 0 01.2 1.254l-.13 1.069v.172A1.243 1.243 0 008.061 5.461h3.462a.209.209 0 01.119.177l-1.2 5.944c0 .052-.088.113-.146.113H3.449L3.075 6.312A3.557 3.557 0 005.408 2.827zM2.371 11.342v.034a.3.3 0 01-.318.319H1.538a.346.346 0 01-.319-.32L1.082 6.81A.319.319 0 011.4 6.492h.344a.375.375 0 01.318.319z"
15
+ fill={fill}
16
+ />
17
+ </Svg>
18
+ );
19
+ };
20
+
21
+ export default CartIcon;
@@ -0,0 +1,36 @@
1
+ import React from 'react';
2
+ import { Svg, Circle, Line } from 'react-native-svg';
3
+
4
+ interface SearchIconProps {
5
+ width?: number;
6
+ height?: number;
7
+ fill?: string;
8
+ }
9
+
10
+ const SearchIcon: React.FC<SearchIconProps> = ({ width = 20, height = 20, fill = '#111' }) => {
11
+ return (
12
+ <Svg width={width} height={height} viewBox="0 0 20 20" fill="none">
13
+ {/* 圆环 */}
14
+ <Circle
15
+ cx={9.5}
16
+ cy={9.5}
17
+ r={6}
18
+ stroke={fill}
19
+ strokeWidth={1.5}
20
+ fill="none"
21
+ />
22
+ {/* 手柄 */}
23
+ <Line
24
+ x1={13.74}
25
+ y1={13.74}
26
+ x2={16.85}
27
+ y2={16.85}
28
+ stroke={fill}
29
+ strokeWidth={1.5}
30
+ strokeLinecap="round"
31
+ />
32
+ </Svg>
33
+ );
34
+ };
35
+
36
+ export default SearchIcon;
@@ -0,0 +1,3 @@
1
+ import Icon from './Icon';
2
+
3
+ export { Icon };
@@ -0,0 +1,30 @@
1
+ // svg from https://www.iconfont.cn/collections/detail?spm=a313x.7781069.0.da5a778a4&cid=19238
2
+ export const Library = {
3
+ 'arrow-left':
4
+ 'M641.28 278.613333l-45.226667-45.226666-278.634666 278.762666 278.613333 278.485334 45.248-45.269334-233.365333-233.237333z',
5
+ 'arrow-right':
6
+ 'M593.450667 512.128L360.064 278.613333l45.290667-45.226666 278.613333 278.762666L405.333333 790.613333l-45.226666-45.269333z',
7
+ 'arrow-up':
8
+ 'M500.8 461.909333L267.306667 695.296l-45.226667-45.269333 278.741333-278.613334L779.306667 650.026667l-45.248 45.226666z',
9
+ 'arrow-down':
10
+ 'M500.8 604.779L267.307 371.392l-45.227 45.27 278.741 278.613L779.307 416.66l-45.248-45.248z',
11
+ 'plus':
12
+ 'M544 213.333333v266.666667H810.666667v64H544V810.666667h-64V544H213.333333v-64h266.666667V213.333333z',
13
+ 'minus': 'M810.666667 480v64H213.333333v-64z',
14
+ 'code':
15
+ 'M541.141333 268.864l61.717334 16.938667-132.394667 482.474666-61.717333-16.938666 132.394666-482.474667zM329.002667 298.666667l44.885333 45.610666-175.36 172.586667 175.04 167.573333-44.266667 46.229334L106.666667 517.504 329.002667 298.666667z m355.882666 0l222.336 218.837333L684.586667 730.666667l-44.266667-46.229334 175.018667-167.573333L640 344.277333 684.885333 298.666667z',
16
+ 'check':
17
+ 'M235.946667 472.938667l-45.226667 45.312 210.090667 209.514666 432.362666-427.690666-45.013333-45.482667-387.157333 382.976z',
18
+ 'sorting':
19
+ 'M438.634667 192v644.202667l-0.810667 5.781333c-1.152 7.210667-2.304 8.704-8.64 17.002667l-7.168 3.925333c-15.914667 8.490667-18.282667 7.168-38.4-3.925333L149.333333 624.533333l45.269334-45.226666 180.032 180.138666V192h64z m216.96 10.005333l231.04 231.146667-45.269334 45.248L661.333333 298.261333V865.706667h-64V226.133333a34.133333 34.133333 0 0 1 58.282667-24.128z',
20
+ 'favorites':
21
+ 'M484.267 272.021l6.634 6.72c5.974 5.974 13.014 12.843 21.099 20.63l9.195-8.918c7.253-7.04 13.44-13.184 18.56-18.432a193.28 193.28 0 0 1 277.44 0c75.904 77.526 76.629 202.795 2.133 281.195L512 853.333 204.672 553.237c-74.475-78.421-73.77-203.69 2.133-281.216a193.28 193.28 0 0 1 277.44 0z m293.162 232.15c46.272-53.76 44.182-136.15-5.973-187.371a129.28 129.28 0 0 0-185.984 0l-15.125 15.104a1687.253 1687.253 0 0 1-4.395 4.31L512 388.18l-49.28-47.445-13.227-12.928-10.965-11.008a129.28 129.28 0 0 0-186.005 0c-51.456 52.565-52.31 137.963-2.198 191.573L512 763.883l261.675-255.531 3.754-4.181z',
22
+ 'favorites-fill':
23
+ 'M484.266667 272.021333l6.634666 6.72c5.973333 5.973333 13.013333 12.842667 21.098667 20.629334l9.194667-8.917334c7.253333-7.04 13.44-13.184 18.56-18.432a193.28 193.28 0 0 1 277.44 0c75.904 77.525333 76.629333 202.794667 2.133333 281.194667L512 853.333333 204.672 553.237333c-74.474667-78.421333-73.770667-203.690667 2.133333-281.216a193.28 193.28 0 0 1 277.44 0z',
24
+ 'search':
25
+ 'M469.333 192c153.174 0 277.334 124.16 277.334 277.333 0 68.054-24.534 130.411-65.216 178.688L846.336 818.24l-48.341 49.877L630.4 695.125a276.053 276.053 0 0 1-161.067 51.542C316.16 746.667 192 622.507 192 469.333S316.16 192 469.333 192z m0 64C351.51 256 256 351.51 256 469.333s95.51 213.334 213.333 213.334 213.334-95.51 213.334-213.334S587.157 256 469.333 256z',
26
+ 'arrow-line-up':
27
+ 'M478.3 927.5V175.2L259 394.5c-10.7 10.7-28.1 10.7-38.7 0l-6.5-6.5c-10.7-10.7-10.7-28.1 0-38.7L481.6 81.4c4.5-9.2 13.4-16 23.9-17.6 7.1-1.5 14.7-0.1 21 4 4 2.4 7.5 5.6 10.1 9.4l0.5 0.5c2.6 2.6 4.6 5.6 5.9 8.8l266.7 266.7c10.7 10.7 10.7 28.1 0 38.7l-6.5 6.5c-10.7 10.7-28.1 10.7-38.7 0l-222.3-222v751.1c0 17.6-14.4 32-32 32-17.5 0-31.9-14.4-31.9-32z',
28
+ 'arrow-line-down':
29
+ 'M478.3 95.2v752.3L259 628.2c-10.7-10.7-28.1-10.7-38.7 0l-6.5 6.5c-10.7 10.7-10.7 28.1 0 38.7l267.9 267.9c4.5 9.2 13.4 16 23.9 17.6 7.1 1.5 14.7 0.1 21-4 4-2.4 7.5-5.6 10.1-9.4l0.5-0.5c2.6-2.6 4.6-5.6 5.9-8.8l266.7-266.7c10.7-10.7 10.7-28.1 0-38.7l-6.5-6.5c-10.7-10.7-28.1-10.7-38.7 0l-222.4 222V95.2c0-17.6-14.4-32-32-32-17.5 0-31.9 14.4-31.9 32z',
30
+ };
package/src/index.tsx ADDED
@@ -0,0 +1 @@
1
+ export * from './RefreshControl';