@oneplatformdev/react-native-uikit 1.2.8 → 1.2.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.
package/README.md CHANGED
@@ -359,7 +359,11 @@ For non-scrolling keyboard avoidance, omitting `keyboardAvoidingBehavior` keeps
359
359
 
360
360
  `CustomAlert` animates a centered modal with a dismissible backdrop. Supply `header`, children, and optional `footer` content; compose your own actions with `Button`. Optional `closeAction` accepts an icon and an application-supplied accessibility label. No strings are translated by the kit.
361
361
 
362
- `BottomModal` provides the reference-style slide/drag dismissal and keyboard-height-aware positioning. Supply `children`, `onClose`, and optionally `title`, `customHeader`, and a labeled `closeAction` icon. `Keyboard.dismiss()` and the close animation start together; `closeRef` can trigger the same close path externally. The kit does not render app-specific toasts.
362
+ `BottomModal` provides slide/handle-drag dismissal and keyboard-height-aware positioning. Supply `children`, `visible`, `onClose`, and optionally `title`, `customHeader`, and a labeled `closeAction` icon. Without `sizing`, the existing `isFullScreen` prop still selects the original fullscreen layout; otherwise the modal follows its content. When both are supplied, `sizing` takes precedence over `isFullScreen`. Use `sizing={{ mode: 'content' }}` for intrinsic height, `sizing={{ mode: 'fixed', fraction: 0.5 }}` for half the available height, or `sizing={{ mode: 'fullscreen' }}` to fill the area below `topInset`. A fixed fraction must be finite, greater than 0 and at most 1; invalid values throw in development. In production, positive finite values above 1 clamp to 1, while non-positive or non-finite values use a fixed 0.5 fallback. Normal `ScrollView` or `FlatList` children remain caller-owned for tall content.
363
+
364
+ `topInset` defaults to the resolved top safe-area inset; `topInset={0}` starts at the physical top. `showHeader={false}` removes the package header and ignores `customHeader`. `backdrop={false}` hides the backdrop; an object may override its `color`, `opacity`, or `dismissOnPress`, leaving other defaults intact. `cornerRadius` is an unscaled design value. `dragToDismiss="handle"` is the default; `"none"` disables dragging, and a hidden header has no drag surface. Content/list drag and multiple positions are not supported.
365
+
366
+ A close action, dismissible backdrop, handle gesture, or Android hardware back calls `onCloseRequest(reason)` once when closing starts. The keyboard is dismissed, and the existing `onClose` runs after the 250-ms exit animation; the parent still owns `visible`. An external `visible: true → false` hides immediately, cancels any pending package animation, and does not call `onCloseRequest` or `onClose`. `onDismiss` fires once after a committed controlled `true → false` transition, not after a guaranteed native OS dialog teardown. An internal close calls it only if the parent subsequently sets `visible=false`; do not use it yet to sequence native modal stacking or camera/gallery presentation. `closeRef` retains its previous close behavior without a close-request reason. The kit does not render app-specific toasts.
363
367
 
364
368
  Both modes require consumers to install the declared native peers used by these components: `@d11/react-native-fast-image`, `react-native-keyboard-controller`, `react-native-linear-gradient`, `react-native-reanimated`, and `react-native-safe-area-context`. Applications using keyboard-aware or safe-area behavior should configure the providers and native setup required by those libraries. The kit does not bundle those peers or install them through `add`.
365
369
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oneplatformdev/react-native-uikit",
3
- "version": "1.2.8",
3
+ "version": "1.2.10",
4
4
  "main": "src/index.ts",
5
5
  "types": "src/index.ts",
