@momo-kits/foundation 0.161.2-beta.8 → 0.161.2-test.1
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/Application/BottomSheet.tsx +7 -4
- package/Application/BottomTab/BottomTabBar.tsx +15 -15
- package/Application/BottomTab/index.tsx +34 -56
- package/Application/Components/BackgroundImageView.tsx +22 -6
- package/Application/Components/HeaderAnimated.tsx +32 -29
- package/Application/Components/HeaderBackground.tsx +20 -26
- package/Application/Components/HeaderExtendHeader.tsx +114 -94
- package/Application/Components/HeaderTitle.tsx +39 -10
- package/Application/Components/SearchHeader.tsx +16 -21
- package/Application/types.ts +4 -4
- package/Application/utils.tsx +4 -3
- package/Badge/BadgeDotAnimation.tsx +53 -71
- package/Button/index.tsx +1 -4
- package/Input/InputOTP.tsx +38 -23
- package/Layout/FloatingButton.tsx +59 -59
- package/Layout/Screen.tsx +71 -61
- package/Loader/ProgressBar.tsx +20 -18
- package/Pagination/Dot.tsx +2 -2
- package/Pagination/PaginationScroll.tsx +31 -27
- package/Skeleton/index.tsx +32 -24
- package/package.json +1 -1
package/Input/InputOTP.tsx
CHANGED
|
@@ -8,13 +8,22 @@ import React, {
|
|
|
8
8
|
useState,
|
|
9
9
|
} from 'react';
|
|
10
10
|
import {
|
|
11
|
-
Animated,
|
|
12
11
|
PixelRatio,
|
|
13
12
|
TextInput,
|
|
14
13
|
TextInputFocusEvent,
|
|
15
14
|
TouchableOpacity,
|
|
16
15
|
View,
|
|
17
16
|
} from 'react-native';
|
|
17
|
+
import Animated, {
|
|
18
|
+
cancelAnimation,
|
|
19
|
+
Easing,
|
|
20
|
+
useAnimatedStyle,
|
|
21
|
+
useSharedValue,
|
|
22
|
+
withDelay,
|
|
23
|
+
withRepeat,
|
|
24
|
+
withSequence,
|
|
25
|
+
withTiming,
|
|
26
|
+
} from 'react-native-reanimated';
|
|
18
27
|
import { useComponentId } from '../Application';
|
|
19
28
|
import { Spacing, Styles } from '../Consts';
|
|
20
29
|
import { useScaleSize, Text } from '../Text';
|
|
@@ -32,37 +41,43 @@ import {
|
|
|
32
41
|
const OTPCaret: FC<CaretProps> = ({ index, length }) => {
|
|
33
42
|
const DURATION = 300;
|
|
34
43
|
const { theme } = useContext(ApplicationContext);
|
|
35
|
-
const opacity =
|
|
44
|
+
const opacity = useSharedValue(0);
|
|
36
45
|
|
|
37
46
|
useEffect(() => {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
).start();
|
|
47
|
+
opacity.value = withRepeat(
|
|
48
|
+
withSequence(
|
|
49
|
+
withTiming(1, { duration: DURATION, easing: Easing.linear }),
|
|
50
|
+
withDelay(
|
|
51
|
+
DURATION * 2,
|
|
52
|
+
withTiming(0, { duration: DURATION, easing: Easing.linear }),
|
|
53
|
+
),
|
|
54
|
+
),
|
|
55
|
+
-1,
|
|
56
|
+
false,
|
|
57
|
+
);
|
|
58
|
+
return () => {
|
|
59
|
+
cancelAnimation(opacity);
|
|
60
|
+
};
|
|
53
61
|
}, [opacity]);
|
|
62
|
+
|
|
63
|
+
const animatedStyle = useAnimatedStyle(() => ({
|
|
64
|
+
opacity: opacity.value,
|
|
65
|
+
}));
|
|
66
|
+
|
|
54
67
|
const spacingStyle = !isNaN(Number(length)) &&
|
|
55
68
|
index !== Number(length) - 1 && { marginRight: Spacing.L };
|
|
56
69
|
|
|
57
70
|
return (
|
|
58
71
|
<View style={[Styles.rowCenter, spacingStyle]}>
|
|
59
72
|
<Animated.View
|
|
60
|
-
style={
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
73
|
+
style={[
|
|
74
|
+
{
|
|
75
|
+
height: useScaleSize(12),
|
|
76
|
+
width: 1,
|
|
77
|
+
backgroundColor: theme.colors.primary,
|
|
78
|
+
},
|
|
79
|
+
animatedStyle,
|
|
80
|
+
]}
|
|
66
81
|
/>
|
|
67
82
|
<Text color={theme.colors.text.hint} typography={'body_default_regular'}>
|
|
68
83
|
-
|
|
@@ -1,17 +1,18 @@
|
|
|
1
|
+
import React, { useContext, useEffect, useState } from 'react';
|
|
1
2
|
import {
|
|
2
|
-
default as React,
|
|
3
|
-
useContext,
|
|
4
|
-
useEffect,
|
|
5
|
-
useRef,
|
|
6
|
-
useState,
|
|
7
|
-
} from 'react';
|
|
8
|
-
import {
|
|
9
|
-
Animated,
|
|
10
3
|
LayoutChangeEvent,
|
|
11
4
|
StyleSheet,
|
|
12
5
|
TouchableOpacity,
|
|
13
6
|
View,
|
|
14
7
|
} from 'react-native';
|
|
8
|
+
import Animated, {
|
|
9
|
+
useAnimatedReaction,
|
|
10
|
+
useAnimatedStyle,
|
|
11
|
+
useSharedValue,
|
|
12
|
+
withTiming,
|
|
13
|
+
runOnJS,
|
|
14
|
+
type SharedValue,
|
|
15
|
+
} from 'react-native-reanimated';
|
|
15
16
|
import { ApplicationContext } from '../Context';
|
|
16
17
|
import { Icon } from '../Icon';
|
|
17
18
|
import { useScaleSize } from '../Text';
|
|
@@ -23,7 +24,7 @@ export interface FloatingButtonProps {
|
|
|
23
24
|
icon?: string;
|
|
24
25
|
iconColor?: string;
|
|
25
26
|
size?: 'small' | 'large';
|
|
26
|
-
animatedValue?:
|
|
27
|
+
animatedValue?: SharedValue<number>;
|
|
27
28
|
bottom?: number;
|
|
28
29
|
renderComponent?: () => React.ReactNode;
|
|
29
30
|
}
|
|
@@ -42,68 +43,67 @@ export const FloatingButton: React.FC<FloatingButtonProps> = ({
|
|
|
42
43
|
const { theme } = useContext(ApplicationContext);
|
|
43
44
|
const scaledFontSize = useScaleSize(16);
|
|
44
45
|
const scaledLineHeight = useScaleSize(22);
|
|
45
|
-
const maxWidth =
|
|
46
|
+
const maxWidth = useSharedValue(0);
|
|
46
47
|
const minWidth = size === 'small' ? 36 : 48;
|
|
47
|
-
const
|
|
48
|
-
const
|
|
49
|
-
const lastOffset =
|
|
50
|
-
const lastDirection =
|
|
51
|
-
const [showText, setShowText] =
|
|
48
|
+
const opacityAnimated = useSharedValue(0);
|
|
49
|
+
const widthAnimated = useSharedValue<number | null>(null);
|
|
50
|
+
const lastOffset = useSharedValue(0);
|
|
51
|
+
const lastDirection = useSharedValue<string | null>(null);
|
|
52
|
+
const [showText, setShowText] = useState(true);
|
|
52
53
|
|
|
53
54
|
useEffect(() => {
|
|
54
55
|
if (!label) return;
|
|
56
|
+
opacityAnimated.value = withTiming(1, { duration: 100 });
|
|
57
|
+
}, [label, opacityAnimated]);
|
|
55
58
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
if (lastDirection.current !== direction) {
|
|
67
|
-
lastDirection.current = direction;
|
|
59
|
+
useAnimatedReaction(
|
|
60
|
+
() => animatedValue?.value ?? 0,
|
|
61
|
+
(value) => {
|
|
62
|
+
'worklet';
|
|
63
|
+
if (!label || !animatedValue) return;
|
|
64
|
+
if (value !== lastOffset.value && value > 0) {
|
|
65
|
+
const direction = value > lastOffset.value ? 'down' : 'up';
|
|
66
|
+
lastOffset.value = value;
|
|
67
|
+
if (lastDirection.value !== direction) {
|
|
68
|
+
lastDirection.value = direction;
|
|
68
69
|
if (direction === 'down') {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
useNativeDriver: false,
|
|
78
|
-
}).start(() => setShowText(false));
|
|
70
|
+
opacityAnimated.value = withTiming(0, { duration: 100 });
|
|
71
|
+
widthAnimated.value = withTiming(
|
|
72
|
+
minWidth,
|
|
73
|
+
{ duration: 100 },
|
|
74
|
+
(finished) => {
|
|
75
|
+
if (finished) runOnJS(setShowText)(false);
|
|
76
|
+
},
|
|
77
|
+
);
|
|
79
78
|
} else {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
useNativeDriver: false,
|
|
89
|
-
}).start(() => setShowText(true));
|
|
79
|
+
opacityAnimated.value = withTiming(1, { duration: 100 });
|
|
80
|
+
widthAnimated.value = withTiming(
|
|
81
|
+
maxWidth.value,
|
|
82
|
+
{ duration: 100 },
|
|
83
|
+
(finished) => {
|
|
84
|
+
if (finished) runOnJS(setShowText)(true);
|
|
85
|
+
},
|
|
86
|
+
);
|
|
90
87
|
}
|
|
91
88
|
}
|
|
92
89
|
}
|
|
93
|
-
}
|
|
90
|
+
},
|
|
91
|
+
[label, animatedValue, minWidth],
|
|
92
|
+
);
|
|
94
93
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
94
|
+
const containerStyle = useAnimatedStyle(() => ({
|
|
95
|
+
width: widthAnimated.value ?? undefined,
|
|
96
|
+
}));
|
|
97
|
+
|
|
98
|
+
const labelStyle = useAnimatedStyle(() => ({
|
|
99
|
+
opacity: opacityAnimated.value,
|
|
100
|
+
}));
|
|
101
101
|
|
|
102
102
|
const handleLayout = (event: LayoutChangeEvent) => {
|
|
103
103
|
const layout = event.nativeEvent.layout;
|
|
104
|
-
if (widthAnimated) return;
|
|
105
|
-
maxWidth.
|
|
106
|
-
|
|
104
|
+
if (widthAnimated.value != null) return;
|
|
105
|
+
maxWidth.value = layout.width;
|
|
106
|
+
widthAnimated.value = layout.width;
|
|
107
107
|
};
|
|
108
108
|
|
|
109
109
|
if (renderComponent) {
|
|
@@ -132,11 +132,11 @@ export const FloatingButton: React.FC<FloatingButtonProps> = ({
|
|
|
132
132
|
{
|
|
133
133
|
right: position === 'right' ? 12 : undefined,
|
|
134
134
|
alignSelf: position === 'center' ? 'center' : 'flex-end',
|
|
135
|
-
width: widthAnimated,
|
|
136
135
|
height: size === 'small' ? 36 : 48,
|
|
137
136
|
backgroundColor: theme.colors.primary,
|
|
138
137
|
bottom,
|
|
139
138
|
},
|
|
139
|
+
containerStyle,
|
|
140
140
|
]}
|
|
141
141
|
>
|
|
142
142
|
<TouchableOpacity
|
|
@@ -156,9 +156,9 @@ export const FloatingButton: React.FC<FloatingButtonProps> = ({
|
|
|
156
156
|
{
|
|
157
157
|
fontSize: scaledFontSize,
|
|
158
158
|
lineHeight: scaledLineHeight,
|
|
159
|
-
opacity: opacityAnimated,
|
|
160
159
|
color: 'white',
|
|
161
160
|
},
|
|
161
|
+
labelStyle,
|
|
162
162
|
]}
|
|
163
163
|
numberOfLines={1}
|
|
164
164
|
>
|
package/Layout/Screen.tsx
CHANGED
|
@@ -12,7 +12,6 @@ import React, {
|
|
|
12
12
|
useRef,
|
|
13
13
|
} from 'react';
|
|
14
14
|
import {
|
|
15
|
-
Animated,
|
|
16
15
|
KeyboardAvoidingView,
|
|
17
16
|
NativeScrollEvent,
|
|
18
17
|
NativeSyntheticEvent,
|
|
@@ -25,6 +24,13 @@ import {
|
|
|
25
24
|
View,
|
|
26
25
|
ViewProps,
|
|
27
26
|
} from 'react-native';
|
|
27
|
+
import Animated, {
|
|
28
|
+
runOnJS,
|
|
29
|
+
useAnimatedScrollHandler,
|
|
30
|
+
useSharedValue,
|
|
31
|
+
withTiming,
|
|
32
|
+
type SharedValue,
|
|
33
|
+
} from 'react-native-reanimated';
|
|
28
34
|
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
29
35
|
import { ApplicationContext, ScreenContext } from '../Context';
|
|
30
36
|
import Navigation from '../Application/Navigation';
|
|
@@ -138,7 +144,7 @@ export interface ScreenProps extends ViewProps {
|
|
|
138
144
|
/**
|
|
139
145
|
* Optional. Animated value for header.
|
|
140
146
|
*/
|
|
141
|
-
animatedValue?:
|
|
147
|
+
animatedValue?: SharedValue<number>;
|
|
142
148
|
|
|
143
149
|
/**
|
|
144
150
|
* Optional. If `true`, use shadow header.
|
|
@@ -195,18 +201,23 @@ const Screen = forwardRef(
|
|
|
195
201
|
const screen: any = useContext(ScreenContext);
|
|
196
202
|
const insets = useSafeAreaInsets();
|
|
197
203
|
const heightHeader = useHeaderHeight();
|
|
198
|
-
const
|
|
199
|
-
|
|
200
|
-
);
|
|
204
|
+
const internalAnimatedValue = useSharedValue(0);
|
|
205
|
+
const animatedValue = customAnimatedValue ?? internalAnimatedValue;
|
|
201
206
|
const currentTint = useRef<string | undefined>(undefined);
|
|
202
207
|
const isTab = navigation?.instance?.getState?.()?.type === 'tab';
|
|
203
208
|
|
|
204
|
-
let handleScroll;
|
|
209
|
+
let handleScroll: any;
|
|
205
210
|
let Component: any = View;
|
|
206
211
|
|
|
207
|
-
|
|
212
|
+
// AI-GENERATED START
|
|
213
|
+
// On iOS the home indicator is ~34pt but visual safe area needs only ~21pt cap.
|
|
214
|
+
// On Android 15+ edge-to-edge the nav bar can be 48dp — no cap needed.
|
|
215
|
+
const bottomInset = Platform.OS === 'ios' ? Math.min(insets.bottom, 21) : insets.bottom;
|
|
216
|
+
// AI-GENERATED END
|
|
217
|
+
|
|
218
|
+
let keyboardOffset = heightHeader - bottomInset;
|
|
208
219
|
if (headerType === 'extended' || animatedHeader || inputSearchProps) {
|
|
209
|
-
keyboardOffset = -
|
|
220
|
+
keyboardOffset = -bottomInset;
|
|
210
221
|
}
|
|
211
222
|
|
|
212
223
|
/**
|
|
@@ -248,9 +259,8 @@ const Screen = forwardRef(
|
|
|
248
259
|
interpolate={{
|
|
249
260
|
inputRange: [0, 50],
|
|
250
261
|
outputRange: [1, 0],
|
|
251
|
-
extrapolate: 'clamp',
|
|
252
262
|
}}
|
|
253
|
-
animatedValue={animatedValue
|
|
263
|
+
animatedValue={animatedValue}
|
|
254
264
|
/>
|
|
255
265
|
),
|
|
256
266
|
};
|
|
@@ -265,7 +275,7 @@ const Screen = forwardRef(
|
|
|
265
275
|
headerBackground: (props: any) => (
|
|
266
276
|
<HeaderBackground
|
|
267
277
|
{...props}
|
|
268
|
-
animatedValue={animatedValue
|
|
278
|
+
animatedValue={animatedValue}
|
|
269
279
|
useShadowHeader={useShadowHeader}
|
|
270
280
|
headerBackground={headerBackground}
|
|
271
281
|
gradientColor={gradientColor}
|
|
@@ -284,9 +294,8 @@ const Screen = forwardRef(
|
|
|
284
294
|
interpolate={{
|
|
285
295
|
inputRange: [0, 50],
|
|
286
296
|
outputRange: [1, 0],
|
|
287
|
-
extrapolate: 'clamp',
|
|
288
297
|
}}
|
|
289
|
-
animatedValue={animatedValue
|
|
298
|
+
animatedValue={animatedValue}
|
|
290
299
|
/>
|
|
291
300
|
),
|
|
292
301
|
};
|
|
@@ -321,7 +330,7 @@ const Screen = forwardRef(
|
|
|
321
330
|
headerBackground: (props: any) => (
|
|
322
331
|
<HeaderBackground
|
|
323
332
|
{...props}
|
|
324
|
-
animatedValue={animatedValue
|
|
333
|
+
animatedValue={animatedValue}
|
|
325
334
|
useGradient={false}
|
|
326
335
|
useShadowHeader={useShadowHeader}
|
|
327
336
|
headerBackground={headerBackground}
|
|
@@ -359,9 +368,8 @@ const Screen = forwardRef(
|
|
|
359
368
|
interpolate={{
|
|
360
369
|
inputRange: [0, 50],
|
|
361
370
|
outputRange: [1, 0],
|
|
362
|
-
extrapolate: 'clamp',
|
|
363
371
|
}}
|
|
364
|
-
animatedValue={animatedValue
|
|
372
|
+
animatedValue={animatedValue}
|
|
365
373
|
/>
|
|
366
374
|
),
|
|
367
375
|
};
|
|
@@ -390,7 +398,7 @@ const Screen = forwardRef(
|
|
|
390
398
|
headerLeft: (props: any) =>
|
|
391
399
|
params?.hiddenBack ? null : <HeaderLeft {...props} />,
|
|
392
400
|
headerTitle: () => (
|
|
393
|
-
<SearchHeader {...params} animatedValue={animatedValue
|
|
401
|
+
<SearchHeader {...params} animatedValue={animatedValue} />
|
|
394
402
|
),
|
|
395
403
|
};
|
|
396
404
|
|
|
@@ -441,45 +449,51 @@ const Screen = forwardRef(
|
|
|
441
449
|
});
|
|
442
450
|
});
|
|
443
451
|
|
|
452
|
+
const onTintColorChange = useCallback(
|
|
453
|
+
(offsetY: number) => {
|
|
454
|
+
if (!animatedHeader) return;
|
|
455
|
+
let color = animatedHeader?.headerTintColor ?? Colors.black_17;
|
|
456
|
+
if (offsetY > 50) {
|
|
457
|
+
color = Colors.black_17;
|
|
458
|
+
}
|
|
459
|
+
if (color !== currentTint.current) {
|
|
460
|
+
currentTint.current = color;
|
|
461
|
+
navigation?.setOptions({
|
|
462
|
+
headerTintColor: color,
|
|
463
|
+
});
|
|
464
|
+
let barStyle: StatusBarStyle = 'dark-content';
|
|
465
|
+
if (currentTint.current === Colors.black_01) {
|
|
466
|
+
barStyle = 'light-content';
|
|
467
|
+
}
|
|
468
|
+
StatusBar.setBarStyle(barStyle, true);
|
|
469
|
+
}
|
|
470
|
+
},
|
|
471
|
+
[animatedHeader, navigation],
|
|
472
|
+
);
|
|
473
|
+
|
|
474
|
+
const emitOnScroll = useCallback(
|
|
475
|
+
(offsetY: number) => {
|
|
476
|
+
scrollViewProps?.onScroll?.({
|
|
477
|
+
nativeEvent: { contentOffset: { x: 0, y: offsetY } },
|
|
478
|
+
} as NativeSyntheticEvent<NativeScrollEvent>);
|
|
479
|
+
},
|
|
480
|
+
[scrollViewProps],
|
|
481
|
+
);
|
|
482
|
+
|
|
483
|
+
const scrollHandler = useAnimatedScrollHandler({
|
|
484
|
+
onScroll: (event) => {
|
|
485
|
+
animatedValue.value = event.contentOffset.y;
|
|
486
|
+
runOnJS(emitOnScroll)(event.contentOffset.y);
|
|
487
|
+
runOnJS(onTintColorChange)(event.contentOffset.y);
|
|
488
|
+
},
|
|
489
|
+
});
|
|
490
|
+
|
|
444
491
|
/**
|
|
445
492
|
* animated when use scroll && animated value
|
|
446
493
|
*/
|
|
447
494
|
if (scrollable) {
|
|
448
495
|
Component = Animated.ScrollView;
|
|
449
|
-
handleScroll =
|
|
450
|
-
[
|
|
451
|
-
{
|
|
452
|
-
nativeEvent: {
|
|
453
|
-
contentOffset: { y: animatedValue.current as Animated.Value },
|
|
454
|
-
},
|
|
455
|
-
},
|
|
456
|
-
],
|
|
457
|
-
{
|
|
458
|
-
useNativeDriver: true,
|
|
459
|
-
listener: (e: NativeSyntheticEvent<NativeScrollEvent>) => {
|
|
460
|
-
scrollViewProps?.onScroll?.(e);
|
|
461
|
-
if (animatedHeader) {
|
|
462
|
-
const offsetY = e.nativeEvent.contentOffset.y;
|
|
463
|
-
let color = animatedHeader?.headerTintColor ?? Colors.black_17;
|
|
464
|
-
if (offsetY > 50) {
|
|
465
|
-
color = Colors.black_17;
|
|
466
|
-
}
|
|
467
|
-
if (color !== currentTint.current) {
|
|
468
|
-
currentTint.current = color;
|
|
469
|
-
navigation?.setOptions({
|
|
470
|
-
headerTintColor: color,
|
|
471
|
-
});
|
|
472
|
-
|
|
473
|
-
let barStyle: StatusBarStyle = 'dark-content';
|
|
474
|
-
if (currentTint.current === Colors.black_01) {
|
|
475
|
-
barStyle = 'light-content';
|
|
476
|
-
}
|
|
477
|
-
StatusBar.setBarStyle(barStyle, true);
|
|
478
|
-
}
|
|
479
|
-
}
|
|
480
|
-
},
|
|
481
|
-
},
|
|
482
|
-
);
|
|
496
|
+
handleScroll = scrollHandler;
|
|
483
497
|
}
|
|
484
498
|
|
|
485
499
|
/**
|
|
@@ -489,11 +503,7 @@ const Screen = forwardRef(
|
|
|
489
503
|
const handleScrollEnd = (e: NativeSyntheticEvent<NativeScrollEvent>) => {
|
|
490
504
|
const offsetY = e.nativeEvent.contentOffset.y;
|
|
491
505
|
if (inputSearchProps && offsetY < 100 && offsetY > 0) {
|
|
492
|
-
|
|
493
|
-
toValue: 0,
|
|
494
|
-
useNativeDriver: true,
|
|
495
|
-
duration: 300,
|
|
496
|
-
}).start();
|
|
506
|
+
animatedValue.value = withTiming(0, { duration: 300 });
|
|
497
507
|
ref?.scrollTo?.({ y: 0, animated: true });
|
|
498
508
|
}
|
|
499
509
|
scrollViewProps?.onScrollEndDrag?.(e);
|
|
@@ -509,7 +519,7 @@ const Screen = forwardRef(
|
|
|
509
519
|
style={[styles.screenBanner, { maxHeight: 210 + layoutOffset }]}
|
|
510
520
|
>
|
|
511
521
|
{animatedHeader?.component({
|
|
512
|
-
animatedValue: animatedValue
|
|
522
|
+
animatedValue: animatedValue,
|
|
513
523
|
})}
|
|
514
524
|
</View>
|
|
515
525
|
);
|
|
@@ -576,7 +586,7 @@ const Screen = forwardRef(
|
|
|
576
586
|
headerType={headerType}
|
|
577
587
|
heightHeader={heightHeader}
|
|
578
588
|
headerRightWidth={headerRightWidth}
|
|
579
|
-
animatedValue={animatedValue
|
|
589
|
+
animatedValue={animatedValue}
|
|
580
590
|
inputSearchProps={inputSearchProps}
|
|
581
591
|
navigation={navigation}
|
|
582
592
|
inputSearchRef={inputSearchRef}
|
|
@@ -614,9 +624,9 @@ const Screen = forwardRef(
|
|
|
614
624
|
<View>
|
|
615
625
|
<FloatingButton
|
|
616
626
|
{...floatingButtonProps}
|
|
617
|
-
animatedValue={animatedValue
|
|
627
|
+
animatedValue={animatedValue}
|
|
618
628
|
bottom={
|
|
619
|
-
Footer || isTab ? 12 :
|
|
629
|
+
Footer || isTab ? 12 : bottomInset + Spacing.S
|
|
620
630
|
}
|
|
621
631
|
/>
|
|
622
632
|
</View>
|
|
@@ -627,7 +637,7 @@ const Screen = forwardRef(
|
|
|
627
637
|
style={[
|
|
628
638
|
styles.shadow,
|
|
629
639
|
{
|
|
630
|
-
paddingBottom:
|
|
640
|
+
paddingBottom: bottomInset + Spacing.S,
|
|
631
641
|
backgroundColor: theme.colors.background.surface,
|
|
632
642
|
},
|
|
633
643
|
]}
|
package/Loader/ProgressBar.tsx
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
|
-
import React, { FC, useContext, useEffect
|
|
2
|
-
import {
|
|
1
|
+
import React, { FC, useContext, useEffect } from 'react';
|
|
2
|
+
import { View } from 'react-native';
|
|
3
|
+
import Animated, {
|
|
4
|
+
useAnimatedStyle,
|
|
5
|
+
useSharedValue,
|
|
6
|
+
withTiming,
|
|
7
|
+
} from 'react-native-reanimated';
|
|
3
8
|
import styles from './styles';
|
|
4
9
|
import { ProgressBarProps } from './types';
|
|
5
10
|
import { ApplicationContext } from '../Context';
|
|
@@ -7,20 +12,15 @@ import { Radius } from '../Consts';
|
|
|
7
12
|
|
|
8
13
|
const ProgressBar: FC<ProgressBarProps> = ({ percent = 0, style }) => {
|
|
9
14
|
const { theme } = useContext(ApplicationContext);
|
|
10
|
-
const animation =
|
|
15
|
+
const animation = useSharedValue(0);
|
|
11
16
|
|
|
12
17
|
useEffect(() => {
|
|
13
|
-
|
|
14
|
-
toValue: percent,
|
|
15
|
-
duration: 200,
|
|
16
|
-
useNativeDriver: false,
|
|
17
|
-
}).start();
|
|
18
|
+
animation.value = withTiming(percent, { duration: 200 });
|
|
18
19
|
}, [percent, animation]);
|
|
19
20
|
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
});
|
|
21
|
+
const animatedStyle = useAnimatedStyle(() => ({
|
|
22
|
+
width: `${Math.min(Math.max(animation.value, 0), 100)}%`,
|
|
23
|
+
}));
|
|
24
24
|
|
|
25
25
|
return (
|
|
26
26
|
<View
|
|
@@ -31,12 +31,14 @@ const ProgressBar: FC<ProgressBarProps> = ({ percent = 0, style }) => {
|
|
|
31
31
|
]}
|
|
32
32
|
>
|
|
33
33
|
<Animated.View
|
|
34
|
-
style={
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
34
|
+
style={[
|
|
35
|
+
{
|
|
36
|
+
height: 4,
|
|
37
|
+
borderRadius: Radius.XXS,
|
|
38
|
+
backgroundColor: theme.colors.primary,
|
|
39
|
+
},
|
|
40
|
+
animatedStyle,
|
|
41
|
+
]}
|
|
40
42
|
/>
|
|
41
43
|
</View>
|
|
42
44
|
);
|
package/Pagination/Dot.tsx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React, { FC, useContext } from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { View } from 'react-native';
|
|
3
3
|
import styles from './styles';
|
|
4
4
|
import { DotProps } from './types';
|
|
5
5
|
import { ApplicationContext } from '../Context';
|
|
@@ -13,7 +13,7 @@ const Dot: FC<DotProps> = ({ active, style }) => {
|
|
|
13
13
|
{ backgroundColor: theme.colors.background.pressed },
|
|
14
14
|
];
|
|
15
15
|
|
|
16
|
-
return <
|
|
16
|
+
return <View style={[style, dotStyle]} />;
|
|
17
17
|
};
|
|
18
18
|
|
|
19
19
|
export default Dot;
|
|
@@ -1,5 +1,12 @@
|
|
|
1
|
-
import React, { FC, useContext,
|
|
2
|
-
import {
|
|
1
|
+
import React, { FC, useContext, useState } from 'react';
|
|
2
|
+
import { View } from 'react-native';
|
|
3
|
+
import Animated, {
|
|
4
|
+
Extrapolation,
|
|
5
|
+
interpolate,
|
|
6
|
+
useAnimatedScrollHandler,
|
|
7
|
+
useAnimatedStyle,
|
|
8
|
+
useSharedValue,
|
|
9
|
+
} from 'react-native-reanimated';
|
|
3
10
|
import { ScrollIndicatorProps } from './types';
|
|
4
11
|
import styles from './styles';
|
|
5
12
|
import { ApplicationContext, MiniAppContext } from '../Context';
|
|
@@ -13,40 +20,37 @@ const PaginationScroll: FC<ScrollIndicatorProps> = ({
|
|
|
13
20
|
}) => {
|
|
14
21
|
const { theme } = useContext(ApplicationContext);
|
|
15
22
|
const context = useContext<any>(MiniAppContext);
|
|
16
|
-
const left =
|
|
23
|
+
const left = useSharedValue(0);
|
|
17
24
|
const [scrollViewWidth, setScrollViewWidth] = useState(0);
|
|
18
25
|
const [scrollContentWidth, setScrollContentWidth] = useState(0);
|
|
19
26
|
|
|
20
27
|
const showBaseLineDebug = context?.features?.showBaseLineDebug ?? false;
|
|
21
28
|
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
29
|
+
const onScroll = useAnimatedScrollHandler({
|
|
30
|
+
onScroll: (event) => {
|
|
31
|
+
left.value = event.contentOffset.x;
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
const indicatorStyle = useAnimatedStyle(() => {
|
|
36
|
+
if (!scrollViewWidth || !scrollContentWidth) {
|
|
37
|
+
return {};
|
|
30
38
|
}
|
|
31
|
-
|
|
32
|
-
|
|
39
|
+
const value = interpolate(
|
|
40
|
+
left.value,
|
|
41
|
+
[0, scrollContentWidth - scrollViewWidth],
|
|
42
|
+
[0, INDICATOR_CONTAINER_WIDTH - INDICATOR_WIDTH],
|
|
43
|
+
Extrapolation.CLAMP,
|
|
44
|
+
);
|
|
45
|
+
return { transform: [{ translateX: value }] };
|
|
46
|
+
});
|
|
33
47
|
|
|
34
48
|
const renderScrollView = () => {
|
|
35
49
|
return (
|
|
36
50
|
<Animated.ScrollView
|
|
37
|
-
ref={scrollViewRef}
|
|
38
|
-
onScroll={
|
|
39
|
-
|
|
40
|
-
{
|
|
41
|
-
nativeEvent: {
|
|
42
|
-
contentOffset: {
|
|
43
|
-
x: left,
|
|
44
|
-
},
|
|
45
|
-
},
|
|
46
|
-
},
|
|
47
|
-
],
|
|
48
|
-
{ useNativeDriver: true },
|
|
49
|
-
)}
|
|
51
|
+
ref={scrollViewRef as any}
|
|
52
|
+
onScroll={onScroll}
|
|
53
|
+
scrollEventThrottle={16}
|
|
50
54
|
alwaysBounceHorizontal={false}
|
|
51
55
|
showsHorizontalScrollIndicator={false}
|
|
52
56
|
horizontal
|
|
@@ -76,7 +80,7 @@ const PaginationScroll: FC<ScrollIndicatorProps> = ({
|
|
|
76
80
|
{
|
|
77
81
|
backgroundColor: theme.colors.primary,
|
|
78
82
|
},
|
|
79
|
-
|
|
83
|
+
indicatorStyle,
|
|
80
84
|
]}
|
|
81
85
|
/>
|
|
82
86
|
</View>
|