@momo-kits/foundation 0.162.2-sp.2 → 0.162.2-test.10

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,12 +1,7 @@
1
- import React, { Ref, useContext } from 'react';
1
+ import React, { Ref, useContext, useEffect, useRef } from 'react';
2
2
  import LinearGradient from 'react-native-linear-gradient';
3
- import { Dimensions, Platform, StyleSheet, View } from 'react-native';
4
- import Animated, {
5
- Extrapolation,
6
- interpolate,
7
- useAnimatedStyle,
8
- } from 'react-native-reanimated';
9
3
  import { ApplicationContext, MiniAppContext } from '../../Context';
4
+ import { Animated, Dimensions, Platform, StyleSheet, View } from 'react-native';
10
5
  import { HeaderType } from '../../Layout/types';
11
6
  import { InputRef, InputSearch } from '../../Input';
12
7
  import Navigation from '../Navigation';
@@ -14,19 +9,20 @@ import { Colors, Radius, Spacing } from '../../Consts';
14
9
  import { Image } from '../../Image';
15
10
  import { SearchHeaderProps } from '../types';
16
11
  import BackgroundImageView from './BackgroundImageView';
17
- import {
18
- AnimatedCompatValue,
19
- useNormalizedSharedValue,
20
- } from '../utils';
21
12
 
22
13
  const SCREEN_PADDING = 12;
23
14
  const BACK_WIDTH = 28;
24
15
 
25
16
  const { width: screenWidth } = Dimensions.get('window');
17
+ const LinearGradientAnimated = Animated.createAnimatedComponent(LinearGradient);
26
18
 
