@momo-kits/foundation 0.162.2-test.9 → 0.162.3-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.
@@ -1,93 +1,75 @@
1
- import React, { useEffect, useRef } from 'react';
2
- import { Animated, View } from 'react-native';
1
+ import React, { useEffect } from 'react';
2
+ import { View } from 'react-native';
3
+ import Animated, {
4
+ cancelAnimation,
5
+ useAnimatedStyle,
6
+ useSharedValue,
7
+ withRepeat,
8
+ withSequence,
9
+ withSpring,
10
+ withTiming,
11
+ } from 'react-native-reanimated';
3
12
  import { BadgeDotProps } from './types';
4
13
  import styles from './styles';
5
14
 
6
15
  const DURATION = 500;
7
16
 
8
17
  const BadgeDotAnimation = ({ size, style }: BadgeDotProps) => {
9
- // Refs for animated values
10
- const scaleAnim = useRef(new Animated.Value(1)).current;
11
- const waveScaleAnim = useRef(new Animated.Value(1)).current;
12
- const waveOpacityAnim = useRef(new Animated.Value(0)).current;
18
+ const scaleAnim = useSharedValue(1);
19
+ const waveScaleAnim = useSharedValue(1);
20
+ const waveOpacityAnim = useSharedValue(0);
13
21
 
14
22
  const dotStyle =
15
23
  size === 'small' ? styles.dotAnimationSmall : styles.dotAnimation;
16
24
  const waveStyle = size === 'small' ? styles.waveSmall : styles.wave;
17
25
 
18
26
  useEffect(() => {
19
- // Infinite loop animation for the scale and wave effect
20
- const animation = Animated.loop(
21
- Animated.parallel([
22
- // Dot pulse animation
23
- Animated.sequence([
24
- Animated.spring(scaleAnim, {
25
- toValue: 1, // Scale up slightly
26
- friction: 5, // Controls the "bounciness" of the spring
27
- tension: 30, // Controls the "stiffness" of the spring
28
- useNativeDriver: true,
29
- }),
30
- Animated.spring(scaleAnim, {
31
- toValue: 1.1,
32
- friction: 5,
33
- tension: 30,
34
- useNativeDriver: true,
35
- }),
36
- ]), // Wave animation
37
- Animated.sequence([
38
- Animated.timing(waveScaleAnim, {
39
- toValue: 2.5,
40
- duration: DURATION * 3,
41
- useNativeDriver: true,
42
- }),
43
- Animated.timing(waveScaleAnim, {
44
- toValue: 1, // Reset wave size
45
- duration: 0,
46
- useNativeDriver: true,
47
- }),
48
- ]), // Wave opacity animation
49
- Animated.sequence([
50
- Animated.timing(waveOpacityAnim, {
51
- toValue: 0.3, // Wave becomes visible
52
- duration: DURATION * 2,
53
- useNativeDriver: true,
54
- }),
55
- Animated.timing(waveOpacityAnim, {
56
- toValue: 0, // Wave fades out
57
- duration: DURATION,
58
- useNativeDriver: true,
59
- }),
60
- ]),
61
- ]),
62
- );
63
- animation.start();
27
+ const springCfg = { damping: 5, stiffness: 30, mass: 1 };
64
28
 
29
+ scaleAnim.value = withRepeat(
30
+ withSequence(
31
+ withSpring(1, springCfg),
32
+ withSpring(1.1, springCfg),
33
+ ),
34
+ -1,
35
+ false,
36
+ );
37
+ waveScaleAnim.value = withRepeat(
38
+ withSequence(
39
+ withTiming(2.5, { duration: DURATION * 3 }),
40
+ withTiming(1, { duration: 0 }),
41
+ ),
42
+ -1,
43
+ false,
44
+ );
45
+ waveOpacityAnim.value = withRepeat(
46
+ withSequence(
47
+ withTiming(0.3, { duration: DURATION * 2 }),
48
+ withTiming(0, { duration: DURATION }),
49
+ ),
50
+ -1,
51
+ false,
52
+ );
65
53
  return () => {
66
- animation.stop();
54
+ cancelAnimation(scaleAnim);
55
+ cancelAnimation(waveScaleAnim);
56
+ cancelAnimation(waveOpacityAnim);
67
57
  };
68
58
  }, [scaleAnim, waveOpacityAnim, waveScaleAnim]);
69
59
 
60
+ const waveAnimatedStyle = useAnimatedStyle(() => ({
61
+ transform: [{ scale: waveScaleAnim.value }],
62
+ opacity: waveOpacityAnim.value,
63
+ }));
64
+
65
+ const dotAnimatedStyle = useAnimatedStyle(() => ({
66
+ transform: [{ scale: scaleAnim.value }],
67
+ }));
68
+
70
69
  return (
71
70
  <View style={[styles.dotAnimationContainer, style]}>
72
- {/* Wave Animation */}
73
- <Animated.View
74
- style={[
75
- waveStyle,
76
- {
77
- transform: [{ scale: waveScaleAnim }],
78
- opacity: waveOpacityAnim,
79
- },
80
- ]}
81
- />
82
- {/* Dot Animation */}
83
- <Animated.View
84
- style={[
85
- dotStyle,
86
- {
87
- transform: [{ scale: scaleAnim }],
88
- },
89
- ]}
90
- />
71
+ <Animated.View style={[waveStyle, waveAnimatedStyle]} />
72
+ <Animated.View style={[dotStyle, dotAnimatedStyle]} />
91
73
  </View>
92
74
  );
93
75
  };
