@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.
@@ -1 +1 @@
1
- {"version":3,"file":"List.native.d.ts","sourceRoot":"","sources":["../packages/components/List/src/List.native.tsx"],"names":[],"mappings":"AAOA,OAAO,EAA0B,uBAAuB,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AA8ClF,QAAA,MAAM,IAAI,EAuPM,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"}
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.20",
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-style-manager": "4.0.0-SNAPSHOT-native-main.20",
30
- "@cleartrip/ct-design-use-merge-refs": "4.0.0-SNAPSHOT-native-main.20",
31
- "@cleartrip/ct-design-theme": "4.0.0-SNAPSHOT-native-main.20",
32
- "@cleartrip/ct-design-scroll-container": "4.0.0-SNAPSHOT-native-main.20"
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.20"
37
+ "@cleartrip/ct-design-types": "4.0.0-SNAPSHOT-native-main.22"
38
38
  },
39
39
  "peerDependencies": {
40
40
  "react": ">=16.8.0",
@@ -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 { FlatList, ListRenderItemInfo, View, ViewToken } from 'react-native';
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={(e) => {
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
  },