19
+ /**
20
+ * Header extended with background image
21
+ * @constructor
22
+ */
27
23
  const HeaderExtendHeader: React.FC<{
28
24
  headerType?: HeaderType;
29
- animatedValue: AnimatedCompatValue;
25
+ animatedValue: Animated.Value;
30
26
  heightHeader: number;
31
27
  headerRightWidth: number;
32
28
  inputSearchProps?: SearchHeaderProps;
@@ -42,131 +38,118 @@ const HeaderExtendHeader: React.FC<{
42
38
  headerRightWidth = 73,
43
39
  inputSearchProps,
44
40
  inputSearchRef,
45
- useShadowHeader: useShadowHeaderProp = true,
41
+ useShadowHeader = true,
46
42
  gradientColor: customGradientColor,
47
43
  headerBackground: customBackground,
48
44
  }) => {
49
45
  const { theme } = useContext(ApplicationContext);
50
46
  const context = useContext<any>(MiniAppContext);
51
- const sharedValue = useNormalizedSharedValue(animatedValue);
47
+ const animated = useRef(new Animated.Value(0));
52
48
  const gradientColor = customGradientColor ?? theme.colors.gradient;
53
49
  const headerBackground = customBackground ?? theme.assets?.headerBackground;
54
50
  const leftPosition = inputSearchProps?.leftPosition || BACK_WIDTH + 20;
55
51
 
56
- let useShadowHeader = useShadowHeaderProp;
57
- if (inputSearchProps && Platform.OS === 'android') {
58
- useShadowHeader = false;
59
- }
60
-
61
52
  const showBaseLineDebug = context?.features?.showBaseLineDebug ?? false;
62
53
 
63
- const heightStyle = useAnimatedStyle(() => ({
64
- height: interpolate(
65
- sharedValue.value,
66
- [0, 100],
67
- [heightHeader + 52, heightHeader],
68
- Extrapolation.CLAMP,
69
- ),
70
- }));
54
+ const opacityBackground = animatedValue?.interpolate({
55
+ inputRange: [0, 52],
56
+ outputRange: [0, 1],
57
+ extrapolate: 'clamp',
58
+ });
59
+ const opacityGradient = animatedValue?.interpolate({
60
+ inputRange: [0, 52],
61
+ outputRange: [1, 0],
62
+ extrapolate: 'clamp',
63
+ });
71
64
 
72
- const gradientOpacityStyle = useAnimatedStyle(() => ({
73
- opacity: interpolate(sharedValue.value, [0, 52], [1, 0], Extrapolation.CLAMP),
74
- }));
65
+ useEffect(() => {
66
+ const listener = animatedValue.addListener(({ value }) => {
67
+ animated.current.setValue(value);
68
+ });
69
+ return () => {
70
+ animatedValue?.removeListener(listener);
71
+ };
72
+ }, [animatedValue]);
75
73
 
76
- const searchTranslateStyle = useAnimatedStyle(() => ({
77
- transform: [
78
- {
79
- translateX: interpolate(
80
- sharedValue.value,
81
- [0, 100],
82
- [SCREEN_PADDING, leftPosition],
83
- Extrapolation.CLAMP,
84
- ),
85
- },
86
- ],
87
- width: interpolate(
88
- sharedValue.value,
89
- [0, 100],
90
- [
91
- screenWidth - SCREEN_PADDING * 2,
92
- screenWidth - leftPosition - 12 - headerRightWidth,
93
- ],
94
- Extrapolation.CLAMP,
95
- ),
96
- }));
74
+ const height = animated.current.interpolate({
75
+ inputRange: [0, 100],
76
+ outputRange: [heightHeader + 52, heightHeader],
77
+ extrapolate: 'clamp',
78
+ });
97
79
 
98
- const searchBackgroundStyle = useAnimatedStyle(() => {
99
- const t = Math.min(Math.max(sharedValue.value / 100, 0), 1);
100
- return { backgroundColor: t === 0
101
- ? theme.colors.background.surface
102
- : theme.colors.background.default };
80
+ const translateX = animated.current.interpolate({
81
+ inputRange: [0, 100],
82
+ outputRange: [SCREEN_PADDING, leftPosition],
83
+ extrapolate: 'clamp',
103
84
  });
104
85
 
105
- const extendedHeightStyle = useAnimatedStyle(() => ({
106
- height: interpolate(
107
- sharedValue.value,
108
- [0, 100],
109
- [heightHeader + 52, heightHeader],
110
- Extrapolation.CLAMP,
111
- ),
112
- }));
86
+ const backgroundColor = animated.current.interpolate({
87
+ inputRange: [0, 100],
88
+ outputRange: [
89
+ theme.colors.background.surface,
90
+ theme.colors.background.default,
91
+ ],
92
+ extrapolate: 'clamp',
93
+ });
94
+
95
+ if (inputSearchProps && Platform.OS === 'android') {
96
+ useShadowHeader = false;
97
+ }
113
98
 
114
99
  if (inputSearchProps) {
115
100
  return (
116
101
  <View style={[{ zIndex: 0 }, showBaseLineDebug && styles.debugBaseLine]}>
117
- <Animated.View style={heightStyle} />
102
+ <Animated.View style={{ height: height }} />
118
103
  <BackgroundImageView
119
104
  useShadowHeader={useShadowHeader}
120
105
  heightHeader={heightHeader}
121
- animatedValue={sharedValue}
106
+ opacityBackground={opacityBackground}
122
107
  headerBackground={headerBackground}
123
108
  />
124
109
  <Animated.View
125
110
  style={[
126
111
  styles.headerBox,
127
- headerType === 'extended'
128
- ? heightStyle
129
- : { height: heightHeader },
112
+ { height: headerType === 'extended' ? height : heightHeader },
130
113
  ]}
131
114
  >
132
115
  {!!gradientColor && (
133
- <Animated.View
134
- style={[styles.extendedHeader, gradientOpacityStyle]}
116
+ <LinearGradientAnimated
117
+ colors={[gradientColor, gradientColor + '00']}
118
+ style={[styles.extendedHeader, { opacity: opacityGradient }]}
135
119
  >
136
- <LinearGradient
137
- colors={[gradientColor, gradientColor + '00']}
138
- style={StyleSheet.absoluteFill}
139
- >
140
- {!!theme.assets?.headerBackground && (
141
- <Image
142
- style={styles.headerBackground}
143
- source={{ uri: theme.assets?.headerBackground }}
144
- loading={false}
145
- />
146
- )}
147
- </LinearGradient>
148
- </Animated.View>
120
+ {!!theme.assets?.headerBackground && (
121
+ <Image
122
+ style={styles.headerBackground}
123
+ source={{ uri: theme.assets?.headerBackground }}
124
+ loading={false}
125
+ />
126
+ )}
127
+ </LinearGradientAnimated>
149
128
  )}
150
129
  </Animated.View>
151
130
  <Animated.View
152
- style={[
153
- {
154
- justifyContent: 'flex-end',
155
- position: 'absolute',
156
- zIndex: 2,
157
- },
158
- heightStyle,
159
- ]}
131
+ style={{
132
+ justifyContent: 'flex-end',
133
+ height,
134
+ position: 'absolute',
135
+ zIndex: 2,
136
+ }}
160
137
  >
161
138
  <Animated.View
162
- style={[{ marginVertical: Spacing.S }, searchTranslateStyle]}
139
+ style={{
140
+ transform: [{ translateX }],
141
+ marginVertical: Spacing.S,
142
+ width: animated.current.interpolate({
143
+ inputRange: [0, 100],
144
+ outputRange: [
145
+ screenWidth - SCREEN_PADDING * 2,
146
+ screenWidth - leftPosition - 12 - headerRightWidth,
147
+ ],
148
+ extrapolate: 'clamp',
149
+ }),
150
+ }}
163
151
  >
164
- <Animated.View
165
- style={[
166
- { borderRadius: Radius.XL },
167
- searchBackgroundStyle,
168
- ]}
169
- >
152
+ <Animated.View style={{ backgroundColor, borderRadius: Radius.XL }}>
170
153
  <InputSearch
171
154
  {...inputSearchProps}
172
155
  ref={inputSearchRef}
@@ -186,26 +169,22 @@ const HeaderExtendHeader: React.FC<{
186
169
  <BackgroundImageView
187
170
  useShadowHeader={useShadowHeader}
188
171
  heightHeader={heightHeader}
189
- animatedValue={sharedValue}
172
+ opacityBackground={opacityBackground}
190
173
  headerBackground={headerBackground}
191
174
  />
192
175
  {!!gradientColor && (
193
- <Animated.View
194
- style={[styles.extendedHeader, gradientOpacityStyle, extendedHeightStyle]}
176
+ <LinearGradientAnimated
177
+ colors={[gradientColor, gradientColor + '00']}
178
+ style={[styles.extendedHeader, { opacity: opacityGradient }]}
195
179
  >
196
- <LinearGradient
197
- colors={[gradientColor, gradientColor + '00']}
198
- style={StyleSheet.absoluteFill}
199
- >
200
- {!!headerBackground && (
201
- <Image
202
- style={styles.headerBackground}
203
- source={{ uri: headerBackground }}
204
- loading={false}
205
- />
206
- )}
207
- </LinearGradient>
208
- </Animated.View>
180
+ {!!headerBackground && (
181
+ <Image
182
+ style={styles.headerBackground}
183
+ source={{ uri: headerBackground }}
184
+ loading={false}
185
+ />
186
+ )}
187
+ </LinearGradientAnimated>
209
188
  )}
210
189
  <View style={{ height: heightHeader }} />
211
190
  </View>
@@ -1,15 +1,11 @@
1
1
  import React, { useContext } from 'react';
2
2
  import {
3
+ Animated,
3
4
  Dimensions,
4
5
  StyleSheet,
5
6
  TouchableOpacity,
6
7
  View,
7
8
  } from 'react-native';
8
- import Animated, {
9
- Extrapolation,
10
- interpolate,
11
- useAnimatedStyle,
12
- } from 'react-native-reanimated';
13
9
  import { ApplicationContext, MiniAppContext } from '../../Context';
14
10
  import { exportFontFamily, Text, useScaleSize } from '../../Text';
15
11
  import { Colors, Radius, Spacing, Styles } from '../../Consts';
@@ -21,51 +17,22 @@ import {
21
17
  import { Image } from '../../Image';
22
18
  import { Icon } from '../../Icon';
23
19
  import { Skeleton } from '../../Skeleton';
24
- import {
25
- AnimatedCompatValue,
26
- useNormalizedSharedValue,
27
- } from '../utils';
28
-
29
- type HeaderTitleInterpolate = {
30
- inputRange: number[];
31
- outputRange: number[];
32
- };
33
-
34
- type HeaderTitleExtraProps = {
35
- animatedValue?: AnimatedCompatValue;
36
- interpolate?: HeaderTitleInterpolate;
37
- tintColor?: string;
38
- children?: React.ReactNode;
39
- };
40
20
 
41
21
  /**
42
22
  * default header title used for nav
43
23
  */
44
- const HeaderTitle: React.FC<HeaderTitleExtraProps & { [key: string]: any }> = props => {
24
+ const HeaderTitle: React.FC<any> = props => {
45
25
  const context = useContext<any>(MiniAppContext);
46
26
 
47
27
  const showBaseLineDebug = context?.features?.showBaseLineDebug ?? false;
48
28
 
49
- const interpolateConfig = props.interpolate ?? {
50
- inputRange: [0, 200],
51
- outputRange: [0, 1],
52
- };
53
- const hasAnimated = props.animatedValue != null;
54
- const sharedValue = useNormalizedSharedValue(props.animatedValue);
55
-
56
- const animatedStyle = useAnimatedStyle(() => {
57
- if (!hasAnimated) {
58
- return { opacity: 1 };
59
- }
60
- return {
61
- opacity: interpolate(
62
- sharedValue.value,
63
- interpolateConfig.inputRange,
64
- interpolateConfig.outputRange,
65
- Extrapolation.CLAMP,
66
- ),
67
- };
68
- });
29
+ const opacity = props.animatedValue?.interpolate(
30
+ props.interpolate ?? {
31
+ inputRange: [0, 200],
32
+ outputRange: [0, 1],
33
+ extrapolate: 'clamp',
34
+ },
35
+ );
69
36
 
70
37
  return (
71
38
  <View
@@ -90,9 +57,9 @@ const HeaderTitle: React.FC<HeaderTitleExtraProps & { [key: string]: any }> = pr
90
57
  },
91
58
  {
92
59
  fontFamily: exportFontFamily('bold', 'action_xs_bold'),
60
+ opacity,
93
61
  color: props.tintColor,
94
62
  },
95
- animatedStyle,
96
63
  ]}
97
64
  numberOfLines={1}
98
65
  />
@@ -1,17 +1,16 @@
1
1
  import { Colors, Radius, Spacing, Styles } from '../../Consts';
2
2
  import { InputRef, InputSearch } from '../../Input';
3
3
  import {
4
+ Animated,
4
5
  Dimensions,
5
6
  StyleSheet,
6
7
  TouchableOpacity,
7
8
  View,
8
9
  } from 'react-native';
9
- import Animated, { useAnimatedStyle } from 'react-native-reanimated';
10
- import React, { useContext } from 'react';
10
+ import React, { useContext, useEffect, useRef } from 'react';
11
11
  import { SearchHeaderProps } from '../types';
12
12
  import { ApplicationContext, MiniAppContext } from '../../Context';
13
13
  import { Text } from '../../Text';
14
- import { useNormalizedSharedValue } from '../utils';
15
14
 
16
15
  const SearchHeader = React.forwardRef<InputRef, SearchHeaderProps>(
17
16
  (
@@ -29,17 +28,28 @@ const SearchHeader = React.forwardRef<InputRef, SearchHeaderProps>(
29
28
  const BACK_WIDTH = 28;
30
29
  const { width: screenWidth } = Dimensions.get('window');
31
30
 
32
- const sharedValue = useNormalizedSharedValue(animatedValue);
31
+ const animated = useRef(new Animated.Value(0));
33
32
  const leftPosition = props?.leftPosition ?? BACK_WIDTH + 20;
34
33
  const headerRightWidth = props?.headerRightWidth ?? 73;
35
34
 
36
- const backgroundStyle = useAnimatedStyle(() => {
37
- const t = Math.min(Math.max(sharedValue.value / 100, 0), 1);
38
- return {
39
- backgroundColor: t === 0
40
- ? theme.colors.background.surface
41
- : theme.colors.background.default,
35
+ useEffect(() => {
36
+ const listener = animatedValue?.addListener(({ value }) => {
37
+ animated.current.setValue(value);
38
+ });
39
+ return () => {
40
+ if (listener) {
41
+ animatedValue?.removeListener(listener);
42
+ }
42
43
  };
44
+ }, [animatedValue]);
45
+
46
+ const backgroundColor = animated.current?.interpolate({
47
+ inputRange: [0, 100],
48
+ outputRange: [
49
+ theme.colors.background.surface,
50
+ theme.colors.background.default,
51
+ ],
52
+ extrapolate: 'clamp',
43
53
  });
44
54
 
45
55
  const goBack = () => {
@@ -81,9 +91,7 @@ const SearchHeader = React.forwardRef<InputRef, SearchHeaderProps>(
81
91
  },
82
92
  ]}
83
93
  >
84
- <Animated.View
85
- style={[{ borderRadius: Radius.XL }, backgroundStyle]}
86
- >
94
+ <Animated.View style={{ backgroundColor, borderRadius: Radius.XL }}>
87
95
  <InputSearch
88
96
  ref={ref}
89
97
  {...props}
@@ -19,7 +19,7 @@ import Navigator from './Navigator';
19
19
  import ScaleSizeProvider from './ScaleSizeProvider';
20
20
  import { getModalOptions, getStackOptions } from './utils';
21
21
  import { NavigationContainerProps } from './types';
22
- import { ApplicationContext, MiniAppContext } from '../Context';
22
+ import { ApplicationContext, FontScaleConfig, MiniAppContext } from '../Context';
23
23
  import Localize from './Localize';
24
24
  import { Colors, defaultTheme } from '../Consts';
25
25
  import { HeaderLeft } from './Components/HeaderLeft';
@@ -29,6 +29,9 @@ import { HeaderBackground } from './Components/HeaderBackground';
29
29
 
30
30
  const Stack = createStackNavigator();
31
31
 
32
+ // Observer storage key the host app writes FontScaleConfig to. Lowercased natively.
33
+ const FONT_SCALE_OBSERVER_KEY = 'font_scale_config';
34
+
32
35
  const NavigationContainer: React.FC<NavigationContainerProps> = ({
33
36
  screen,
34
37
  theme = defaultTheme,
@@ -45,6 +48,11 @@ const NavigationContainer: React.FC<NavigationContainerProps> = ({
45
48
  }) => {
46
49
  const context = useContext<any>(MiniAppContext);
47
50
  const [currentContext, setCurrentContext] = useState<any>({});
51
+ // Default = the host-seeded fontScaleConfig from MiniAppContext, so the first frame is already
52
+ // scaled (no flash). The observer below overwrites it on later host writes (live rate changes).
53
+ const [observerFontScaleConfig, setObserverFontScaleConfig] = useState<
54
+ FontScaleConfig | undefined
55
+ >(context?.fontScaleConfig);
48
56
 
49
57
  const mergedContext = {
50
58
  ...context,
@@ -55,10 +63,27 @@ const NavigationContainer: React.FC<NavigationContainerProps> = ({
55
63
  },
56
64
  };
57
65
 
66
+ useEffect(() => {
67
+ // The initial value is seeded by the host into MiniAppContext and used as this state's default
68
+ // above, so the first frame is already scaled (no flash). Here we only subscribe to later host
69
+ // writes so a rate change applies live while the miniapp is open.
70
+ const sub = maxApi?.observer?.(
71
+ FONT_SCALE_OBSERVER_KEY,
72
+ (data: FontScaleConfig) => {
73
+ setObserverFontScaleConfig(data ?? undefined);
74
+ },
75
+ );
76
+ return () => {
77
+ sub?.remove?.();
78
+ };
79
+ }, [maxApi]);
80
+
58
81
  return (
59
82
  <SafeAreaProvider>
60
83
  <MiniAppContext.Provider value={mergedContext}>
61
- <ScaleSizeProvider scaleSizeMaxRate={mergedContext?.scaleSizeMaxRate}>
84
+ <ScaleSizeProvider
85
+ fontScaleConfig={observerFontScaleConfig}
86
+ >
62
87
  <Navigation
63
88
  screen={screen}
64
89
  theme={theme}
@@ -1,13 +1,15 @@
1
- import React, { FC } from 'react';
1
+ import React, { FC, useContext } from 'react';
2
2
  import { ScaleSizeContext } from '../Context';
3
3
  import { ScaleSizeProviderProps } from './types';
4
4
 
5
5
  const ScaleSizeProvider: FC<ScaleSizeProviderProps> = ({
6
- scaleSizeMaxRate,
6
+ fontScaleConfig,
7
7
  children,
8
8
  }) => {
9
+ // Inherit the config from an upper layer (e.g. app root) when the host doesn't set it via MiniAppContext.
10
+ const parent = useContext(ScaleSizeContext);
9
11
  return (
10
- <ScaleSizeContext.Provider value={{ scaleSizeMaxRate }}>
12
+ <ScaleSizeContext.Provider value={fontScaleConfig ?? parent}>
11
13
  {children}
12
14
  </ScaleSizeContext.Provider>
13
15
  );
@@ -78,7 +78,7 @@ const WidgetContainer: React.FC<WidgetContainerProps> = ({
78
78
  <SafeAreaProvider>
79
79
  <MiniAppContext.Provider value={mergedContext}>
80
80
  <ScaleSizeProvider
81
- scaleSizeMaxRate={mergedContext?.scaleSizeMaxRate}
81
+ fontScaleConfig={mergedContext?.fontScaleConfig}
82
82
  >
83
83
  <ApplicationContext.Provider
84
84
  value={{
@@ -7,9 +7,9 @@ import {
7
7
  ViewProps,
8
8
  ViewStyle,
9
9
  } from 'react-native';
10
- import type { SharedValue } from 'react-native-reanimated';
11
10
  import { PopupNotifyProps } from '../Popup/types';
12
11
  import { InputRef, InputSearchProps } from '../Input';
12
+ import { FontScaleConfig } from '../Context';
13
13
  import Navigation from './Navigation';
14
14
 
15
15
  export type NavigationProps = {
@@ -109,7 +109,7 @@ export type Theme = {
109
109
  };
110
110
 
111
111
  export type ScaleSizeProviderProps = {
112
- scaleSizeMaxRate?: number;
112
+ fontScaleConfig?: FontScaleConfig;
113
113
  children: ViewProps['children'];
114
114
  };
115
115
 
@@ -259,7 +259,7 @@ export interface HeaderBackProps extends NavigationButtonProps {
259
259
  }
260
260
 
261
261
  export type HeaderBackgroundProps = {
262
- animatedValue?: Animated.Value | SharedValue<number>;
262
+ animatedValue?: Animated.Value;
263
263
  useGradient?: boolean;
264
264
  useShadowHeader?: boolean;
265
265
  backgroundColor?: string;
@@ -299,7 +299,7 @@ export type TitleJourneyProps = {
299
299
  };
300
300
 
301
301
  export interface HeaderAnimatedProps extends ViewProps {
302
- animatedValue: Animated.Value | SharedValue<number>;
302
+ animatedValue: Animated.Value;
303
303
  image: string;
304
304
  useScale?: boolean;
305
305
  }
@@ -343,7 +343,7 @@ export type AnimatedHeader = {
343
343
 
344
344
  export interface SearchHeaderProps extends InputSearchProps {
345
345
  ref?: React.RefObject<InputRef>;
346
- animatedValue?: Animated.Value | SharedValue<number>;
346
+ animatedValue?: Animated.Value;
347
347
  headerRightWidth?: 0 | 74 | 110 | number;
348
348
  leftPosition?: 12 | 48 | number;
349
349
  renderButtons?: () => ReactNode;
@@ -6,8 +6,6 @@ import {
6
6
  import type { HeaderTitleProps, NavigationOptions } from './types';
7
7
  import { Colors, Spacing } from '../Consts';
8
8
  import { Animated, AppState, Platform } from 'react-native';
9
- import type { SharedValue } from 'react-native-reanimated';
10
- import { useSharedValue } from 'react-native-reanimated';
11
9
  import {
12
10
  MiniAppContext,
13
11
  ScreenContext,
@@ -62,7 +60,7 @@ const getModalOptions = (): StackNavigationOptions => {
62
60
  */
63
61
  const getOptions = (
64
62
  params: NavigationOptions,
65
- animatedValue?: SharedValue<number>,
63
+ animatedValue?: Animated.Value,
66
64
  ) => {
67
65
  let options: StackNavigationOptions = {};
68
66
 
@@ -133,7 +131,7 @@ const getOptions = (
133
131
 
134
132
  const exportHeaderTitle = (
135
133
  params: NavigationOptions,
136
- animatedValue?: SharedValue<number>,
134
+ animatedValue?: Animated.Value,
137
135
  ): StackNavigationOptions => {
138
136
  if (typeof params.headerTitle === 'object') {
139
137
  return {
@@ -230,43 +228,6 @@ const useAppState = () => {
230
228
  };
231
229
  };
232
230
 
233
- /**
234
- * Union of the two animation runtimes an internal foundation component might
235
- * receive. Public Screen props still accept React Native Animated.Value only;
236
- * this type keeps lower-level header/floating components compatible while they
237
- * render with reanimated.
238
- */
239
- export type AnimatedCompatValue = Animated.Value | SharedValue<number>;
240
-
241
- const isRNAnimatedValue = (v: unknown): v is Animated.Value =>
242
- !!v &&
243
- typeof (v as Animated.Value).setValue === 'function' &&
244
- typeof (v as Animated.Value).interpolate === 'function';
245
-
246
- const isSharedValue = (v: unknown): v is SharedValue<number> =>
247
- !!v && !isRNAnimatedValue(v) && 'value' in (v as object);
248
-
249
- const useNormalizedSharedValue = (
250
- input?: AnimatedCompatValue,
251
- ): SharedValue<number> => {
252
- const fallback = useSharedValue(0);
253
-
254
- useEffect(() => {
255
- if (!isRNAnimatedValue(input)) {
256
- return;
257
- }
258
- const id = input.addListener(({ value }) => {
259
- fallback.value = value;
260
- });
261
- return () => input.removeListener(id);
262
- }, [input, fallback]);
263
-
264
- if (isSharedValue(input)) {
265
- return input;
266
- }
267
- return fallback;
268
- };
269
-
270
231
  export {
271
232
  getStackOptions,
272
233
  getModalOptions,
@@ -275,5 +236,4 @@ export {
275
236
  setAutomationID,
276
237
  useComponentId,
277
238
  useAppState,
278
- useNormalizedSharedValue,
279
239
  };