6
6
  "bin": {
@@ -1,2 +1,8 @@
1
1
  export { BottomModal } from './ui/BottomModal';
2
- export type { BottomModalCloseAction, BottomModalProps } from './types/types';
2
+ export type {
3
+ BottomModalBackdrop,
4
+ BottomModalCloseAction,
5
+ BottomModalCloseReason,
6
+ BottomModalProps,
7
+ BottomModalSizing,
8
+ } from './types/types';
@@ -1,9 +1,12 @@
1
- import { useCallback, useEffect, useMemo } from 'react';
1
+ import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
2
2
  import { Animated, Easing, Keyboard, PanResponder, useWindowDimensions } from 'react-native';
3
- import type { BottomModalProps } from '../types/types';
3
+ import type { BottomModalCloseReason, BottomModalProps } from '../types/types';
4
4
  import { useBottomModalInsets } from './useBottomModalInsets';
5
5
 
6
- type UseBottomModalProps = Pick<BottomModalProps, 'onClose' | 'closeRef'>;
6
+ type UseBottomModalProps = Pick<
7
+ BottomModalProps,
8
+ 'visible' | 'onClose' | 'onCloseRequest' | 'onDismiss' | 'closeRef' | 'topInset'
9
+ >;
7
10
 
8
11
  const CLOSE_DISTANCE = 120;
9
12
  const CLOSE_VELOCITY = 1;
@@ -14,16 +17,35 @@ const isDragDownGesture = (gestureState: { dx: number; dy: number }) =>
14
17
  gestureState.dy > 0 && Math.abs(gestureState.dy) > Math.abs(gestureState.dx);
15
18
 
16
19
  export const useBottomModal = ({
20
+ visible,
17
21
  onClose,
22
+ onCloseRequest,
23
+ onDismiss,
18
24
  closeRef,
25
+ topInset: requestedTopInset,
19
26
  }: UseBottomModalProps) => {
20
27
  const { height } = useWindowDimensions();
21
- const { topInset, bottomInset } = useBottomModalInsets();
22
- const translateY = useMemo(() => new Animated.Value(height), [height]);
23
- const backdropOpacity = useMemo(() => new Animated.Value(0), []);
28
+ const { topInset, bottomInset } = useBottomModalInsets(requestedTopInset);
29
+ const [translateY] = useState(() => new Animated.Value(height));
30
+ const [backdropOpacity] = useState(() => new Animated.Value(0));
31
+ const previousVisibleRef = useRef(visible);
32
+ const visibleRef = useRef(visible);
33
+ const openedRef = useRef(false);
34
+ const closingRef = useRef(false);
35
+ const animationIdRef = useRef(0);
36
+ const onCloseRef = useRef(onClose);
37
+ const onCloseRequestRef = useRef(onCloseRequest);
38
+ visibleRef.current = visible;
39
+ onCloseRef.current = onClose;
40
+ onCloseRequestRef.current = onCloseRequest;
24
41
 
25
42
  const onRunCloseAnimation = useCallback(
26
- (shouldNotify = true) => {
43
+ (shouldNotify: boolean, reason?: BottomModalCloseReason) => {
44
+ if (closingRef.current) return;
45
+ closingRef.current = true;
46
+ const animationId = ++animationIdRef.current;
47
+ if (reason) onCloseRequestRef.current?.(reason);
48
+ Keyboard.dismiss();
27
49
  translateY.stopAnimation();
28
50
  backdropOpacity.stopAnimation();
29
51
  Animated.parallel([
@@ -40,24 +62,22 @@ export const useBottomModal = ({
40
62
  useNativeDriver: true,
41
63
  }),
42
64
  ]).start(({ finished }) => {
43
- if (finished && shouldNotify) onClose();
65
+ if (!finished || animationId !== animationIdRef.current) return;
66
+ closingRef.current = false;
67
+ if (shouldNotify) onCloseRef.current();
44
68
  });
45
69
  },
46
- [backdropOpacity, height, onClose, translateY],
47
- );
48
- const onCloseAnimated = useCallback(
49
- (shouldNotify = true) => {
50
- Keyboard.dismiss();
51
- onRunCloseAnimation(shouldNotify);
52
- },
53
- [onRunCloseAnimation],
70
+ [backdropOpacity, height, translateY],
54
71
  );
55
- const onClosePress = useCallback(() => onCloseAnimated(), [onCloseAnimated]);
56
- const onShow = useCallback(() => {
72
+ const onRunOpenAnimation = useCallback((reset: boolean) => {
73
+ ++animationIdRef.current;
74
+ closingRef.current = false;
57
75
  translateY.stopAnimation();
58
76
  backdropOpacity.stopAnimation();
59
- translateY.setValue(height);
60
- backdropOpacity.setValue(0);
77
+ if (reset) {
78
+ translateY.setValue(height);
79
+ backdropOpacity.setValue(0);
80
+ }
61
81
  Animated.parallel([
62
82
  Animated.timing(translateY, {
63
83
  toValue: 0,
@@ -73,6 +93,43 @@ export const useBottomModal = ({
73
93
  }),
74
94
  ]).start();
75
95
  }, [backdropOpacity, height, translateY]);
96
+ const onShow = useCallback(() => {
97
+ if (!visibleRef.current || closingRef.current || openedRef.current) return;
98
+ openedRef.current = true;
99
+ onRunOpenAnimation(true);
100
+ }, [onRunOpenAnimation]);
101
+
102
+ useLayoutEffect(() => {
103
+ const wasVisible = previousVisibleRef.current;
104
+ previousVisibleRef.current = visible;
105
+ if (visible === wasVisible) return;
106
+ ++animationIdRef.current;
107
+ closingRef.current = false;
108
+ translateY.stopAnimation();
109
+ backdropOpacity.stopAnimation();
110
+ if (visible) {
111
+ openedRef.current = true;
112
+ onRunOpenAnimation(false);
113
+ } else {
114
+ openedRef.current = false;
115
+ translateY.setValue(height);
116
+ backdropOpacity.setValue(0);
117
+ // Report the controlled hidden commit, not native dialog teardown.
118
+ onDismiss?.();
119
+ }
120
+ }, [backdropOpacity, height, onDismiss, onRunOpenAnimation, translateY, visible]);
121
+
122
+ const onClosePress = useCallback(() => onRunCloseAnimation(true), [onRunCloseAnimation]);
123
+ const onCloseAction = useCallback(() => onRunCloseAnimation(true, 'closeAction'), [onRunCloseAnimation]);
124
+ const onBackdropPress = useCallback(() => onRunCloseAnimation(true, 'backdrop'), [onRunCloseAnimation]);
125
+ const onHardwareBack = useCallback(() => onRunCloseAnimation(true, 'hardwareBack'), [onRunCloseAnimation]);
126
+ const onGestureClose = useCallback(() => onRunCloseAnimation(true, 'gesture'), [onRunCloseAnimation]);
127
+
128
+ useEffect(() => () => {
129
+ ++animationIdRef.current;
130
+ translateY.stopAnimation();
131
+ backdropOpacity.stopAnimation();
132
+ }, [backdropOpacity, translateY]);
76
133
 
77
134
  useEffect(() => {
78
135
  if (!closeRef) return;
@@ -89,7 +146,7 @@ export const useBottomModal = ({
89
146
  },
90
147
  onPanResponderRelease: (_, gestureState) => {
91
148
  if (gestureState.dy > CLOSE_DISTANCE || gestureState.vy > CLOSE_VELOCITY) {
92
- onCloseAnimated();
149
+ onGestureClose();
93
150
  return;
94
151
  }
95
152
  Animated.spring(translateY, { toValue: 0, useNativeDriver: true, bounciness: 0, speed: 18 }).start();
@@ -98,7 +155,7 @@ export const useBottomModal = ({
98
155
  Animated.spring(translateY, { toValue: 0, useNativeDriver: true, bounciness: 0, speed: 18 }).start();
99
156
  },
100
157
  }),
101
- [onCloseAnimated, translateY],
158
+ [onGestureClose, translateY],
102
159
  );
103
160
 
104
161
  return {
@@ -107,7 +164,9 @@ export const useBottomModal = ({
107
164
  bottomInset,
108
165
  translateY,
109
166
  backdropOpacity,
110
- onClosePress,
167
+ onCloseAction,
168
+ onBackdropPress,
169
+ onHardwareBack,
111
170
  onShow,
112
171
  panHandlers: panResponder.panHandlers,
113
172
  };
@@ -7,9 +7,13 @@ const getAndroidNavigationInset = () => {
7
7
  return Math.max(0, Dimensions.get('screen').height - Dimensions.get('window').height);
8
8
  };
9
9
 
10
- export const useBottomModalInsets = () => {
10
+ export const useBottomModalInsets = (requestedTopInset?: number) => {
11
11
  const { top, bottom } = useSafeAreaInsets();
12
- const topInset = useMemo(() => Math.max(top, initialWindowMetrics?.insets.top || 0), [top]);
12
+ const topInset = useMemo(() => {
13
+ const defaultTopInset = Math.max(top, initialWindowMetrics?.insets.top || 0);
14
+ return requestedTopInset === undefined || !Number.isFinite(requestedTopInset)
15
+ ? defaultTopInset : Math.max(0, requestedTopInset);
16
+ }, [requestedTopInset, top]);
13
17
  const bottomInset = useMemo(
14
18
  () => Math.max(bottom, initialWindowMetrics?.insets.bottom || 0, getAndroidNavigationInset()),
15
19
  [bottom],
@@ -8,6 +8,19 @@ export interface BottomModalCloseAction {
8
8
  accessibilityHint?: string;
9
9
  }
10
10
 
11
+ export type BottomModalSizing =
12
+ | { mode: 'content' }
13
+ | { mode: 'fixed'; fraction: number }
14
+ | { mode: 'fullscreen' };
15
+
16
+ export type BottomModalBackdrop = false | {
17
+ color?: string;
18
+ opacity?: number;
19
+ dismissOnPress?: boolean;
20
+ };
21
+
22
+ export type BottomModalCloseReason = 'closeAction' | 'backdrop' | 'gesture' | 'hardwareBack';
23
+
11
24
  export interface BottomModalProps {
12
25
  visible: boolean;
13
26
  onClose: () => void;
@@ -20,4 +33,12 @@ export interface BottomModalProps {
20
33
  contentContainerStyle?: StyleProp<ViewStyle>;
21
34
  isFullScreen?: boolean;
22
35
  shouldAvoidKeyboard?: boolean;
36
+ sizing?: BottomModalSizing;
37
+ topInset?: number;
38
+ showHeader?: boolean;
39
+ backdrop?: BottomModalBackdrop;
40
+ cornerRadius?: number;
41
+ dragToDismiss?: 'none' | 'handle';
42
+ onCloseRequest?: (reason: BottomModalCloseReason) => void;
43
+ onDismiss?: () => void;
23
44
  }
@@ -20,54 +20,76 @@ export const BottomModal = ({
20
20
  contentContainerStyle,
21
21
  isFullScreen = false,
22
22
  shouldAvoidKeyboard = true,
23
+ sizing,
24
+ topInset,
25
+ showHeader = true,
26
+ backdrop,
27
+ cornerRadius,
28
+ dragToDismiss = 'handle',
29
+ onCloseRequest,
30
+ onDismiss,
23
31
  }: BottomModalProps) => {
24
32
  const { colors, spacing, radius } = useUITheme();
25
33
  const scaling = useScaling();
34
+ const backdropOptions = backdrop === false ? undefined : backdrop;
35
+ const backdropColor = backdropOptions?.color ?? colors.text;
36
+ const targetBackdropOpacity = backdropOptions?.opacity !== undefined && Number.isFinite(backdropOptions.opacity)
37
+ ? Math.min(1, Math.max(0, backdropOptions.opacity)) : 0.5;
38
+ const dismissOnBackdropPress = backdropOptions?.dismissOnPress ?? true;
39
+ const resolvedCornerRadius = cornerRadius !== undefined && Number.isFinite(cornerRadius)
40
+ ? Math.max(0, cornerRadius) : undefined;
26
41
  const {
27
42
  bottomInset,
28
43
  height,
29
- topInset,
44
+ topInset: resolvedTopInset,
30
45
  translateY,
31
46
  backdropOpacity,
32
- onClosePress,
47
+ onCloseAction,
48
+ onBackdropPress,
49
+ onHardwareBack,
33
50
  onShow,
34
51
  panHandlers,
35
- } = useBottomModal({ onClose, closeRef });
52
+ } = useBottomModal({ visible, onClose, onCloseRequest, onDismiss, closeRef, topInset });
36
53
 
37
54
  const {
38
55
  modalContentStyle,
39
56
  animatedBackdropStyle,
40
57
  animatedKeyboardContainerStyle,
41
58
  animatedKeyboardBackgroundStyle,
59
+ isBoundedHeight,
42
60
  } = useBottomModalAnimatedStyles(
43
- height, topInset, bottomInset, translateY, backdropOpacity, isFullScreen, shouldAvoidKeyboard,
61
+ height, resolvedTopInset, bottomInset, translateY, backdropOpacity,
62
+ isFullScreen, shouldAvoidKeyboard, sizing, targetBackdropOpacity,
44
63
  );
45
64
 
46
65
  const styles = useMemo(
47
- () => getStyles(colors, spacing, radius, scaling, bottomInset),
48
- [bottomInset, colors, radius, scaling, spacing],
66
+ () => getStyles(colors, spacing, radius, scaling, bottomInset, backdropColor, resolvedCornerRadius),
67
+ [backdropColor, bottomInset, colors, radius, resolvedCornerRadius, scaling, spacing],
49
68
  );
50
69
 
51
70
  return (
52
- <Modal visible={visible} transparent animationType="none" statusBarTranslucent onRequestClose={onClosePress} onShow={onShow}>
71
+ <Modal visible={visible} transparent animationType="none" statusBarTranslucent
72
+ onRequestClose={onHardwareBack} onShow={onShow}>
53
73
  <View style={styles.modalContainer}>
54
- <Animated.View pointerEvents="none" style={[styles.backdrop, animatedBackdropStyle]} />
74
+ {backdrop !== false ? (
75
+ <>
76
+ <Animated.View pointerEvents="none" style={[styles.backdrop, animatedBackdropStyle]} />
77
+ {dismissOnBackdropPress ? (
78
+ <Pressable accessible={false} importantForAccessibility="no"
79
+ onPress={onBackdropPress} style={StyleSheet.absoluteFill} />
80
+ ) : null}
81
+ </>
82
+ ) : null}
55
83
  <Reanimated.View pointerEvents="none" style={[styles.keyboardBackground, animatedKeyboardBackgroundStyle]} />
56
- <Pressable
57
- accessible={false}
58
- importantForAccessibility="no"
59
- onPress={onClosePress}
60
- style={StyleSheet.absoluteFill}
61
- />
62
84
 
63
85
  <Reanimated.View style={animatedKeyboardContainerStyle}>
64
86
  <Animated.View
65
87
  accessibilityViewIsModal
66
- onAccessibilityEscape={onClosePress}
88
+ onAccessibilityEscape={onCloseAction}
67
89
  style={[styles.modalContent, modalContentStyle]}
68
90
  >
69
- <View style={[styles.container, isFullScreen && styles.fullScreenContainer]}>
70
- <View {...panHandlers}>
91
+ <View style={[styles.container, isBoundedHeight && styles.fullScreenContainer]}>
92
+ {showHeader ? <View {...(dragToDismiss === 'handle' ? panHandlers : {})}>
71
93
  <TouchableWithoutFeedback>
72
94
  {customHeader ? (
73
95
  <View>{customHeader}</View>
@@ -84,7 +106,7 @@ export const BottomModal = ({
84
106
  accessibilityRole="button"
85
107
  accessibilityLabel={closeAction.accessibilityLabel}
86
108
  accessibilityHint={closeAction.accessibilityHint ?? title}
87
- onPress={onClosePress}
109
+ onPress={onCloseAction}
88
110
  style={styles.closeButton}
89
111
  >
90
112
  {closeAction.icon}
@@ -93,10 +115,10 @@ export const BottomModal = ({
93
115
  </View>
94
116
  )}
95
117
  </TouchableWithoutFeedback>
96
- </View>
118
+ </View> : null}
97
119
  <View style={[
98
120
  styles.contentContainer,
99
- isFullScreen && styles.fullScreenContentContainer,
121
+ isBoundedHeight && styles.boundedContentContainer,
100
122
  contentContainerStyle,
101
123
  ]}>
102
124
  {children}
@@ -5,6 +5,7 @@ import { useAnimatedStyle } from 'react-native-reanimated';
5
5
  import { useAnimatedKeyboard } from 'react-native-keyboard-controller';
6
6
  import type { UIColors, UIRadius, UISpacing } from '../../../theme';
7
7
  import type { ScalingFunctions } from '../../../utils';
8
+ import type { BottomModalSizing } from '../types/types';
8
9
 
9
10
  export const getStyles = (
10
11
  colors: UIColors,
@@ -12,6 +13,8 @@ export const getStyles = (
12
13
  radius: UIRadius,
13
14
  scaling: ScalingFunctions,
14
15
  bottomInset: number,
16
+ backdropColor: string,
17
+ cornerRadius?: number,
15
18
  ) => {
16
19
  const styles = StyleSheet.create({
17
20
  modalContainer: {
@@ -24,7 +27,7 @@ export const getStyles = (
24
27
  right: 0,
25
28
  bottom: 0,
26
29
  left: 0,
27
- backgroundColor: colors.text,
30
+ backgroundColor: backdropColor,
28
31
  },
29
32
  keyboardBackground: {
30
33
  position: 'absolute',
@@ -35,8 +38,8 @@ export const getStyles = (
35
38
  },
36
39
  modalContent: {
37
40
  backgroundColor: colors.background,
38
- borderTopLeftRadius: scaling.scaleHorizontal(radius.lg),
39
- borderTopRightRadius: scaling.scaleHorizontal(radius.lg),
41
+ borderTopLeftRadius: scaling.scaleHorizontal(cornerRadius ?? radius.lg),
42
+ borderTopRightRadius: scaling.scaleHorizontal(cornerRadius ?? radius.lg),
40
43
  overflow: 'hidden',
41
44
  flexShrink: 1,
42
45
  },
@@ -78,7 +81,7 @@ export const getStyles = (
78
81
  paddingBottom: bottomInset + scaling.scaleVertical(spacing.lg),
79
82
  flexShrink: 1,
80
83
  },
81
- fullScreenContentContainer: {
84
+ boundedContentContainer: {
82
85
  flex: 1,
83
86
  minHeight: 0,
84
87
  },
@@ -95,12 +98,25 @@ export const useBottomModalAnimatedStyles = (
95
98
  backdropOpacity: Animated.Value,
96
99
  isFullScreen: boolean,
97
100
  shouldAvoidKeyboard: boolean,
101
+ sizing?: BottomModalSizing,
102
+ targetBackdropOpacity = 0.5,
98
103
  ) => {
99
- const maxHeight = height - topInset;
104
+ const maxHeight = Math.max(0, height - topInset);
105
+ const mode = sizing?.mode ?? (isFullScreen ? 'fullscreen' : 'content');
106
+ const fraction = sizing?.mode === 'fixed' ? sizing.fraction : undefined;
107
+ const validFraction = fraction !== undefined && Number.isFinite(fraction) && fraction > 0 && fraction <= 1;
108
+ if (__DEV__ && sizing?.mode === 'fixed' && !validFraction) {
109
+ throw new Error('BottomModal fixed fraction must be finite and greater than 0 and at most 1.');
110
+ }
111
+ const resolvedFraction = fraction !== undefined && Number.isFinite(fraction) && fraction > 0
112
+ ? Math.min(1, fraction) : 0.5;
113
+ const modalHeight = mode === 'fullscreen' ? maxHeight
114
+ : mode === 'fixed' ? maxHeight * resolvedFraction : undefined;
115
+ const isBoundedHeight = modalHeight !== undefined;
100
116
  const { height: keyboardHeight } = useAnimatedKeyboard();
101
117
  const animatedBackdropStyle = useMemo(
102
- () => ({ opacity: backdropOpacity.interpolate({ inputRange: [0, 1], outputRange: [0, 0.5] }) }),
103
- [backdropOpacity],
118
+ () => ({ opacity: backdropOpacity.interpolate({ inputRange: [0, 1], outputRange: [0, targetBackdropOpacity] }) }),
119
+ [backdropOpacity, targetBackdropOpacity],
104
120
  );
105
121
  const animatedKeyboardContainerStyle = useAnimatedStyle(() => {
106
122
  if (!shouldAvoidKeyboard) return { marginBottom: 0, maxHeight };
@@ -111,9 +127,9 @@ export const useBottomModalAnimatedStyles = (
111
127
  height: shouldAvoidKeyboard ? Math.max(keyboardHeight.value - bottomInset, 0) : 0,
112
128
  }));
113
129
  const modalContentStyle = useMemo<ViewStyle>(
114
- () => ({ maxHeight, height: isFullScreen ? maxHeight : undefined, transform: [{ translateY }] }),
115
- [isFullScreen, maxHeight, translateY],
130
+ () => ({ maxHeight, height: modalHeight, transform: [{ translateY }] }),
131
+ [maxHeight, modalHeight, translateY],
116
132
  );
117
133
 
118
- return { animatedBackdropStyle, animatedKeyboardContainerStyle, animatedKeyboardBackgroundStyle, modalContentStyle };
134
+ return { animatedBackdropStyle, animatedKeyboardContainerStyle, animatedKeyboardBackgroundStyle, modalContentStyle, isBoundedHeight };
119
135
  };
@@ -61,10 +61,10 @@ export const useSwitchAnimatedStyles = (
61
61
  ) => {
62
62
  const trackStyle = useAnimatedStyle(() => ({
63
63
  backgroundColor: interpolateColor(progress.value, [0, 1], [inactiveTrackColor, activeTrackColor]),
64
- }), [activeTrackColor, inactiveTrackColor]);
64
+ }));
65
65
  const thumbStyle = useAnimatedStyle(() => ({
66
66
  transform: [{ translateX: progress.value * travelDistance }],
67
- }), [travelDistance]);
67
+ }));
68
68
 
69
69
  return { trackStyle, thumbStyle };
70
70
  };