@momo-kits/foundation 0.161.2-beta.9 → 0.161.2-reanimated.2

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.
@@ -37,8 +37,7 @@ const BottomSheet: React.FC<BottomSheetParams> = props => {
37
37
  const action = useRef<undefined | string>(undefined);
38
38
  const insets = useSafeAreaInsets();
39
39
  const heightHeader = useHeaderHeight();
40
- const bottomInset = Platform.OS === 'ios' ? Math.min(insets.bottom, 21) : insets.bottom;
41
- const keyboardOffset = heightHeader - bottomInset;
40
+ const keyboardOffset = heightHeader - Math.min(insets.bottom, 21);
42
41
 
43
42
  const showBaseLineDebug = context?.features?.showBaseLineDebug ?? false;
44
43
 
@@ -312,7 +311,7 @@ const BottomSheet: React.FC<BottomSheetParams> = props => {
312
311
  {useBottomInset && (
313
312
  <View
314
313
  style={{
315
- height: bottomInset,
314
+ height: Math.min(insets.bottom, 21),
316
315
  backgroundColor: backgroundColor,
317
316
  }}
318
317
  />
@@ -296,7 +296,7 @@ const BottomTab: React.FC<BottomTabProps> = ({
296
296
  activeTintColor={theme.colors.primary}
297
297
  style={[
298
298
  {
299
- height: 64 + (Platform.OS === 'ios' ? Math.min(insets.bottom, 21) : insets.bottom),
299
+ height: 64 + Math.min(insets.bottom, 21),
300
300
  },
301
301
  ]}
302
302
  />
@@ -8,6 +8,7 @@ import { type HeaderAnimatedProps } from '../types';
8
8
  import React from 'react';
9
9
  import { Image } from '../../Image';
10
10
  import { Colors } from '../../Consts';
11
+ import { useNormalizedSharedValue } from './useAnimatedCompat';
11
12
 
12
13
  const HeaderAnimated: React.FC<HeaderAnimatedProps> = ({
13
14
  animatedValue,
@@ -15,23 +16,19 @@ const HeaderAnimated: React.FC<HeaderAnimatedProps> = ({
15
16
  useScale = true,
16
17
  children,
17
18
  }) => {
19
+ const sharedValue = useNormalizedSharedValue(animatedValue);
18
20
  const animatedStyle = useAnimatedStyle(() => {
19
21
  const scale = useScale
20
- ? interpolate(
21
- animatedValue.value,
22
- [-300, 0, 300],
23
- [4, 1, 1],
24
- Extrapolation.CLAMP,
25
- )
22
+ ? interpolate(sharedValue.value, [-300, 0, 300], [4, 1, 1], Extrapolation.CLAMP)
26
23
  : 1;
27
24
  const opacity = interpolate(
28
- animatedValue.value,
25
+ sharedValue.value,
29
26
  [0, 150, 300],
30
27
  [1, 0.5, 0],
31
28
  Extrapolation.CLAMP,
32
29
  );
33
30
  const translateY = interpolate(
34
- animatedValue.value,
31
+ sharedValue.value,
35
32
  [-300, 0],
36
33
  [-80, -2],
37
34
  Extrapolation.CLAMP,
@@ -1,12 +1,13 @@
1
1
  import LinearGradient from 'react-native-linear-gradient';
2
2
  import { StyleSheet, View } from 'react-native';
3
3
  import React, { useContext } from 'react';
4
- import Animated, { useSharedValue } from 'react-native-reanimated';
4
+ import Animated from 'react-native-reanimated';
5
5
  import { HeaderBackgroundProps } from '../types';
6
6
  import { ApplicationContext, MiniAppContext } from '../../Context';
7
7
  import { Colors, Styles } from '../../Consts';
8
8
  import { Image } from '../../Image';
9
9
  import BackgroundImageView from './BackgroundImageView';
10
+ import { useNormalizedSharedValue } from './useAnimatedCompat';
10
11
 
11
12
  const HeaderBackground: React.FC<HeaderBackgroundProps> = ({
12
13
  animatedValue,
@@ -19,8 +20,7 @@ const HeaderBackground: React.FC<HeaderBackgroundProps> = ({
19
20
  const context = useContext<any>(MiniAppContext);
20
21
  const gradientColor = customGradientColor ?? theme.colors.gradient;
21
22
  const headerImage = headerBackground ?? theme.assets?.headerBackground;
22
- const fallback = useSharedValue(0);
23
- const sv = animatedValue ?? fallback;
23
+ const sharedValue = useNormalizedSharedValue(animatedValue);
24
24
 
25
25
  const showBaseLineDebug = context?.features?.showBaseLineDebug ?? false;
26
26
 
@@ -29,7 +29,7 @@ const HeaderBackground: React.FC<HeaderBackgroundProps> = ({
29
29
  <BackgroundImageView
30
30
  useShadowHeader={useShadowHeader}
31
31
  heightHeader={'100%'}
32
- animatedValue={sv}
32
+ animatedValue={sharedValue}
33
33
  headerBackground={headerImage}
34
34
  />
35
35
  <View style={styles.gradientContainer}>
@@ -5,8 +5,6 @@ import Animated, {
5
5
  Extrapolation,
6
6
  interpolate,
7
7
  useAnimatedStyle,
8
- useSharedValue,
9
- type SharedValue,
10
8
  } from 'react-native-reanimated';
11
9
  import { ApplicationContext, MiniAppContext } from '../../Context';
12
10
  import { HeaderType } from '../../Layout/types';
@@ -16,6 +14,10 @@ import { Colors, Radius, Spacing } from '../../Consts';
16
14
  import { Image } from '../../Image';
17
15
  import { SearchHeaderProps } from '../types';
18
16
  import BackgroundImageView from './BackgroundImageView';
17
+ import {
18
+ AnimatedCompatValue,
19
+ useNormalizedSharedValue,
20
+ } from './useAnimatedCompat';
19
21
 
20
22
  const SCREEN_PADDING = 12;
21
23
  const BACK_WIDTH = 28;
@@ -24,7 +26,7 @@ const { width: screenWidth } = Dimensions.get('window');
24
26
 
25
27
  const HeaderExtendHeader: React.FC<{
26
28
  headerType?: HeaderType;
27
- animatedValue: SharedValue<number>;
29
+ animatedValue: AnimatedCompatValue;
28
30
  heightHeader: number;
29
31
  headerRightWidth: number;
30
32
  inputSearchProps?: SearchHeaderProps;
@@ -46,8 +48,7 @@ const HeaderExtendHeader: React.FC<{
46
48
  }) => {
47
49
  const { theme } = useContext(ApplicationContext);
48
50
  const context = useContext<any>(MiniAppContext);
49
- const fallback = useSharedValue(0);
50
- const sv = animatedValue ?? fallback;
51
+ const sharedValue = useNormalizedSharedValue(animatedValue);
51
52
  const gradientColor = customGradientColor ?? theme.colors.gradient;
52
53
  const headerBackground = customBackground ?? theme.assets?.headerBackground;
53
54
  const leftPosition = inputSearchProps?.leftPosition || BACK_WIDTH + 20;
@@ -61,7 +62,7 @@ const HeaderExtendHeader: React.FC<{
61
62
 
62
63
  const heightStyle = useAnimatedStyle(() => ({
63
64
  height: interpolate(
64
- sv.value,
65
+ sharedValue.value,
65
66
  [0, 100],
66
67
  [heightHeader + 52, heightHeader],
67
68
  Extrapolation.CLAMP,
@@ -69,14 +70,14 @@ const HeaderExtendHeader: React.FC<{
69
70
  }));
70
71
 
71
72
  const gradientOpacityStyle = useAnimatedStyle(() => ({
72
- opacity: interpolate(sv.value, [0, 52], [1, 0], Extrapolation.CLAMP),
73
+ opacity: interpolate(sharedValue.value, [0, 52], [1, 0], Extrapolation.CLAMP),
73
74
  }));
74
75
 
75
76
  const searchTranslateStyle = useAnimatedStyle(() => ({
76
77
  transform: [
77
78
  {
78
79
  translateX: interpolate(
79
- sv.value,
80
+ sharedValue.value,
80
81
  [0, 100],
81
82
  [SCREEN_PADDING, leftPosition],
82
83
  Extrapolation.CLAMP,
@@ -84,7 +85,7 @@ const HeaderExtendHeader: React.FC<{
84
85
  },
85
86
  ],
86
87
  width: interpolate(
87
- sv.value,
88
+ sharedValue.value,
88
89
  [0, 100],
89
90
  [
90
91
  screenWidth - SCREEN_PADDING * 2,
@@ -95,7 +96,7 @@ const HeaderExtendHeader: React.FC<{
95
96
  }));
96
97
 
97
98
  const searchBackgroundStyle = useAnimatedStyle(() => {
98
- const t = Math.min(Math.max(sv.value / 100, 0), 1);
99
+ const t = Math.min(Math.max(sharedValue.value / 100, 0), 1);
99
100
  return { backgroundColor: t === 0
100
101
  ? theme.colors.background.surface
101
102
  : theme.colors.background.default };
@@ -103,7 +104,7 @@ const HeaderExtendHeader: React.FC<{
103
104
 
104
105
  const extendedHeightStyle = useAnimatedStyle(() => ({
105
106
  height: interpolate(
106
- sv.value,
107
+ sharedValue.value,
107
108
  [0, 100],
108
109
  [heightHeader + 52, heightHeader],
109
110
  Extrapolation.CLAMP,
@@ -117,7 +118,7 @@ const HeaderExtendHeader: React.FC<{
117
118
  <BackgroundImageView
118
119
  useShadowHeader={useShadowHeader}
119
120
  heightHeader={heightHeader}
120
- animatedValue={sv}
121
+ animatedValue={sharedValue}
121
122
  headerBackground={headerBackground}
122
123
  />
123
124
  <Animated.View
@@ -185,7 +186,7 @@ const HeaderExtendHeader: React.FC<{
185
186
  <BackgroundImageView
186
187
  useShadowHeader={useShadowHeader}
187
188
  heightHeader={heightHeader}
188
- animatedValue={sv}
189
+ animatedValue={sharedValue}
189
190
  headerBackground={headerBackground}
190
191
  />
191
192
  {!!gradientColor && (
@@ -9,7 +9,6 @@ import Animated, {
9
9
  Extrapolation,
10
10
  interpolate,
11
11
  useAnimatedStyle,
12
- type SharedValue,
13
12
  } from 'react-native-reanimated';
14
13
  import { ApplicationContext, MiniAppContext } from '../../Context';
15
14
  import { exportFontFamily, Text, useScaleSize } from '../../Text';
@@ -22,6 +21,10 @@ import {
22
21
  import { Image } from '../../Image';
23
22
  import { Icon } from '../../Icon';
24
23
  import { Skeleton } from '../../Skeleton';
24
+ import {
25
+ AnimatedCompatValue,
26
+ useNormalizedSharedValue,
27
+ } from './useAnimatedCompat';
25
28
 
26
29
  type HeaderTitleInterpolate = {
27
30
  inputRange: number[];
@@ -29,7 +32,7 @@ type HeaderTitleInterpolate = {
29
32
  };
30
33
 
31
34
  type HeaderTitleExtraProps = {
32
- animatedValue?: SharedValue<number>;
35
+ animatedValue?: AnimatedCompatValue;
33
36
  interpolate?: HeaderTitleInterpolate;
34
37
  tintColor?: string;
35
38
  children?: React.ReactNode;
@@ -47,15 +50,16 @@ const HeaderTitle: React.FC<HeaderTitleExtraProps & { [key: string]: any }> = pr
47
50
  inputRange: [0, 200],
48
51
  outputRange: [0, 1],
49
52
  };
50
- const animatedValue = props.animatedValue;
53
+ const hasAnimated = props.animatedValue != null;
54
+ const sharedValue = useNormalizedSharedValue(props.animatedValue);
51
55
 
52
56
  const animatedStyle = useAnimatedStyle(() => {
53
- if (!animatedValue) {
57
+ if (!hasAnimated) {
54
58
  return { opacity: 1 };
55
59
  }
56
60
  return {
57
61
  opacity: interpolate(
58
- animatedValue.value,
62
+ sharedValue.value,
59
63
  interpolateConfig.inputRange,
60
64
  interpolateConfig.outputRange,
61
65
  Extrapolation.CLAMP,
@@ -6,14 +6,12 @@ import {
6
6
  TouchableOpacity,
7
7
  View,
8
8
  } from 'react-native';
9
- import Animated, {
10
- useAnimatedStyle,
11
- useSharedValue,
12
- } from 'react-native-reanimated';
9
+ import Animated, { useAnimatedStyle } from 'react-native-reanimated';
13
10
  import React, { useContext } from 'react';
14
11
  import { SearchHeaderProps } from '../types';
15
12
  import { ApplicationContext, MiniAppContext } from '../../Context';
16
13
  import { Text } from '../../Text';
14
+ import { useNormalizedSharedValue } from './useAnimatedCompat';
17
15
 
18
16
  const SearchHeader = React.forwardRef<InputRef, SearchHeaderProps>(
19
17
  (
@@ -31,13 +29,12 @@ const SearchHeader = React.forwardRef<InputRef, SearchHeaderProps>(
31
29
  const BACK_WIDTH = 28;
32
30
  const { width: screenWidth } = Dimensions.get('window');
33
31
 
34
- const fallback = useSharedValue(0);
35
- const sv = animatedValue ?? fallback;
32
+ const sharedValue = useNormalizedSharedValue(animatedValue);
36
33
  const leftPosition = props?.leftPosition ?? BACK_WIDTH + 20;
37
34
  const headerRightWidth = props?.headerRightWidth ?? 73;
38
35
 
39
36
  const backgroundStyle = useAnimatedStyle(() => {
40
- const t = Math.min(Math.max(sv.value / 100, 0), 1);
37
+ const t = Math.min(Math.max(sharedValue.value / 100, 0), 1);
41
38
  return {
42
39
  backgroundColor: t === 0
43
40
  ? theme.colors.background.surface
@@ -0,0 +1,63 @@
1
+ import { useEffect } from 'react';
2
+ import { Animated } from 'react-native';
3
+ import { useSharedValue, type SharedValue } from 'react-native-reanimated';
4
+
5
+ /**
6
+ * Union of the two animation runtimes a consumer might hand to a foundation
7
+ * component. react-native `Animated.Value` (legacy) and reanimated
8
+ * `SharedValue` are different objects and cannot be used interchangeably, so
9
+ * foundation components normalize whatever they receive into a `SharedValue`
10
+ * via {@link useNormalizedSharedValue} before rendering with reanimated.
11
+ */
12
+ export type AnimatedCompatValue = Animated.Value | SharedValue<number>;
13
+
14
+ /**
15
+ * A react-native `Animated.Value` exposes `setValue`/`interpolate` methods; a
16
+ * reanimated `SharedValue` does not.
17
+ */
18
+ export const isRNAnimatedValue = (v: unknown): v is Animated.Value =>
19
+ !!v &&
20
+ typeof (v as Animated.Value).setValue === 'function' &&
21
+ typeof (v as Animated.Value).interpolate === 'function';
22
+
23
+ /**
24
+ * Heuristic: a reanimated `SharedValue` carries a `value` accessor but none of
25
+ * the `Animated.Value` methods.
26
+ */
27
+ export const isSharedValue = (v: unknown): v is SharedValue<number> =>
28
+ !!v && !isRNAnimatedValue(v) && 'value' in (v as object);
29
+
30
+ /**
31
+ * Normalize an incoming animated value (either runtime, or nothing) into a
32
+ * reanimated `SharedValue<number>` that foundation components can drive with
33
+ * `useAnimatedStyle`/`interpolate`.
34
+ *
35
+ * - SharedValue → returned as-is (zero overhead, shares the same UI-thread cell)
36
+ * - Animated.Value → bridged: a JS-thread `addListener` mirrors its value into
37
+ * an internal SharedValue. NOTE: when the source `Animated.Value` is driven
38
+ * by `Animated.event(..., { useNativeDriver: true })`, `addListener` does NOT
39
+ * fire per frame, so the bridge stays flat. For the Screen-driven case the
40
+ * bridge is fed from the scroll `listener` instead (see Screen.tsx); this
41
+ * hook's listener is the safety net for standalone / JS-driven usage.
42
+ * - undefined → a fresh internal SharedValue stuck at 0
43
+ */
44
+ export function useNormalizedSharedValue(
45
+ input?: AnimatedCompatValue,
46
+ ): SharedValue<number> {
47
+ const fallback = useSharedValue(0);
48
+
49
+ useEffect(() => {
50
+ if (!isRNAnimatedValue(input)) {
51
+ return;
52
+ }
53
+ const id = input.addListener(({ value }) => {
54
+ fallback.value = value;
55
+ });
56
+ return () => input.removeListener(id);
57
+ }, [input, fallback]);
58
+
59
+ if (isSharedValue(input)) {
60
+ return input;
61
+ }
62
+ return fallback;
63
+ }
@@ -2,6 +2,7 @@ import { EventArg } from '@react-navigation/core';
2
2
  import { StackNavigationOptions } from '@react-navigation/stack';
3
3
  import React, { ReactNode } from 'react';
4
4
  import {
5
+ Animated,
5
6
  TouchableOpacityProps,
6
7
  ViewProps,
7
8
  ViewStyle,
@@ -258,7 +259,7 @@ export interface HeaderBackProps extends NavigationButtonProps {
258
259
  }
259
260
 
260
261
  export type HeaderBackgroundProps = {
261
- animatedValue?: SharedValue<number>;
262
+ animatedValue?: Animated.Value | SharedValue<number>;
262
263
  useGradient?: boolean;
263
264
  useShadowHeader?: boolean;
264
265
  backgroundColor?: string;
@@ -298,7 +299,7 @@ export type TitleJourneyProps = {
298
299
  };
299
300
 
300
301
  export interface HeaderAnimatedProps extends ViewProps {
301
- animatedValue: SharedValue<number>;
302
+ animatedValue: Animated.Value | SharedValue<number>;
302
303
  image: string;
303
304
  useScale?: boolean;
304
305
  }
@@ -342,7 +343,7 @@ export type AnimatedHeader = {
342
343
 
343
344
  export interface SearchHeaderProps extends InputSearchProps {
344
345
  ref?: React.RefObject<InputRef>;
345
- animatedValue?: SharedValue<number>;
346
+ animatedValue?: Animated.Value | SharedValue<number>;
346
347
  headerRightWidth?: 0 | 74 | 110 | number;
347
348
  leftPosition?: 12 | 48 | number;
348
349
  renderButtons?: () => ReactNode;
@@ -11,11 +11,14 @@ import Animated, {
11
11
  useSharedValue,
12
12
  withTiming,
13
13
  runOnJS,
14
- type SharedValue,
15
14
  } from 'react-native-reanimated';
16
15
  import { ApplicationContext } from '../Context';
17
16
  import { Icon } from '../Icon';
18
17
  import { useScaleSize } from '../Text';
18
+ import {
19
+ AnimatedCompatValue,
20
+ useNormalizedSharedValue,
21
+ } from '../Application/Components/useAnimatedCompat';
19
22
 
20
23
  export interface FloatingButtonProps {
21
24
  label?: string;
@@ -24,7 +27,7 @@ export interface FloatingButtonProps {
24
27
  icon?: string;
25
28
  iconColor?: string;
26
29
  size?: 'small' | 'large';
27
- animatedValue?: SharedValue<number>;
30
+ animatedValue?: AnimatedCompatValue;
28
31
  bottom?: number;
29
32
  renderComponent?: () => React.ReactNode;
30
33
  }
@@ -41,6 +44,8 @@ export const FloatingButton: React.FC<FloatingButtonProps> = ({
41
44
  bottom = 12,
42
45
  }: FloatingButtonProps) => {
43
46
  const { theme } = useContext(ApplicationContext);
47
+ const enabled = animatedValue != null;
48
+ const sharedValue = useNormalizedSharedValue(animatedValue);
44
49
  const scaledFontSize = useScaleSize(16);
45
50
  const scaledLineHeight = useScaleSize(22);
46
51
  const maxWidth = useSharedValue(0);
@@ -57,10 +62,10 @@ export const FloatingButton: React.FC<FloatingButtonProps> = ({
57
62
  }, [label, opacityAnimated]);
58
63
 
59
64
  useAnimatedReaction(
60
- () => animatedValue?.value ?? 0,
65
+ () => sharedValue.value,
61
66
  (value) => {
62
67
  'worklet';
63
- if (!label || !animatedValue) return;
68
+ if (!label || !enabled) return;
64
69
  if (value !== lastOffset.value && value > 0) {
65
70
  const direction = value > lastOffset.value ? 'down' : 'up';
66
71
  lastOffset.value = value;
@@ -88,7 +93,7 @@ export const FloatingButton: React.FC<FloatingButtonProps> = ({
88
93
  }
89
94
  }
90
95
  },
91
- [label, animatedValue, minWidth],
96
+ [label, enabled, minWidth],
92
97
  );
93
98
 
94
99
  const containerStyle = useAnimatedStyle(() => ({
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,9 +25,7 @@ import {
24
25
  View,
25
26
  ViewProps,
26
27
  } from 'react-native';
27
- import Animated, {
28
- runOnJS,
29
- useAnimatedScrollHandler,
28
+ import {
30
29
  useSharedValue,
31
30
  withTiming,
32
31
  type SharedValue,
@@ -48,6 +47,10 @@ import { InputRef } from '../Input';
48
47
  import { exportHeaderTitle, getOptions } from '../Application/utils';
49
48
  import { validateChildren } from './utils';
50
49
  import Section from './Section';
50
+ import {
51
+ isRNAnimatedValue,
52
+ isSharedValue,
53
+ } from '../Application/Components/useAnimatedCompat';
51
54
  import {
52
55
  HeaderBackground,
53
56
  HeaderExtendHeader,
@@ -142,9 +145,10 @@ export interface ScreenProps extends ViewProps {
142
145
  inputSearchRef?: Ref<InputRef>;
143
146
 
144
147
  /**
145
- * Optional. Animated value for header.
148
+ * Optional. Animated value for header. Accepts either a react-native
149
+ * `Animated.Value` (legacy) or a reanimated `SharedValue<number>`.
146
150
  */
147
- animatedValue?: SharedValue<number>;
151
+ animatedValue?: Animated.Value | SharedValue<number>;
148
152
 
149
153
  /**
150
154
  * Optional. If `true`, use shadow header.
@@ -201,18 +205,36 @@ const Screen = forwardRef(
201
205
  const screen: any = useContext(ScreenContext);
202
206
  const insets = useSafeAreaInsets();
203
207
  const heightHeader = useHeaderHeight();
204
- const internalAnimatedValue = useSharedValue(0);
205
- const animatedValue = customAnimatedValue ?? internalAnimatedValue;
208
+
209
+ /**
210
+ * Dual animation source so both runtimes are supported without crashing:
211
+ * - `rnValue` is a react-native `Animated.Value` driven natively by the
212
+ * scroll event. It is what legacy consumers receive (`animatedValue`).
213
+ * - `sharedValue` is a reanimated `SharedValue` consumed by every internal
214
+ * foundation component (and offered to modern consumers). It is kept in
215
+ * sync from the scroll `listener` (see below), which fires on the JS
216
+ * thread even when the scroll uses the native driver.
217
+ * A consumer may also pass one of the two in; we reuse whichever matches.
218
+ */
219
+ const rnValue = useRef<Animated.Value>(
220
+ isRNAnimatedValue(customAnimatedValue)
221
+ ? customAnimatedValue
222
+ : new Animated.Value(0),
223
+ );
224
+ const internalShared = useSharedValue(0);
225
+ const sharedValue = isSharedValue(customAnimatedValue)
226
+ ? customAnimatedValue
227
+ : internalShared;
228
+
206
229
  const currentTint = useRef<string | undefined>(undefined);
207
230
  const isTab = navigation?.instance?.getState?.()?.type === 'tab';
208
231
 
209
- let handleScroll: any;
232
+ let handleScroll;
210
233
  let Component: any = View;
211
234
 
212
- const bottomInset = Platform.OS === 'ios' ? Math.min(insets.bottom, 21) : insets.bottom;
213
- let keyboardOffset = heightHeader - bottomInset;
235
+ let keyboardOffset = heightHeader - Math.min(insets.bottom, 21);
214
236
  if (headerType === 'extended' || animatedHeader || inputSearchProps) {
215
- keyboardOffset = -bottomInset;
237
+ keyboardOffset = -Math.min(insets.bottom, 21);
216
238
  }
217
239
 
218
240
  /**
@@ -255,7 +277,7 @@ const Screen = forwardRef(
255
277
  inputRange: [0, 50],
256
278
  outputRange: [1, 0],
257
279
  }}
258
- animatedValue={animatedValue}
280
+ animatedValue={sharedValue}
259
281
  />
260
282
  ),
261
283
  };
@@ -270,7 +292,7 @@ const Screen = forwardRef(
270
292
  headerBackground: (props: any) => (
271
293
  <HeaderBackground
272
294
  {...props}
273
- animatedValue={animatedValue}
295
+ animatedValue={sharedValue}
274
296
  useShadowHeader={useShadowHeader}
275
297
  headerBackground={headerBackground}
276
298
  gradientColor={gradientColor}
@@ -290,7 +312,7 @@ const Screen = forwardRef(
290
312
  inputRange: [0, 50],
291
313
  outputRange: [1, 0],
292
314
  }}
293
- animatedValue={animatedValue}
315
+ animatedValue={sharedValue}
294
316
  />
295
317
  ),
296
318
  };
@@ -304,6 +326,7 @@ const Screen = forwardRef(
304
326
  headerBackground,
305
327
  inputSearchProps,
306
328
  navigation?.instance,
329
+ sharedValue,
307
330
  useShadowHeader,
308
331
  ],
309
332
  );
@@ -325,7 +348,7 @@ const Screen = forwardRef(
325
348
  headerBackground: (props: any) => (
326
349
  <HeaderBackground
327
350
  {...props}
328
- animatedValue={animatedValue}
351
+ animatedValue={sharedValue}
329
352
  useGradient={false}
330
353
  useShadowHeader={useShadowHeader}
331
354
  headerBackground={headerBackground}
@@ -346,7 +369,13 @@ const Screen = forwardRef(
346
369
 
347
370
  navigation?.instance?.setOptions(options);
348
371
  },
349
- [gradientColor, headerBackground, navigation?.instance, useShadowHeader],
372
+ [
373
+ gradientColor,
374
+ headerBackground,
375
+ navigation?.instance,
376
+ sharedValue,
377
+ useShadowHeader,
378
+ ],
350
379
  );
351
380
 
352
381
  /**
@@ -364,13 +393,13 @@ const Screen = forwardRef(
364
393
  inputRange: [0, 50],
365
394
  outputRange: [1, 0],
366
395
  }}
367
- animatedValue={animatedValue}
396
+ animatedValue={sharedValue}
368
397
  />
369
398
  ),
370
399
  };
371
400
 
372
401
  navigation?.instance?.setOptions(options);
373
- }, [navigation?.instance]);
402
+ }, [navigation?.instance, sharedValue]);
374
403
 
375
404
  /**
376
405
  * export search header
@@ -393,7 +422,7 @@ const Screen = forwardRef(
393
422
  headerLeft: (props: any) =>
394
423
  params?.hiddenBack ? null : <HeaderLeft {...props} />,
395
424
  headerTitle: () => (
396
- <SearchHeader {...params} animatedValue={animatedValue} />
425
+ <SearchHeader {...params} animatedValue={sharedValue} />
397
426
  ),
398
427
  };
399
428
 
@@ -444,51 +473,47 @@ const Screen = forwardRef(
444
473
  });
445
474
  });
446
475
 
447
- const onTintColorChange = useCallback(
448
- (offsetY: number) => {
449
- if (!animatedHeader) return;
450
- let color = animatedHeader?.headerTintColor ?? Colors.black_17;
451
- if (offsetY > 50) {
452
- color = Colors.black_17;
453
- }
454
- if (color !== currentTint.current) {
455
- currentTint.current = color;
456
- navigation?.setOptions({
457
- headerTintColor: color,
458
- });
459
- let barStyle: StatusBarStyle = 'dark-content';
460
- if (currentTint.current === Colors.black_01) {
461
- barStyle = 'light-content';
462
- }
463
- StatusBar.setBarStyle(barStyle, true);
464
- }
465
- },
466
- [animatedHeader, navigation],
467
- );
468
-
469
- const emitOnScroll = useCallback(
470
- (offsetY: number) => {
471
- scrollViewProps?.onScroll?.({
472
- nativeEvent: { contentOffset: { x: 0, y: offsetY } },
473
- } as NativeSyntheticEvent<NativeScrollEvent>);
474
- },
475
- [scrollViewProps],
476
- );
477
-
478
- const scrollHandler = useAnimatedScrollHandler({
479
- onScroll: (event) => {
480
- animatedValue.value = event.contentOffset.y;
481
- runOnJS(emitOnScroll)(event.contentOffset.y);
482
- runOnJS(onTintColorChange)(event.contentOffset.y);
483
- },
484
- });
485
-
486
476
  /**
487
477
  * animated when use scroll && animated value
488
478
  */
489
479
  if (scrollable) {
490
480
  Component = Animated.ScrollView;
491
- handleScroll = scrollHandler;
481
+ handleScroll = Animated.event(
482
+ [
483
+ {
484
+ nativeEvent: {
485
+ contentOffset: { y: rnValue.current as Animated.Value },
486
+ },
487
+ },
488
+ ],
489
+ {
490
+ useNativeDriver: true,
491
+ listener: (e: NativeSyntheticEvent<NativeScrollEvent>) => {
492
+ const offsetY = e.nativeEvent.contentOffset.y;
493
+ // keep the reanimated source in sync for the internal components.
494
+ sharedValue.value = offsetY;
495
+ scrollViewProps?.onScroll?.(e);
496
+ if (animatedHeader) {
497
+ let color = animatedHeader?.headerTintColor ?? Colors.black_17;
498
+ if (offsetY > 50) {
499
+ color = Colors.black_17;
500
+ }
501
+ if (color !== currentTint.current) {
502
+ currentTint.current = color;
503
+ navigation?.setOptions({
504
+ headerTintColor: color,
505
+ });
506
+
507
+ let barStyle: StatusBarStyle = 'dark-content';
508
+ if (currentTint.current === Colors.black_01) {
509
+ barStyle = 'light-content';
510
+ }
511
+ StatusBar.setBarStyle(barStyle, true);
512
+ }
513
+ }
514
+ },
515
+ },
516
+ );
492
517
  }
493
518
 
494
519
  /**
@@ -498,7 +523,12 @@ const Screen = forwardRef(
498
523
  const handleScrollEnd = (e: NativeSyntheticEvent<NativeScrollEvent>) => {
499
524
  const offsetY = e.nativeEvent.contentOffset.y;
500
525
  if (inputSearchProps && offsetY < 100 && offsetY > 0) {
501
- animatedValue.value = withTiming(0, { duration: 300 });
526
+ Animated.timing(rnValue.current, {
527
+ toValue: 0,
528
+ useNativeDriver: true,
529
+ duration: 300,
530
+ }).start();
531
+ sharedValue.value = withTiming(0, { duration: 300 });
502
532
  ref?.scrollTo?.({ y: 0, animated: true });
503
533
  }
504
534
  scrollViewProps?.onScrollEndDrag?.(e);
@@ -514,7 +544,10 @@ const Screen = forwardRef(
514
544
  style={[styles.screenBanner, { maxHeight: 210 + layoutOffset }]}
515
545
  >
516
546
  {animatedHeader?.component({
517
- animatedValue: animatedValue,
547
+ // legacy consumers read `animatedValue` (Animated.Value),
548
+ // modern consumers read `sharedValue` (SharedValue).
549
+ animatedValue: rnValue.current,
550
+ sharedValue: sharedValue,
518
551
  })}
519
552
  </View>
520
553
  );
@@ -581,7 +614,7 @@ const Screen = forwardRef(
581
614
  headerType={headerType}
582
615
  heightHeader={heightHeader}
583
616
  headerRightWidth={headerRightWidth}
584
- animatedValue={animatedValue}
617
+ animatedValue={sharedValue}
585
618
  inputSearchProps={inputSearchProps}
586
619
  navigation={navigation}
587
620
  inputSearchRef={inputSearchRef}
@@ -619,9 +652,9 @@ const Screen = forwardRef(
619
652
  <View>
620
653
  <FloatingButton
621
654
  {...floatingButtonProps}
622
- animatedValue={animatedValue}
655
+ animatedValue={sharedValue}
623
656
  bottom={
624
- Footer || isTab ? 12 : bottomInset + Spacing.S
657
+ Footer || isTab ? 12 : Math.min(insets.bottom, 21) + Spacing.S
625
658
  }
626
659
  />
627
660
  </View>
@@ -632,7 +665,7 @@ const Screen = forwardRef(
632
665
  style={[
633
666
  styles.shadow,
634
667
  {
635
- paddingBottom: bottomInset + Spacing.S,
668
+ paddingBottom: Math.min(insets.bottom, 21) + Spacing.S,
636
669
  backgroundColor: theme.colors.background.surface,
637
670
  },
638
671
  ]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@momo-kits/foundation",
3
- "version": "0.161.2-beta.9",
3
+ "version": "0.161.2-reanimated.2",
4
4
  "description": "React Native Component Kits",
5
5
  "main": "index.ts",
6
6
  "scripts": {},