@cleartrip/ct-design-list 4.0.0-SNAPSHOT-native-main.20 → 4.0.0-SNAPSHOT-native-main.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/List.native.d.ts.map +1 -1
- package/package.json +6 -6
- package/src/List.native.tsx +103 -5
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"List.native.d.ts","sourceRoot":"","sources":["../packages/components/List/src/List.native.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"List.native.d.ts","sourceRoot":"","sources":["../packages/components/List/src/List.native.tsx"],"names":[],"mappings":"AA0BA,OAAO,EAA0B,uBAAuB,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAsElF,QAAA,MAAM,IAAI,EA8SM,CAAC,CAAC,EAAE,CAAC,SAAS;IAAE,IAAI,EAAE,CAAC,EAAE,CAAA;CAAE,EACzC,KAAK,EAAE,uBAAuB,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG;IAAE,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;CAAE,KAChE,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC;AAIvB,eAAe,IAAI,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cleartrip/ct-design-list",
|
|
3
|
-
"version": "4.0.0-SNAPSHOT-native-main.
|
|
3
|
+
"version": "4.0.0-SNAPSHOT-native-main.22",
|
|
4
4
|
"description": "List Component (virtualised)",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"main": "./dist/ct-design-list.cjs.js",
|
|
@@ -26,15 +26,15 @@
|
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"react-window": "^1.8.9",
|
|
28
28
|
"react-window-infinite-loader": "^1.0.9",
|
|
29
|
-
"@cleartrip/ct-design-
|
|
30
|
-
"@cleartrip/ct-design-
|
|
31
|
-
"@cleartrip/ct-design-
|
|
32
|
-
"@cleartrip/ct-design-
|
|
29
|
+
"@cleartrip/ct-design-scroll-container": "4.0.0-SNAPSHOT-native-main.22",
|
|
30
|
+
"@cleartrip/ct-design-style-manager": "4.0.0-SNAPSHOT-native-main.22",
|
|
31
|
+
"@cleartrip/ct-design-use-merge-refs": "4.0.0-SNAPSHOT-native-main.22",
|
|
32
|
+
"@cleartrip/ct-design-theme": "4.0.0-SNAPSHOT-native-main.22"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
35
|
"@types/react-window": "^1.8.8",
|
|
36
36
|
"@types/react-window-infinite-loader": "^1.0.9",
|
|
37
|
-
"@cleartrip/ct-design-types": "4.0.0-SNAPSHOT-native-main.
|
|
37
|
+
"@cleartrip/ct-design-types": "4.0.0-SNAPSHOT-native-main.22"
|
|
38
38
|
},
|
|
39
39
|
"peerDependencies": {
|
|
40
40
|
"react": ">=16.8.0",
|
package/src/List.native.tsx
CHANGED
|
@@ -3,16 +3,59 @@ import { forwardRef, memo, useCallback, useRef } from 'react';
|
|
|
3
3
|
import { makeStyles } from '@cleartrip/ct-design-style-manager';
|
|
4
4
|
import useMergeRefs from '@cleartrip/ct-design-use-merge-refs';
|
|
5
5
|
import { ScrollContextProvider, ScrollableObservableChild } from '@cleartrip/ct-design-scroll-container';
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
FlatList,
|
|
8
|
+
ListRenderItemInfo,
|
|
9
|
+
NativeScrollEvent,
|
|
10
|
+
NativeSyntheticEvent,
|
|
11
|
+
Pressable,
|
|
12
|
+
Text,
|
|
13
|
+
View,
|
|
14
|
+
ViewToken,
|
|
15
|
+
} from 'react-native';
|
|
16
|
+
import Animated, {
|
|
17
|
+
Extrapolation,
|
|
18
|
+
interpolate,
|
|
19
|
+
useAnimatedStyle,
|
|
20
|
+
useSharedValue,
|
|
21
|
+
withTiming,
|
|
22
|
+
} from 'react-native-reanimated';
|
|
23
|
+
|
|
24
|
+
const AnimatedPressable = Animated.createAnimatedComponent(Pressable);
|
|
25
|
+
const BACK_TO_TOP_ANIM_DURATION = 200;
|
|
7
26
|
|
|
8
27
|
import { IListItemRendererProps, IVariableSizedListProps, ListRef } from './type';
|
|
9
28
|
import { ListItemProvider } from './ListItemContext';
|
|
10
29
|
import { areSetsEqual } from './utils';
|
|
11
30
|
|
|
31
|
+
const BACK_TO_TOP_SHOW_THRESHOLD = 200;
|
|
32
|
+
const SCROLL_DELTA_TOLERANCE = 4;
|
|
33
|
+
|
|
12
34
|
const staticStyles = makeStyles(() => ({
|
|
13
35
|
itemSeparator: {
|
|
14
36
|
height: 24,
|
|
15
37
|
},
|
|
38
|
+
backToTopChip: {
|
|
39
|
+
position: 'absolute',
|
|
40
|
+
top: 16,
|
|
41
|
+
alignSelf: 'center',
|
|
42
|
+
backgroundColor: '#1A1A1A',
|
|
43
|
+
paddingHorizontal: 12,
|
|
44
|
+
paddingVertical: 8,
|
|
45
|
+
borderRadius: 999,
|
|
46
|
+
flexDirection: 'row',
|
|
47
|
+
alignItems: 'center',
|
|
48
|
+
shadowColor: '#000',
|
|
49
|
+
shadowOffset: { width: 0, height: 2 },
|
|
50
|
+
shadowOpacity: 0.2,
|
|
51
|
+
shadowRadius: 4,
|
|
52
|
+
elevation: 4,
|
|
53
|
+
},
|
|
54
|
+
backToTopText: {
|
|
55
|
+
color: '#FFFFFF',
|
|
56
|
+
fontSize: 14,
|
|
57
|
+
fontWeight: '500',
|
|
58
|
+
},
|
|
16
59
|
}));
|
|
17
60
|
|
|
18
61
|
/**
|
|
@@ -79,6 +122,56 @@ const List = memo(
|
|
|
79
122
|
const observableChildRefs = useRef<Array<ScrollableObservableChild>>([]);
|
|
80
123
|
const isScrollToScrollingRef = useRef(false);
|
|
81
124
|
|
|
125
|
+
const prevScrollYRef = useRef(0);
|
|
126
|
+
const showTargetRef = useRef(0);
|
|
127
|
+
const showProgress = useSharedValue(0);
|
|
128
|
+
|
|
129
|
+
const chipAnimatedStyle = useAnimatedStyle(() => {
|
|
130
|
+
const v = showProgress.value;
|
|
131
|
+
return {
|
|
132
|
+
opacity: v,
|
|
133
|
+
transform: [{ translateY: interpolate(v, [0, 1], [-12, 0], Extrapolation.CLAMP) }],
|
|
134
|
+
pointerEvents: v > 0.5 ? 'auto' : 'none',
|
|
135
|
+
};
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
const setShowTarget = (next: 0 | 1) => {
|
|
139
|
+
if (showTargetRef.current === next) return;
|
|
140
|
+
showTargetRef.current = next;
|
|
141
|
+
showProgress.value = withTiming(next, { duration: BACK_TO_TOP_ANIM_DURATION });
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
const handleBackToTopPress = useCallback(() => {
|
|
145
|
+
_ref.current?.scrollToOffset({ offset: 0, animated: true });
|
|
146
|
+
if (showTargetRef.current !== 0) {
|
|
147
|
+
showTargetRef.current = 0;
|
|
148
|
+
showProgress.value = withTiming(0, { duration: BACK_TO_TOP_ANIM_DURATION });
|
|
149
|
+
}
|
|
150
|
+
// showProgress / showTargetRef are stable refs; safe with [] deps
|
|
151
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
152
|
+
}, []);
|
|
153
|
+
|
|
154
|
+
const handleScroll = useCallback(
|
|
155
|
+
(e: NativeSyntheticEvent<NativeScrollEvent>) => {
|
|
156
|
+
const currentY = e.nativeEvent.contentOffset.y;
|
|
157
|
+
const prevY = prevScrollYRef.current;
|
|
158
|
+
|
|
159
|
+
if (currentY <= BACK_TO_TOP_SHOW_THRESHOLD) {
|
|
160
|
+
setShowTarget(0);
|
|
161
|
+
} else if (prevY - currentY > SCROLL_DELTA_TOLERANCE) {
|
|
162
|
+
setShowTarget(1);
|
|
163
|
+
} else if (currentY - prevY > SCROLL_DELTA_TOLERANCE) {
|
|
164
|
+
setShowTarget(0);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
prevScrollYRef.current = currentY;
|
|
168
|
+
|
|
169
|
+
onScroll?.(e);
|
|
170
|
+
scrollMaintainingRef.current = true;
|
|
171
|
+
},
|
|
172
|
+
[onScroll],
|
|
173
|
+
);
|
|
174
|
+
|
|
82
175
|
const viewableIndicesRef = useRef<Set<number>>(new Set());
|
|
83
176
|
|
|
84
177
|
const viewableItemsRef = useRef<Record<string, Record<string, unknown>>>({});
|
|
@@ -259,10 +352,7 @@ const List = memo(
|
|
|
259
352
|
alwaysBounceHorizontal={false}
|
|
260
353
|
alwaysBounceVertical={false}
|
|
261
354
|
keyboardShouldPersistTaps={keyboardShouldPersistTaps}
|
|
262
|
-
onScroll={
|
|
263
|
-
onScroll?.(e);
|
|
264
|
-
scrollMaintainingRef.current = true;
|
|
265
|
-
}}
|
|
355
|
+
onScroll={handleScroll}
|
|
266
356
|
onScrollEndDrag={() => {
|
|
267
357
|
/**
|
|
268
358
|
* !NOTE:
|
|
@@ -294,6 +384,14 @@ const List = memo(
|
|
|
294
384
|
bounces={false}
|
|
295
385
|
scrollEventThrottle={1}
|
|
296
386
|
/>
|
|
387
|
+
<AnimatedPressable
|
|
388
|
+
onPress={handleBackToTopPress}
|
|
389
|
+
accessibilityRole='button'
|
|
390
|
+
accessibilityLabel='Back to top'
|
|
391
|
+
style={[staticStyles.backToTopChip, chipAnimatedStyle]}
|
|
392
|
+
>
|
|
393
|
+
<Text style={staticStyles.backToTopText}>↑ Back to top</Text>
|
|
394
|
+
</AnimatedPressable>
|
|
297
395
|
</ScrollContextProvider>
|
|
298
396
|
);
|
|
299
397
|
},
|