package/Button/index.tsx CHANGED
@@ -1,6 +1,5 @@
1
1
  import React, { FC, useContext, useRef } from 'react';
2
2
  import {
3
- Animated,
4
3
  StyleSheet,
5
4
  TouchableOpacity,
6
5
  TouchableOpacityProps,
@@ -29,8 +28,6 @@ import Reanimated, {
29
28
  withTiming,
30
29
  } from 'react-native-reanimated';
31
30
 
32
- const AnimationLinear = Animated.createAnimatedComponent(LinearGradient);
33
-
34
31
  export interface ButtonProps extends TouchableOpacityProps {
35
32
  /**
36
33
  * Defines the visual style of the button.
@@ -359,7 +356,7 @@ const Button: FC<ButtonProps> = ({
359
356
  {renderTitle()}
360
357
  {renderIcon('right')}
361
358
  {gradientPros && (
362
- <AnimationLinear {...gradientPros} style={styles.gradientView} />
359
+ <LinearGradient {...gradientPros} style={styles.gradientView} />
363
360
  )}
364
361
  {gradientPros && <View style={styles.strokeView} />}
365
362
  </Reanimated.View>
package/Input/Input.tsx CHANGED
@@ -77,7 +77,8 @@ const Input = forwardRef(
77
77
  const scaledFontSize = useScaleSize(14);
78
78
  const scaleHeight = useScaleSize(size === 'small' ? 48 : 56);
79
79
  const [focused, setFocused] = useState(false);
80
- const haveValue = !!value || !!defaultValue;
80
+ const [internalText, setInternalText] = useState(defaultValue || '');
81
+ const haveValue = value !== undefined ? !!value : !!internalText;
81
82
  const inputRef = useRef<TextInput | null>(null);
82
83
  const componentName = 'Input';
83
84
 
@@ -95,6 +96,9 @@ const Input = forwardRef(
95
96
  };
96
97
 
97
98
  const _onChangeText = (text: string) => {
99
+ if (value === undefined) {
100
+ setInternalText(text);
101
+ }
98
102
  onChangeText?.(text);
99
103
  };
100
104
 
@@ -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 = useRef(new Animated.Value(0)).current;
44
+ const opacity = useSharedValue(0);
36
45
 
37
46
  useEffect(() => {
38
- Animated.loop(
39
- Animated.sequence([
40
- Animated.timing(opacity, {
41
- toValue: 1,
42
- duration: DURATION,
43
- useNativeDriver: true,
44
- }),
45
- Animated.delay(DURATION * 2),
46
- Animated.timing(opacity, {
47
- toValue: 0,
48
- duration: DURATION,
49
- useNativeDriver: true,
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
- height: useScaleSize(12),
62
- width: 1,
63
- backgroundColor: theme.colors.primary,
64
- opacity,
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
  -
@@ -69,7 +69,8 @@ const InputPhoneNumber = forwardRef(
69
69
  const context = useContext<any>(MiniAppContext);
70
70
  const scaleHeight = useScaleSize(size === 'small' ? 48 : 56);
71
71
  const [focused, setFocused] = useState(false);
72
- const haveValue = !!value || !!defaultValue;
72
+ const [internalText, setInternalText] = useState(defaultValue || '');
73
+ const haveValue = value !== undefined ? !!value : !!internalText;
73
74
  const inputRef = useRef<TextInput | null>(null);
74
75
  const componentName = 'InputPhoneNumber';
75
76
 
@@ -86,6 +87,9 @@ const InputPhoneNumber = forwardRef(
86
87
  };
87
88
 
88
89
  const _onChangeText = (text: string) => {
90
+ if (value === undefined) {
91
+ setInternalText(text);
92
+ }
89
93
  onChangeText?.(text);
90
94
  };
91
95
 
@@ -1,20 +1,24 @@
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
+ } from 'react-native-reanimated';
15
15
  import { ApplicationContext } from '../Context';
16
16
  import { Icon } from '../Icon';
17
17
  import { useScaleSize } from '../Text';
18
+ import {
19
+ AnimatedCompatValue,
20
+ useNormalizedSharedValue,
21
+ } from '../Application/utils';
18
22
 
19
23
  export interface FloatingButtonProps {
20
24
  label?: string;
@@ -23,7 +27,7 @@ export interface FloatingButtonProps {
23
27
  icon?: string;
24
28
  iconColor?: string;
25
29
  size?: 'small' | 'large';
26
- animatedValue?: Animated.Value;
30
+ animatedValue?: AnimatedCompatValue;
27
31
  bottom?: number;
28
32
  renderComponent?: () => React.ReactNode;
29
33
  }
@@ -40,70 +44,71 @@ export const FloatingButton: React.FC<FloatingButtonProps> = ({
40
44
  bottom = 12,
41
45
  }: FloatingButtonProps) => {
42
46
  const { theme } = useContext(ApplicationContext);
47
+ const enabled = animatedValue != null;
48
+ const sharedValue = useNormalizedSharedValue(animatedValue);
43
49
  const scaledFontSize = useScaleSize(16);
44
50
  const scaledLineHeight = useScaleSize(22);
45
- const maxWidth = useRef(0);
51
+ const maxWidth = useSharedValue(0);
46
52
  const minWidth = size === 'small' ? 36 : 48;
47
- const [opacityAnimated] = useState(new Animated.Value(0)); // Initial opacity set to 0
48
- const [widthAnimated, setWidthAnimated] = useState<Animated.Value>();
49
- const lastOffset = useRef(0);
50
- const lastDirection = useRef<string>(null);
51
- const [showText, setShowText] = React.useState(true);
53
+ const opacityAnimated = useSharedValue(0);
54
+ const widthAnimated = useSharedValue<number | null>(null);
55
+ const lastOffset = useSharedValue(0);
56
+ const lastDirection = useSharedValue<string | null>(null);
57
+ const [showText, setShowText] = useState(true);
52
58
 
53
59
  useEffect(() => {
54
60
  if (!label) return;
61
+ opacityAnimated.value = withTiming(1, { duration: 100 });
62
+ }, [label, opacityAnimated]);
55
63
 
56
- Animated.timing(opacityAnimated, {
57
- toValue: 1,
58
- duration: 100,
59
- useNativeDriver: true,
60
- }).start();
61
-
62
- const listener = animatedValue?.addListener(({ value }) => {
63
- if (value !== lastOffset.current && value > 0) {
64
- const direction = value > lastOffset.current ? 'down' : 'up';
65
- lastOffset.current = value;
66
- if (lastDirection.current !== direction) {
67
- lastDirection.current = direction;
64
+ useAnimatedReaction(
65
+ () => sharedValue.value,
66
+ (value) => {
67
+ 'worklet';
68
+ if (!label || !enabled) return;
69
+ if (value !== lastOffset.value && value > 0) {
70
+ const direction = value > lastOffset.value ? 'down' : 'up';
71
+ lastOffset.value = value;
72
+ if (lastDirection.value !== direction) {
73
+ lastDirection.value = direction;
68
74
  if (direction === 'down') {
69
- Animated.timing(opacityAnimated, {
70
- toValue: 0,
71
- duration: 100,
72
- useNativeDriver: true,
73
- }).start();
74
- Animated.timing(widthAnimated!, {
75
- toValue: minWidth,
76
- duration: 100,
77
- useNativeDriver: false,
78
- }).start(() => setShowText(false));
75
+ opacityAnimated.value = withTiming(0, { duration: 100 });
76
+ widthAnimated.value = withTiming(
77
+ minWidth,
78
+ { duration: 100 },
79
+ (finished) => {
80
+ if (finished) runOnJS(setShowText)(false);
81
+ },
82
+ );
79
83
  } else {
80
- Animated.timing(opacityAnimated, {
81
- toValue: 1,
82
- duration: 100,
83
- useNativeDriver: true,
84
- }).start();
85
- Animated.timing(widthAnimated!, {
86
- toValue: maxWidth.current,
87
- duration: 100,
88
- useNativeDriver: false,
89
- }).start(() => setShowText(true));
84
+ opacityAnimated.value = withTiming(1, { duration: 100 });
85
+ widthAnimated.value = withTiming(
86
+ maxWidth.value,
87
+ { duration: 100 },
88
+ (finished) => {
89
+ if (finished) runOnJS(setShowText)(true);
90
+ },
91
+ );
90
92
  }
91
93
  }
92
94
  }
93
- });
95
+ },
96
+ [label, enabled, minWidth],
97
+ );
94
98
 
95
- return () => {
96
- if (listener) {
97
- animatedValue?.removeListener(listener);
98
- }
99
- };
100
- }, [animatedValue, label, minWidth, opacityAnimated, widthAnimated]);
99
+ const containerStyle = useAnimatedStyle(() => ({
100
+ width: widthAnimated.value ?? undefined,
101
+ }));
102
+
103
+ const labelStyle = useAnimatedStyle(() => ({
104
+ opacity: opacityAnimated.value,
105
+ }));
101
106
 
102
107
  const handleLayout = (event: LayoutChangeEvent) => {
103
108
  const layout = event.nativeEvent.layout;
104
- if (widthAnimated) return;
105
- maxWidth.current = layout.width;
106
- setWidthAnimated(new Animated.Value(layout.width));
109
+ if (widthAnimated.value != null) return;
110
+ maxWidth.value = layout.width;
111
+ widthAnimated.value = layout.width;
107
112
  };
108
113
 
109
114
  if (renderComponent) {
@@ -132,11 +137,11 @@ export const FloatingButton: React.FC<FloatingButtonProps> = ({
132
137
  {
133
138
  right: position === 'right' ? 12 : undefined,
134
139
  alignSelf: position === 'center' ? 'center' : 'flex-end',
135
- width: widthAnimated,
136
140
  height: size === 'small' ? 36 : 48,
137
141
  backgroundColor: theme.colors.primary,
138
142
  bottom,
139
143
  },
144
+ containerStyle,
140
145
  ]}
141
146
  >
142
147
  <TouchableOpacity
@@ -156,9 +161,9 @@ export const FloatingButton: React.FC<FloatingButtonProps> = ({
156
161
  {
157
162
  fontSize: scaledFontSize,
158
163
  lineHeight: scaledLineHeight,
159
- opacity: opacityAnimated,
160
164
  color: 'white',
161
165
  },
166
+ labelStyle,
162
167
  ]}
163
168
  numberOfLines={1}
164
169
  >