@momo-kits/foundation 0.161.2-test.1 → 0.162.2-test.5
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 +4 -7
- package/Application/BottomTab/BottomTabBar.tsx +15 -15
- package/Application/BottomTab/index.tsx +56 -34
- package/Application/Components/BackgroundImageView.tsx +6 -22
- package/Application/Components/HeaderAnimated.tsx +29 -32
- package/Application/Components/HeaderBackground.tsx +26 -20
- package/Application/Components/HeaderExtendHeader.tsx +94 -114
- package/Application/Components/HeaderTitle.tsx +10 -39
- package/Application/Components/SearchHeader.tsx +21 -16
- package/Application/NavigationContainer.tsx +31 -2
- package/Application/ScaleSizeProvider.tsx +5 -3
- package/Application/WidgetContainer.tsx +1 -1
- package/Application/types.ts +6 -5
- package/Application/utils.tsx +3 -4
- package/Badge/BadgeDotAnimation.tsx +71 -53
- package/Button/index.tsx +4 -1
- package/Context/index.ts +6 -3
- package/Input/InputOTP.tsx +23 -38
- package/Layout/FloatingButton.tsx +59 -59
- package/Layout/Screen.tsx +61 -71
- package/Loader/ProgressBar.tsx +18 -20
- package/Pagination/Dot.tsx +2 -2
- package/Pagination/PaginationScroll.tsx +27 -31
- package/Skeleton/index.tsx +24 -32
- package/Text/utils.ts +11 -6
- package/package.json +1 -1
package/Layout/Screen.tsx
CHANGED
|
@@ -12,6 +12,7 @@ import React, {
|
|
|
12
12
|
useRef,
|
|
13
13
|
} from 'react';
|
|
14
14
|
import {
|
|
15
|
+
Animated,
|
|
15
16
|
KeyboardAvoidingView,
|
|
16
17
|
NativeScrollEvent,
|
|
17
18
|
NativeSyntheticEvent,
|
|
@@ -24,13 +25,6 @@ import {
|
|
|
24
25
|
View,
|
|
25
26
|
ViewProps,
|
|
26
27
|
} from 'react-native';
|
|
27
|
-
import Animated, {
|
|
28
|
-
runOnJS,
|
|
29
|
-
useAnimatedScrollHandler,
|
|
30
|
-
useSharedValue,
|
|
31
|
-
withTiming,
|
|
32
|
-
type SharedValue,
|
|
33
|
-
} from 'react-native-reanimated';
|
|
34
28
|
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
35
29
|
import { ApplicationContext, ScreenContext } from '../Context';
|
|
36
30
|
import Navigation from '../Application/Navigation';
|
|
@@ -144,7 +138,7 @@ export interface ScreenProps extends ViewProps {
|
|
|
144
138
|
/**
|
|
145
139
|
* Optional. Animated value for header.
|
|
146
140
|
*/
|
|
147
|
-
animatedValue?:
|
|
141
|
+
animatedValue?: Animated.Value;
|
|
148
142
|
|
|
149
143
|
/**
|
|
150
144
|
* Optional. If `true`, use shadow header.
|
|
@@ -201,23 +195,18 @@ const Screen = forwardRef(
|
|
|
201
195
|
const screen: any = useContext(ScreenContext);
|
|
202
196
|
const insets = useSafeAreaInsets();
|
|
203
197
|
const heightHeader = useHeaderHeight();
|
|
204
|
-
const
|
|
205
|
-
|
|
198
|
+
const animatedValue = useRef<Animated.Value>(
|
|
199
|
+
customAnimatedValue || new Animated.Value(0),
|
|
200
|
+
);
|
|
206
201
|
const currentTint = useRef<string | undefined>(undefined);
|
|
207
202
|
const isTab = navigation?.instance?.getState?.()?.type === 'tab';
|
|
208
203
|
|
|
209
|
-
let handleScroll
|
|
204
|
+
let handleScroll;
|
|
210
205
|
let Component: any = View;
|
|
211
206
|
|
|
212
|
-
|
|
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;
|
|
207
|
+
let keyboardOffset = heightHeader - Math.min(insets.bottom, 21);
|
|
219
208
|
if (headerType === 'extended' || animatedHeader || inputSearchProps) {
|
|
220
|
-
keyboardOffset = -
|
|
209
|
+
keyboardOffset = -Math.min(insets.bottom, 21);
|
|
221
210
|
}
|
|
222
211
|
|
|
223
212
|
/**
|
|
@@ -259,8 +248,9 @@ const Screen = forwardRef(
|
|
|
259
248
|
interpolate={{
|
|
260
249
|
inputRange: [0, 50],
|
|
261
250
|
outputRange: [1, 0],
|
|
251
|
+
extrapolate: 'clamp',
|
|
262
252
|
}}
|
|
263
|
-
animatedValue={animatedValue}
|
|
253
|
+
animatedValue={animatedValue.current}
|
|
264
254
|
/>
|
|
265
255
|
),
|
|
266
256
|
};
|
|
@@ -275,7 +265,7 @@ const Screen = forwardRef(
|
|
|
275
265
|
headerBackground: (props: any) => (
|
|
276
266
|
<HeaderBackground
|
|
277
267
|
{...props}
|
|
278
|
-
animatedValue={animatedValue}
|
|
268
|
+
animatedValue={animatedValue.current}
|
|
279
269
|
useShadowHeader={useShadowHeader}
|
|
280
270
|
headerBackground={headerBackground}
|
|
281
271
|
gradientColor={gradientColor}
|
|
@@ -294,8 +284,9 @@ const Screen = forwardRef(
|
|
|
294
284
|
interpolate={{
|
|
295
285
|
inputRange: [0, 50],
|
|
296
286
|
outputRange: [1, 0],
|
|
287
|
+
extrapolate: 'clamp',
|
|
297
288
|
}}
|
|
298
|
-
animatedValue={animatedValue}
|
|
289
|
+
animatedValue={animatedValue.current}
|
|
299
290
|
/>
|
|
300
291
|
),
|
|
301
292
|
};
|
|
@@ -330,7 +321,7 @@ const Screen = forwardRef(
|
|
|
330
321
|
headerBackground: (props: any) => (
|
|
331
322
|
<HeaderBackground
|
|
332
323
|
{...props}
|
|
333
|
-
animatedValue={animatedValue}
|
|
324
|
+
animatedValue={animatedValue.current}
|
|
334
325
|
useGradient={false}
|
|
335
326
|
useShadowHeader={useShadowHeader}
|
|
336
327
|
headerBackground={headerBackground}
|
|
@@ -368,8 +359,9 @@ const Screen = forwardRef(
|
|
|
368
359
|
interpolate={{
|
|
369
360
|
inputRange: [0, 50],
|
|
370
361
|
outputRange: [1, 0],
|
|
362
|
+
extrapolate: 'clamp',
|
|
371
363
|
}}
|
|
372
|
-
animatedValue={animatedValue}
|
|
364
|
+
animatedValue={animatedValue.current}
|
|
373
365
|
/>
|
|
374
366
|
),
|
|
375
367
|
};
|
|
@@ -398,7 +390,7 @@ const Screen = forwardRef(
|
|
|
398
390
|
headerLeft: (props: any) =>
|
|
399
391
|
params?.hiddenBack ? null : <HeaderLeft {...props} />,
|
|
400
392
|
headerTitle: () => (
|
|
401
|
-
<SearchHeader {...params} animatedValue={animatedValue} />
|
|
393
|
+
<SearchHeader {...params} animatedValue={animatedValue.current} />
|
|
402
394
|
),
|
|
403
395
|
};
|
|
404
396
|
|
|
@@ -449,51 +441,45 @@ const Screen = forwardRef(
|
|
|
449
441
|
});
|
|
450
442
|
});
|
|
451
443
|
|
|
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
|
-
|
|
491
444
|
/**
|
|
492
445
|
* animated when use scroll && animated value
|
|
493
446
|
*/
|
|
494
447
|
if (scrollable) {
|
|
495
448
|
Component = Animated.ScrollView;
|
|
496
|
-
handleScroll =
|
|
449
|
+
handleScroll = Animated.event(
|
|
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
|
+
);
|
|
497
483
|
}
|
|
498
484
|
|
|
499
485
|
/**
|
|
@@ -503,7 +489,11 @@ const Screen = forwardRef(
|
|
|
503
489
|
const handleScrollEnd = (e: NativeSyntheticEvent<NativeScrollEvent>) => {
|
|
504
490
|
const offsetY = e.nativeEvent.contentOffset.y;
|
|
505
491
|
if (inputSearchProps && offsetY < 100 && offsetY > 0) {
|
|
506
|
-
animatedValue.
|
|
492
|
+
Animated.timing(animatedValue.current, {
|
|
493
|
+
toValue: 0,
|
|
494
|
+
useNativeDriver: true,
|
|
495
|
+
duration: 300,
|
|
496
|
+
}).start();
|
|
507
497
|
ref?.scrollTo?.({ y: 0, animated: true });
|
|
508
498
|
}
|
|
509
499
|
scrollViewProps?.onScrollEndDrag?.(e);
|
|
@@ -519,7 +509,7 @@ const Screen = forwardRef(
|
|
|
519
509
|
style={[styles.screenBanner, { maxHeight: 210 + layoutOffset }]}
|
|
520
510
|
>
|
|
521
511
|
{animatedHeader?.component({
|
|
522
|
-
animatedValue: animatedValue,
|
|
512
|
+
animatedValue: animatedValue.current,
|
|
523
513
|
})}
|
|
524
514
|
</View>
|
|
525
515
|
);
|
|
@@ -586,7 +576,7 @@ const Screen = forwardRef(
|
|
|
586
576
|
headerType={headerType}
|
|
587
577
|
heightHeader={heightHeader}
|
|
588
578
|
headerRightWidth={headerRightWidth}
|
|
589
|
-
animatedValue={animatedValue}
|
|
579
|
+
animatedValue={animatedValue.current}
|
|
590
580
|
inputSearchProps={inputSearchProps}
|
|
591
581
|
navigation={navigation}
|
|
592
582
|
inputSearchRef={inputSearchRef}
|
|
@@ -624,9 +614,9 @@ const Screen = forwardRef(
|
|
|
624
614
|
<View>
|
|
625
615
|
<FloatingButton
|
|
626
616
|
{...floatingButtonProps}
|
|
627
|
-
animatedValue={animatedValue}
|
|
617
|
+
animatedValue={animatedValue.current}
|
|
628
618
|
bottom={
|
|
629
|
-
Footer || isTab ? 12 :
|
|
619
|
+
Footer || isTab ? 12 : Math.min(insets.bottom, 21) + Spacing.S
|
|
630
620
|
}
|
|
631
621
|
/>
|
|
632
622
|
</View>
|
|
@@ -637,7 +627,7 @@ const Screen = forwardRef(
|
|
|
637
627
|
style={[
|
|
638
628
|
styles.shadow,
|
|
639
629
|
{
|
|
640
|
-
paddingBottom:
|
|
630
|
+
paddingBottom: Math.min(insets.bottom, 21) + Spacing.S,
|
|
641
631
|
backgroundColor: theme.colors.background.surface,
|
|
642
632
|
},
|
|
643
633
|
]}
|
package/Loader/ProgressBar.tsx
CHANGED
|
@@ -1,10 +1,5 @@
|
|
|
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';
|
|
1
|
+
import React, { FC, useContext, useEffect, useRef } from 'react';
|
|
2
|
+
import { Animated, View } from 'react-native';
|
|
8
3
|
import styles from './styles';
|
|
9
4
|
import { ProgressBarProps } from './types';
|
|
10
5
|
import { ApplicationContext } from '../Context';
|
|
@@ -12,15 +7,20 @@ import { Radius } from '../Consts';
|
|
|
12
7
|
|
|
13
8
|
const ProgressBar: FC<ProgressBarProps> = ({ percent = 0, style }) => {
|
|
14
9
|
const { theme } = useContext(ApplicationContext);
|
|
15
|
-
const animation =
|
|
10
|
+
const animation = useRef(new Animated.Value(0)).current;
|
|
16
11
|
|
|
17
12
|
useEffect(() => {
|
|
18
|
-
|
|
13
|
+
Animated.timing(animation, {
|
|
14
|
+
toValue: percent,
|
|
15
|
+
duration: 200,
|
|
16
|
+
useNativeDriver: false,
|
|
17
|
+
}).start();
|
|
19
18
|
}, [percent, animation]);
|
|
20
19
|
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
const width = animation.interpolate({
|
|
21
|
+
inputRange: [0, 100],
|
|
22
|
+
outputRange: ['0%', '100%'],
|
|
23
|
+
});
|
|
24
24
|
|
|
25
25
|
return (
|
|
26
26
|
<View
|
|
@@ -31,14 +31,12 @@ const ProgressBar: FC<ProgressBarProps> = ({ percent = 0, style }) => {
|
|
|
31
31
|
]}
|
|
32
32
|
>
|
|
33
33
|
<Animated.View
|
|
34
|
-
style={
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
animatedStyle,
|
|
41
|
-
]}
|
|
34
|
+
style={{
|
|
35
|
+
height: 4,
|
|
36
|
+
borderRadius: Radius.XXS,
|
|
37
|
+
width,
|
|
38
|
+
backgroundColor: theme.colors.primary,
|
|
39
|
+
}}
|
|
42
40
|
/>
|
|
43
41
|
</View>
|
|
44
42
|
);
|
package/Pagination/Dot.tsx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React, { FC, useContext } from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { Animated } 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 <View style={[style, dotStyle]} />;
|
|
16
|
+
return <Animated.View style={[style, dotStyle]} />;
|
|
17
17
|
};
|
|
18
18
|
|
|
19
19
|
export default Dot;
|
|
@@ -1,12 +1,5 @@
|
|
|
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';
|
|
1
|
+
import React, { FC, useContext, useRef, useState } from 'react';
|
|
2
|
+
import { Animated, View } from 'react-native';
|
|
10
3
|
import { ScrollIndicatorProps } from './types';
|
|
11
4
|
import styles from './styles';
|
|
12
5
|
import { ApplicationContext, MiniAppContext } from '../Context';
|
|
@@ -20,37 +13,40 @@ const PaginationScroll: FC<ScrollIndicatorProps> = ({
|
|
|
20
13
|
}) => {
|
|
21
14
|
const { theme } = useContext(ApplicationContext);
|
|
22
15
|
const context = useContext<any>(MiniAppContext);
|
|
23
|
-
const left =
|
|
16
|
+
const left = useRef(new Animated.Value(0)).current;
|
|
24
17
|
const [scrollViewWidth, setScrollViewWidth] = useState(0);
|
|
25
18
|
const [scrollContentWidth, setScrollContentWidth] = useState(0);
|
|
26
19
|
|
|
27
20
|
const showBaseLineDebug = context?.features?.showBaseLineDebug ?? false;
|
|
28
21
|
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
return {};
|
|
22
|
+
const translateX = () => {
|
|
23
|
+
if (scrollViewWidth && scrollContentWidth) {
|
|
24
|
+
const value = left.interpolate({
|
|
25
|
+
inputRange: [0, scrollContentWidth - scrollViewWidth],
|
|
26
|
+
outputRange: [0, INDICATOR_CONTAINER_WIDTH - INDICATOR_WIDTH],
|
|
27
|
+
extrapolate: 'clamp',
|
|
28
|
+
});
|
|
29
|
+
return { transform: [{ translateX: value }] };
|
|
38
30
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
[0, scrollContentWidth - scrollViewWidth],
|
|
42
|
-
[0, INDICATOR_CONTAINER_WIDTH - INDICATOR_WIDTH],
|
|
43
|
-
Extrapolation.CLAMP,
|
|
44
|
-
);
|
|
45
|
-
return { transform: [{ translateX: value }] };
|
|
46
|
-
});
|
|
31
|
+
return {};
|
|
32
|
+
};
|
|
47
33
|
|
|
48
34
|
const renderScrollView = () => {
|
|
49
35
|
return (
|
|
50
36
|
<Animated.ScrollView
|
|
51
|
-
ref={scrollViewRef
|
|
52
|
-
onScroll={
|
|
53
|
-
|
|
37
|
+
ref={scrollViewRef}
|
|
38
|
+
onScroll={Animated.event(
|
|
39
|
+
[
|
|
40
|
+
{
|
|
41
|
+
nativeEvent: {
|
|
42
|
+
contentOffset: {
|
|
43
|
+
x: left,
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
],
|
|
48
|
+
{ useNativeDriver: true },
|
|
49
|
+
)}
|
|
54
50
|
alwaysBounceHorizontal={false}
|
|
55
51
|
showsHorizontalScrollIndicator={false}
|
|
56
52
|
horizontal
|
|
@@ -80,7 +76,7 @@ const PaginationScroll: FC<ScrollIndicatorProps> = ({
|
|
|
80
76
|
{
|
|
81
77
|
backgroundColor: theme.colors.primary,
|
|
82
78
|
},
|
|
83
|
-
|
|
79
|
+
translateX(),
|
|
84
80
|
]}
|
|
85
81
|
/>
|
|
86
82
|
</View>
|
package/Skeleton/index.tsx
CHANGED
|
@@ -1,20 +1,13 @@
|
|
|
1
|
-
import React, { useContext, useEffect, useState } from 'react';
|
|
1
|
+
import React, { useContext, useEffect, useMemo, useRef, useState } from 'react';
|
|
2
2
|
import {
|
|
3
|
+
Animated,
|
|
4
|
+
Easing,
|
|
3
5
|
LayoutAnimation,
|
|
4
6
|
Platform,
|
|
5
7
|
StyleSheet,
|
|
6
8
|
UIManager,
|
|
7
9
|
View,
|
|
8
10
|
} from 'react-native';
|
|
9
|
-
import Animated, {
|
|
10
|
-
cancelAnimation,
|
|
11
|
-
Easing,
|
|
12
|
-
interpolate,
|
|
13
|
-
useAnimatedStyle,
|
|
14
|
-
useSharedValue,
|
|
15
|
-
withRepeat,
|
|
16
|
-
withTiming,
|
|
17
|
-
} from 'react-native-reanimated';
|
|
18
11
|
import LinearGradient from 'react-native-linear-gradient';
|
|
19
12
|
import { SkeletonTypes } from './types';
|
|
20
13
|
import { Colors, Styles } from '../Consts';
|
|
@@ -27,31 +20,32 @@ const Skeleton: React.FC<SkeletonTypes> = ({ style }) => {
|
|
|
27
20
|
const PRIMARY_COLOR = Colors.black_05;
|
|
28
21
|
const HIGHLIGHT_COLOR1 = Colors.black_05;
|
|
29
22
|
const HIGHLIGHT_COLOR2 = Colors.black_03;
|
|
30
|
-
const
|
|
23
|
+
const beginShimmerPosition = useRef(new Animated.Value(0)).current;
|
|
31
24
|
|
|
32
25
|
const shimmerColors = [HIGHLIGHT_COLOR1, HIGHLIGHT_COLOR2, HIGHLIGHT_COLOR1];
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
26
|
+
const linearTranslate = beginShimmerPosition.interpolate({
|
|
27
|
+
inputRange: [0, 1],
|
|
28
|
+
outputRange: [-width, width],
|
|
29
|
+
});
|
|
30
|
+
const animatedValue = useMemo(() => {
|
|
31
|
+
return Animated.loop(
|
|
32
|
+
Animated.timing(beginShimmerPosition, {
|
|
33
|
+
toValue: 1,
|
|
38
34
|
duration: 1000,
|
|
39
35
|
easing: Easing.linear,
|
|
36
|
+
useNativeDriver: Platform.OS !== 'web',
|
|
40
37
|
}),
|
|
41
|
-
-1,
|
|
42
|
-
false,
|
|
43
38
|
);
|
|
39
|
+
}, [beginShimmerPosition]);
|
|
40
|
+
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
animatedValue.start();
|
|
44
43
|
screen?.onLoading?.(true);
|
|
45
44
|
return () => {
|
|
46
|
-
|
|
45
|
+
animatedValue.stop();
|
|
47
46
|
screen?.onLoading?.(false);
|
|
48
47
|
};
|
|
49
|
-
}, [
|
|
50
|
-
|
|
51
|
-
const shimmerStyle = useAnimatedStyle(() => {
|
|
52
|
-
const translateX = interpolate(progress.value, [0, 1], [-width, width]);
|
|
53
|
-
return { transform: [{ translateX }] };
|
|
54
|
-
});
|
|
48
|
+
}, [animatedValue, screen]);
|
|
55
49
|
|
|
56
50
|
const onLayout = (newWidth: number) => {
|
|
57
51
|
if (newWidth !== width) {
|
|
@@ -66,13 +60,11 @@ const Skeleton: React.FC<SkeletonTypes> = ({ style }) => {
|
|
|
66
60
|
style={[Styles.flex, { backgroundColor: PRIMARY_COLOR }]}
|
|
67
61
|
>
|
|
68
62
|
<Animated.View
|
|
69
|
-
style={
|
|
70
|
-
{
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
shimmerStyle,
|
|
75
|
-
]}
|
|
63
|
+
style={{
|
|
64
|
+
transform: [{ translateX: linearTranslate }],
|
|
65
|
+
width: '60%',
|
|
66
|
+
height: '100%',
|
|
67
|
+
}}
|
|
76
68
|
>
|
|
77
69
|
<LinearGradient
|
|
78
70
|
style={[StyleSheet.absoluteFill]}
|
package/Text/utils.ts
CHANGED
|
@@ -7,13 +7,18 @@ const DEFAULT_SCREEN_SIZE = 375;
|
|
|
7
7
|
const MAX_FONT_SCALE = 1.5;
|
|
8
8
|
const MAX_DEVICE_SCALE = 5;
|
|
9
9
|
|
|
10
|
-
const useScaleSize = (size: number,
|
|
11
|
-
const {
|
|
10
|
+
const useScaleSize = (size: number, userScaleRate?: number) => {
|
|
11
|
+
const { useOSFontScale = false, userScaleRate: ctxRate } =
|
|
12
|
+
useContext(ScaleSizeContext);
|
|
12
13
|
const fontScale = PixelRatio.getFontScale();
|
|
13
14
|
const { width } = useWindowDimensions();
|
|
14
15
|
const deviceScale = width / DEFAULT_SCREEN_SIZE;
|
|
15
16
|
|
|
16
|
-
|
|
17
|
+
// rate user tự chỉnh: param override > config > 1 (không scale)
|
|
18
|
+
const customRate = userScaleRate ?? ctxRate ?? 1;
|
|
19
|
+
// useOSFontScale bật -> lấy scale OS đè lên, ignore custom rate;
|
|
20
|
+
// tắt -> dùng custom rate. Cap cứng MAX_FONT_SCALE (150%) cả hai.
|
|
21
|
+
const appliedRate = useOSFontScale ? fontScale : customRate;
|
|
17
22
|
|
|
18
23
|
let fontSizeDeviceScale = size;
|
|
19
24
|
let fontSizeOSScale = size;
|
|
@@ -25,10 +30,10 @@ const useScaleSize = (size: number, scaleRate?: number) => {
|
|
|
25
30
|
);
|
|
26
31
|
}
|
|
27
32
|
|
|
28
|
-
if (
|
|
33
|
+
if (appliedRate > 1) {
|
|
29
34
|
fontSizeOSScale = Math.min(
|
|
30
|
-
|
|
31
|
-
fontSizeOSScale *
|
|
35
|
+
appliedRate * fontSizeOSScale,
|
|
36
|
+
fontSizeOSScale * MAX_FONT_SCALE,
|
|
32
37
|
);
|
|
33
38
|
}
|
|
34
39
|
|