@mustmove/bottom-sheet 1.0.0
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/LICENSE +21 -0
- package/README.md +66 -0
- package/mock.js +231 -0
- package/package.json +107 -0
- package/src/components/bottomSheet/BottomSheet.tsx +1885 -0
- package/src/components/bottomSheet/BottomSheetBody.tsx +44 -0
- package/src/components/bottomSheet/BottomSheetContent.tsx +261 -0
- package/src/components/bottomSheet/constants.ts +58 -0
- package/src/components/bottomSheet/index.ts +2 -0
- package/src/components/bottomSheet/styles.ts +11 -0
- package/src/components/bottomSheet/types.d.ts +358 -0
- package/src/components/bottomSheetBackdrop/BottomSheetBackdrop.tsx +165 -0
- package/src/components/bottomSheetBackdrop/constants.ts +22 -0
- package/src/components/bottomSheetBackdrop/index.ts +2 -0
- package/src/components/bottomSheetBackdrop/styles.ts +8 -0
- package/src/components/bottomSheetBackdrop/types.d.ts +58 -0
- package/src/components/bottomSheetBackground/BottomSheetBackground.tsx +20 -0
- package/src/components/bottomSheetBackground/BottomSheetBackgroundContainer.tsx +35 -0
- package/src/components/bottomSheetBackground/index.ts +2 -0
- package/src/components/bottomSheetBackground/styles.ts +9 -0
- package/src/components/bottomSheetBackground/types.d.ts +12 -0
- package/src/components/bottomSheetDebugView/BottomSheetDebugView.tsx +26 -0
- package/src/components/bottomSheetDebugView/ReText.tsx +72 -0
- package/src/components/bottomSheetDebugView/ReText.webx.tsx +55 -0
- package/src/components/bottomSheetDebugView/index.ts +1 -0
- package/src/components/bottomSheetDebugView/styles.ts +19 -0
- package/src/components/bottomSheetDebugView/styles.web.ts +20 -0
- package/src/components/bottomSheetDraggableView/BottomSheetDraggableView.tsx +123 -0
- package/src/components/bottomSheetDraggableView/index.ts +1 -0
- package/src/components/bottomSheetDraggableView/types.d.ts +9 -0
- package/src/components/bottomSheetFooter/BottomSheetFooter.tsx +119 -0
- package/src/components/bottomSheetFooter/BottomSheetFooterContainer.tsx +43 -0
- package/src/components/bottomSheetFooter/index.ts +3 -0
- package/src/components/bottomSheetFooter/styles.ts +12 -0
- package/src/components/bottomSheetFooter/types.d.ts +41 -0
- package/src/components/bottomSheetGestureHandlersProvider/BottomSheetGestureHandlersProvider.tsx +69 -0
- package/src/components/bottomSheetGestureHandlersProvider/index.ts +1 -0
- package/src/components/bottomSheetGestureHandlersProvider/types.d.ts +8 -0
- package/src/components/bottomSheetHandle/BottomSheetHandle.tsx +51 -0
- package/src/components/bottomSheetHandle/BottomSheetHandleContainer.tsx +187 -0
- package/src/components/bottomSheetHandle/constants.ts +12 -0
- package/src/components/bottomSheetHandle/index.ts +6 -0
- package/src/components/bottomSheetHandle/styles.ts +23 -0
- package/src/components/bottomSheetHandle/types.d.ts +52 -0
- package/src/components/bottomSheetHostingContainer/BottomSheetHostingContainer.tsx +130 -0
- package/src/components/bottomSheetHostingContainer/index.ts +2 -0
- package/src/components/bottomSheetHostingContainer/styles.ts +5 -0
- package/src/components/bottomSheetHostingContainer/styles.web.ts +11 -0
- package/src/components/bottomSheetHostingContainer/types.d.ts +17 -0
- package/src/components/bottomSheetModal/BottomSheetModal.tsx +482 -0
- package/src/components/bottomSheetModal/constants.ts +4 -0
- package/src/components/bottomSheetModal/index.ts +6 -0
- package/src/components/bottomSheetModal/types.d.ts +67 -0
- package/src/components/bottomSheetModalProvider/BottomSheetModalProvider.tsx +211 -0
- package/src/components/bottomSheetModalProvider/index.ts +1 -0
- package/src/components/bottomSheetModalProvider/types.d.ts +12 -0
- package/src/components/bottomSheetRefreshControl/BottomSheetRefreshControl.android.tsx +84 -0
- package/src/components/bottomSheetRefreshControl/BottomSheetRefreshControl.tsx +1 -0
- package/src/components/bottomSheetRefreshControl/index.ts +20 -0
- package/src/components/bottomSheetScrollable/BottomSheetDraggableScrollable.tsx +23 -0
- package/src/components/bottomSheetScrollable/BottomSheetFlashList.tsx +88 -0
- package/src/components/bottomSheetScrollable/BottomSheetFlashList.web.tsx +1 -0
- package/src/components/bottomSheetScrollable/BottomSheetFlatList.tsx +26 -0
- package/src/components/bottomSheetScrollable/BottomSheetScrollView.tsx +27 -0
- package/src/components/bottomSheetScrollable/BottomSheetSectionList.tsx +29 -0
- package/src/components/bottomSheetScrollable/BottomSheetVirtualizedList.tsx +27 -0
- package/src/components/bottomSheetScrollable/ScrollableContainer.android.tsx +55 -0
- package/src/components/bottomSheetScrollable/ScrollableContainer.tsx +22 -0
- package/src/components/bottomSheetScrollable/ScrollableContainer.web.tsx +102 -0
- package/src/components/bottomSheetScrollable/createBottomSheetScrollableComponent.tsx +153 -0
- package/src/components/bottomSheetScrollable/index.ts +15 -0
- package/src/components/bottomSheetScrollable/styles.ts +8 -0
- package/src/components/bottomSheetScrollable/types.d.ts +280 -0
- package/src/components/bottomSheetScrollable/useBottomSheetContentSizeSetter.ts +32 -0
- package/src/components/bottomSheetTextInput/BottomSheetTextInput.tsx +127 -0
- package/src/components/bottomSheetTextInput/index.ts +2 -0
- package/src/components/bottomSheetTextInput/types.ts +3 -0
- package/src/components/bottomSheetView/BottomSheetView.tsx +93 -0
- package/src/components/bottomSheetView/index.ts +1 -0
- package/src/components/bottomSheetView/styles.ts +10 -0
- package/src/components/bottomSheetView/types.d.ts +24 -0
- package/src/components/touchables/Touchables.ios.tsx +5 -0
- package/src/components/touchables/Touchables.tsx +5 -0
- package/src/components/touchables/index.ts +20 -0
- package/src/constants.ts +159 -0
- package/src/contexts/external.ts +8 -0
- package/src/contexts/gesture.ts +13 -0
- package/src/contexts/index.ts +15 -0
- package/src/contexts/internal.ts +65 -0
- package/src/contexts/modal/external.ts +11 -0
- package/src/contexts/modal/internal.ts +25 -0
- package/src/hooks/index.ts +29 -0
- package/src/hooks/useAnimatedDetents.ts +119 -0
- package/src/hooks/useAnimatedKeyboard.ts +174 -0
- package/src/hooks/useAnimatedLayout.ts +109 -0
- package/src/hooks/useBottomSheet.ts +12 -0
- package/src/hooks/useBottomSheetContentContainerStyle.ts +88 -0
- package/src/hooks/useBottomSheetGestureHandlers.ts +12 -0
- package/src/hooks/useBottomSheetInternal.ts +25 -0
- package/src/hooks/useBottomSheetModal.ts +12 -0
- package/src/hooks/useBottomSheetModalInternal.ts +25 -0
- package/src/hooks/useBottomSheetScrollableCreator.tsx +60 -0
- package/src/hooks/useBottomSheetSpringConfigs.ts +11 -0
- package/src/hooks/useBottomSheetTimingConfigs.ts +36 -0
- package/src/hooks/useBoundingClientRect.ts +77 -0
- package/src/hooks/useGestureEventsHandlersDefault.tsx +436 -0
- package/src/hooks/useGestureEventsHandlersDefault.web.tsx +418 -0
- package/src/hooks/useGestureHandler.ts +90 -0
- package/src/hooks/usePropsValidator.ts +108 -0
- package/src/hooks/useReactiveSharedValue.ts +45 -0
- package/src/hooks/useScrollEventsHandlersDefault.ts +167 -0
- package/src/hooks/useScrollHandler.ts +72 -0
- package/src/hooks/useScrollHandler.web.ts +181 -0
- package/src/hooks/useScrollable.ts +131 -0
- package/src/hooks/useScrollableSetter.ts +56 -0
- package/src/hooks/useStableCallback.ts +26 -0
- package/src/index.ts +79 -0
- package/src/types.d.ts +336 -0
- package/src/utilities/animate.ts +56 -0
- package/src/utilities/clamp.ts +8 -0
- package/src/utilities/easingExp.ts +10 -0
- package/src/utilities/findNodeHandle.ts +1 -0
- package/src/utilities/findNodeHandle.web.ts +33 -0
- package/src/utilities/getKeyboardAnimationConfigs.ts +44 -0
- package/src/utilities/getRefNativeTag.web.ts +6 -0
- package/src/utilities/id.ts +6 -0
- package/src/utilities/index.ts +7 -0
- package/src/utilities/isFabricInstalled.ts +9 -0
- package/src/utilities/logger.ts +55 -0
- package/src/utilities/noop.ts +7 -0
- package/src/utilities/normalizeSnapPoint.ts +17 -0
- package/src/utilities/snapPoint.ts +11 -0
- package/src/utilities/validateSnapPoint.ts +20 -0
|
@@ -0,0 +1,1885 @@
|
|
|
1
|
+
import invariant from 'invariant';
|
|
2
|
+
import React, {
|
|
3
|
+
forwardRef,
|
|
4
|
+
memo,
|
|
5
|
+
useCallback,
|
|
6
|
+
useEffect,
|
|
7
|
+
useImperativeHandle,
|
|
8
|
+
useMemo,
|
|
9
|
+
} from 'react';
|
|
10
|
+
import { Platform, StyleSheet } from 'react-native';
|
|
11
|
+
import { State } from 'react-native-gesture-handler';
|
|
12
|
+
import Animated, {
|
|
13
|
+
cancelAnimation,
|
|
14
|
+
Extrapolation,
|
|
15
|
+
interpolate,
|
|
16
|
+
ReduceMotion,
|
|
17
|
+
runOnJS,
|
|
18
|
+
runOnUI,
|
|
19
|
+
useAnimatedReaction,
|
|
20
|
+
useDerivedValue,
|
|
21
|
+
useReducedMotion,
|
|
22
|
+
useSharedValue,
|
|
23
|
+
type WithSpringConfig,
|
|
24
|
+
type WithTimingConfig,
|
|
25
|
+
} from 'react-native-reanimated';
|
|
26
|
+
import {
|
|
27
|
+
ANIMATION_SOURCE,
|
|
28
|
+
ANIMATION_STATUS,
|
|
29
|
+
INITIAL_LAYOUT_VALUE,
|
|
30
|
+
KEYBOARD_BEHAVIOR,
|
|
31
|
+
KEYBOARD_BLUR_BEHAVIOR,
|
|
32
|
+
KEYBOARD_INPUT_MODE,
|
|
33
|
+
KEYBOARD_STATUS,
|
|
34
|
+
SHEET_STATE,
|
|
35
|
+
SNAP_POINT_TYPE,
|
|
36
|
+
} from '../../constants';
|
|
37
|
+
import {
|
|
38
|
+
BottomSheetInternalProvider,
|
|
39
|
+
BottomSheetProvider,
|
|
40
|
+
} from '../../contexts';
|
|
41
|
+
import {
|
|
42
|
+
useAnimatedDetents,
|
|
43
|
+
useAnimatedKeyboard,
|
|
44
|
+
useAnimatedLayout,
|
|
45
|
+
usePropsValidator,
|
|
46
|
+
useReactiveSharedValue,
|
|
47
|
+
useScrollable,
|
|
48
|
+
useStableCallback,
|
|
49
|
+
} from '../../hooks';
|
|
50
|
+
import type { AnimationState, BottomSheetMethods } from '../../types';
|
|
51
|
+
import {
|
|
52
|
+
animate,
|
|
53
|
+
getKeyboardAnimationConfigs,
|
|
54
|
+
normalizeSnapPoint,
|
|
55
|
+
print,
|
|
56
|
+
} from '../../utilities';
|
|
57
|
+
import { BottomSheetBackgroundContainer } from '../bottomSheetBackground';
|
|
58
|
+
// import BottomSheetDebugView from '../bottomSheetDebugView';
|
|
59
|
+
import { BottomSheetFooterContainer } from '../bottomSheetFooter';
|
|
60
|
+
import BottomSheetGestureHandlersProvider from '../bottomSheetGestureHandlersProvider';
|
|
61
|
+
import {
|
|
62
|
+
BottomSheetHandle,
|
|
63
|
+
BottomSheetHandleContainer,
|
|
64
|
+
} from '../bottomSheetHandle';
|
|
65
|
+
import { BottomSheetHostingContainer } from '../bottomSheetHostingContainer';
|
|
66
|
+
import { BottomSheetBody } from './BottomSheetBody';
|
|
67
|
+
import { BottomSheetContent } from './BottomSheetContent';
|
|
68
|
+
import {
|
|
69
|
+
DEFAULT_ACCESSIBILITY_LABEL,
|
|
70
|
+
DEFAULT_ACCESSIBILITY_ROLE,
|
|
71
|
+
DEFAULT_ACCESSIBLE,
|
|
72
|
+
DEFAULT_ANIMATE_ON_MOUNT,
|
|
73
|
+
DEFAULT_DYNAMIC_SIZING,
|
|
74
|
+
DEFAULT_ENABLE_BLUR_KEYBOARD_ON_GESTURE,
|
|
75
|
+
DEFAULT_ENABLE_CONTENT_PANNING_GESTURE,
|
|
76
|
+
DEFAULT_ENABLE_OVER_DRAG,
|
|
77
|
+
DEFAULT_ENABLE_PAN_DOWN_TO_CLOSE,
|
|
78
|
+
DEFAULT_KEYBOARD_BEHAVIOR,
|
|
79
|
+
DEFAULT_KEYBOARD_BLUR_BEHAVIOR,
|
|
80
|
+
DEFAULT_KEYBOARD_INDEX,
|
|
81
|
+
DEFAULT_KEYBOARD_INPUT_MODE,
|
|
82
|
+
DEFAULT_OVER_DRAG_RESISTANCE_FACTOR,
|
|
83
|
+
INITIAL_POSITION,
|
|
84
|
+
INITIAL_VALUE,
|
|
85
|
+
} from './constants';
|
|
86
|
+
import type { AnimateToPositionType, BottomSheetProps } from './types';
|
|
87
|
+
|
|
88
|
+
Animated.addWhitelistedUIProps({
|
|
89
|
+
decelerationRate: true,
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
type BottomSheet = BottomSheetMethods;
|
|
93
|
+
|
|
94
|
+
const BottomSheetComponent = forwardRef<BottomSheet, BottomSheetProps>(
|
|
95
|
+
function BottomSheet(props, ref) {
|
|
96
|
+
//#region extract props
|
|
97
|
+
const {
|
|
98
|
+
// animations configurations
|
|
99
|
+
animationConfigs: _providedAnimationConfigs,
|
|
100
|
+
|
|
101
|
+
// configurations
|
|
102
|
+
index: _providedIndex = 0,
|
|
103
|
+
snapPoints: _providedSnapPoints,
|
|
104
|
+
animateOnMount = DEFAULT_ANIMATE_ON_MOUNT,
|
|
105
|
+
enableContentPanningGesture = DEFAULT_ENABLE_CONTENT_PANNING_GESTURE,
|
|
106
|
+
enableHandlePanningGesture,
|
|
107
|
+
enableOverDrag = DEFAULT_ENABLE_OVER_DRAG,
|
|
108
|
+
enablePanDownToClose = DEFAULT_ENABLE_PAN_DOWN_TO_CLOSE,
|
|
109
|
+
enableDynamicSizing = DEFAULT_DYNAMIC_SIZING,
|
|
110
|
+
overDragResistanceFactor = DEFAULT_OVER_DRAG_RESISTANCE_FACTOR,
|
|
111
|
+
overrideReduceMotion: _providedOverrideReduceMotion,
|
|
112
|
+
|
|
113
|
+
// styles
|
|
114
|
+
style,
|
|
115
|
+
containerStyle: _providedContainerStyle,
|
|
116
|
+
backgroundStyle: _providedBackgroundStyle,
|
|
117
|
+
handleStyle: _providedHandleStyle,
|
|
118
|
+
handleIndicatorStyle: _providedHandleIndicatorStyle,
|
|
119
|
+
|
|
120
|
+
// hooks
|
|
121
|
+
gestureEventsHandlersHook,
|
|
122
|
+
|
|
123
|
+
// keyboard
|
|
124
|
+
keyboardBehavior = DEFAULT_KEYBOARD_BEHAVIOR,
|
|
125
|
+
keyboardBlurBehavior = DEFAULT_KEYBOARD_BLUR_BEHAVIOR,
|
|
126
|
+
android_keyboardInputMode = DEFAULT_KEYBOARD_INPUT_MODE,
|
|
127
|
+
enableBlurKeyboardOnGesture = DEFAULT_ENABLE_BLUR_KEYBOARD_ON_GESTURE,
|
|
128
|
+
|
|
129
|
+
// layout
|
|
130
|
+
containerLayoutState,
|
|
131
|
+
topInset = 0,
|
|
132
|
+
bottomInset = 0,
|
|
133
|
+
maxDynamicContentSize,
|
|
134
|
+
containerHeight,
|
|
135
|
+
containerOffset,
|
|
136
|
+
|
|
137
|
+
// animated callback shared values
|
|
138
|
+
animatedPosition: _providedAnimatedPosition,
|
|
139
|
+
animatedIndex: _providedAnimatedIndex,
|
|
140
|
+
|
|
141
|
+
// gestures
|
|
142
|
+
simultaneousHandlers: _providedSimultaneousHandlers,
|
|
143
|
+
waitFor: _providedWaitFor,
|
|
144
|
+
activeOffsetX: _providedActiveOffsetX,
|
|
145
|
+
activeOffsetY: _providedActiveOffsetY,
|
|
146
|
+
failOffsetX: _providedFailOffsetX,
|
|
147
|
+
failOffsetY: _providedFailOffsetY,
|
|
148
|
+
|
|
149
|
+
// callbacks
|
|
150
|
+
onChange: _providedOnChange,
|
|
151
|
+
onClose: _providedOnClose,
|
|
152
|
+
onAnimate: _providedOnAnimate,
|
|
153
|
+
|
|
154
|
+
// private
|
|
155
|
+
$modal = false,
|
|
156
|
+
detached = false,
|
|
157
|
+
|
|
158
|
+
// components
|
|
159
|
+
handleComponent = BottomSheetHandle,
|
|
160
|
+
backdropComponent: BackdropComponent,
|
|
161
|
+
backgroundComponent,
|
|
162
|
+
footerComponent,
|
|
163
|
+
children,
|
|
164
|
+
|
|
165
|
+
// accessibility
|
|
166
|
+
accessible: _providedAccessible = DEFAULT_ACCESSIBLE,
|
|
167
|
+
accessibilityLabel:
|
|
168
|
+
_providedAccessibilityLabel = DEFAULT_ACCESSIBILITY_LABEL,
|
|
169
|
+
accessibilityRole:
|
|
170
|
+
_providedAccessibilityRole = DEFAULT_ACCESSIBILITY_ROLE,
|
|
171
|
+
} = props;
|
|
172
|
+
//#endregion
|
|
173
|
+
|
|
174
|
+
//#region validate props
|
|
175
|
+
if (__DEV__) {
|
|
176
|
+
// biome-ignore lint/correctness/useHookAtTopLevel: used in development only.
|
|
177
|
+
usePropsValidator({
|
|
178
|
+
index: _providedIndex,
|
|
179
|
+
snapPoints: _providedSnapPoints,
|
|
180
|
+
enableDynamicSizing,
|
|
181
|
+
topInset,
|
|
182
|
+
bottomInset,
|
|
183
|
+
containerHeight,
|
|
184
|
+
containerOffset,
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
//#endregion
|
|
188
|
+
|
|
189
|
+
//#region layout variables
|
|
190
|
+
const animatedLayoutState = useAnimatedLayout(
|
|
191
|
+
containerLayoutState,
|
|
192
|
+
topInset,
|
|
193
|
+
bottomInset,
|
|
194
|
+
$modal,
|
|
195
|
+
handleComponent === null
|
|
196
|
+
);
|
|
197
|
+
const animatedDetentsState = useAnimatedDetents(
|
|
198
|
+
_providedSnapPoints,
|
|
199
|
+
animatedLayoutState,
|
|
200
|
+
enableDynamicSizing,
|
|
201
|
+
maxDynamicContentSize,
|
|
202
|
+
detached,
|
|
203
|
+
$modal,
|
|
204
|
+
bottomInset
|
|
205
|
+
);
|
|
206
|
+
const animatedSheetHeight = useDerivedValue(() => {
|
|
207
|
+
const { containerHeight } = animatedLayoutState.get();
|
|
208
|
+
const { highestDetentPosition } = animatedDetentsState.get();
|
|
209
|
+
|
|
210
|
+
if (highestDetentPosition === undefined) {
|
|
211
|
+
return INITIAL_LAYOUT_VALUE;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
return containerHeight - highestDetentPosition;
|
|
215
|
+
}, [animatedLayoutState, animatedDetentsState]);
|
|
216
|
+
const animatedCurrentIndex = useReactiveSharedValue(
|
|
217
|
+
animateOnMount ? -1 : _providedIndex
|
|
218
|
+
);
|
|
219
|
+
const animatedPosition = useSharedValue(INITIAL_POSITION);
|
|
220
|
+
|
|
221
|
+
// conditional
|
|
222
|
+
const isAnimatedOnMount = useSharedValue(
|
|
223
|
+
!animateOnMount || _providedIndex === -1
|
|
224
|
+
);
|
|
225
|
+
const isLayoutCalculated = useDerivedValue(() => {
|
|
226
|
+
let isContainerHeightCalculated = false;
|
|
227
|
+
const { containerHeight, handleHeight } = animatedLayoutState.get();
|
|
228
|
+
//container height was provided.
|
|
229
|
+
if (containerHeight !== null || containerHeight !== undefined) {
|
|
230
|
+
isContainerHeightCalculated = true;
|
|
231
|
+
}
|
|
232
|
+
// container height did set.
|
|
233
|
+
if (containerHeight !== INITIAL_LAYOUT_VALUE) {
|
|
234
|
+
isContainerHeightCalculated = true;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
let isHandleHeightCalculated = false;
|
|
238
|
+
// handle component is null.
|
|
239
|
+
if (handleComponent === null) {
|
|
240
|
+
isHandleHeightCalculated = true;
|
|
241
|
+
}
|
|
242
|
+
// handle height did set.
|
|
243
|
+
if (handleHeight !== INITIAL_LAYOUT_VALUE) {
|
|
244
|
+
isHandleHeightCalculated = true;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
let isSnapPointsNormalized = false;
|
|
248
|
+
const { detents } = animatedDetentsState.get();
|
|
249
|
+
// the first snap point did normalized
|
|
250
|
+
if (detents) {
|
|
251
|
+
isSnapPointsNormalized = true;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
return (
|
|
255
|
+
isContainerHeightCalculated &&
|
|
256
|
+
isHandleHeightCalculated &&
|
|
257
|
+
isSnapPointsNormalized
|
|
258
|
+
);
|
|
259
|
+
}, [animatedLayoutState, animatedDetentsState, handleComponent]);
|
|
260
|
+
const isInTemporaryPosition = useSharedValue(false);
|
|
261
|
+
const animatedContainerHeightDidChange = useSharedValue(false);
|
|
262
|
+
|
|
263
|
+
// gesture
|
|
264
|
+
const animatedContentGestureState = useSharedValue<State>(
|
|
265
|
+
State.UNDETERMINED
|
|
266
|
+
);
|
|
267
|
+
const animatedHandleGestureState = useSharedValue<State>(
|
|
268
|
+
State.UNDETERMINED
|
|
269
|
+
);
|
|
270
|
+
//#endregion
|
|
271
|
+
|
|
272
|
+
//#region hooks variables
|
|
273
|
+
// keyboard
|
|
274
|
+
const { state: animatedKeyboardState, textInputNodesRef } =
|
|
275
|
+
useAnimatedKeyboard();
|
|
276
|
+
const userReduceMotionSetting = useReducedMotion();
|
|
277
|
+
const reduceMotion = useMemo(() => {
|
|
278
|
+
return !_providedOverrideReduceMotion ||
|
|
279
|
+
_providedOverrideReduceMotion === ReduceMotion.System
|
|
280
|
+
? userReduceMotionSetting
|
|
281
|
+
: _providedOverrideReduceMotion === ReduceMotion.Always;
|
|
282
|
+
}, [userReduceMotionSetting, _providedOverrideReduceMotion]);
|
|
283
|
+
//#endregion
|
|
284
|
+
|
|
285
|
+
//#region state/dynamic variables
|
|
286
|
+
// states
|
|
287
|
+
const animatedAnimationState = useSharedValue<AnimationState>({
|
|
288
|
+
status: ANIMATION_STATUS.UNDETERMINED,
|
|
289
|
+
source: ANIMATION_SOURCE.MOUNT,
|
|
290
|
+
});
|
|
291
|
+
const animatedSheetState = useDerivedValue(() => {
|
|
292
|
+
const { detents, closedDetentPosition } = animatedDetentsState.get();
|
|
293
|
+
|
|
294
|
+
if (
|
|
295
|
+
!detents ||
|
|
296
|
+
detents.length === 0 ||
|
|
297
|
+
closedDetentPosition === undefined
|
|
298
|
+
) {
|
|
299
|
+
return SHEET_STATE.CLOSED;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// closed position = position >= container height
|
|
303
|
+
if (animatedPosition.value >= closedDetentPosition) {
|
|
304
|
+
return SHEET_STATE.CLOSED;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
const { containerHeight } = animatedLayoutState.get();
|
|
308
|
+
// extended position = container height - sheet height
|
|
309
|
+
const extendedPosition = containerHeight - animatedSheetHeight.value;
|
|
310
|
+
if (animatedPosition.value === extendedPosition) {
|
|
311
|
+
return SHEET_STATE.EXTENDED;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// extended position with keyboard =
|
|
315
|
+
// container height - (sheet height + keyboard height in root container)
|
|
316
|
+
const keyboardHeightInContainer =
|
|
317
|
+
animatedKeyboardState.get().heightWithinContainer;
|
|
318
|
+
const extendedPositionWithKeyboard = Math.max(
|
|
319
|
+
0,
|
|
320
|
+
containerHeight -
|
|
321
|
+
(animatedSheetHeight.value + keyboardHeightInContainer)
|
|
322
|
+
);
|
|
323
|
+
|
|
324
|
+
// detect if keyboard is open and the sheet is in temporary position
|
|
325
|
+
if (
|
|
326
|
+
keyboardBehavior === KEYBOARD_BEHAVIOR.interactive &&
|
|
327
|
+
isInTemporaryPosition.value &&
|
|
328
|
+
animatedPosition.value === extendedPositionWithKeyboard
|
|
329
|
+
) {
|
|
330
|
+
return SHEET_STATE.EXTENDED;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// fill parent = 0
|
|
334
|
+
if (animatedPosition.value === 0) {
|
|
335
|
+
return SHEET_STATE.FILL_PARENT;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// detect if position is below extended point
|
|
339
|
+
if (animatedPosition.value < extendedPosition) {
|
|
340
|
+
return SHEET_STATE.OVER_EXTENDED;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
return SHEET_STATE.OPENED;
|
|
344
|
+
}, [
|
|
345
|
+
animatedLayoutState,
|
|
346
|
+
animatedDetentsState,
|
|
347
|
+
animatedKeyboardState,
|
|
348
|
+
animatedPosition,
|
|
349
|
+
animatedSheetHeight,
|
|
350
|
+
isInTemporaryPosition,
|
|
351
|
+
keyboardBehavior,
|
|
352
|
+
]);
|
|
353
|
+
const {
|
|
354
|
+
state: animatedScrollableState,
|
|
355
|
+
status: animatedScrollableStatus,
|
|
356
|
+
setScrollableRef,
|
|
357
|
+
removeScrollableRef,
|
|
358
|
+
} = useScrollable(
|
|
359
|
+
enableContentPanningGesture,
|
|
360
|
+
animatedSheetState,
|
|
361
|
+
animatedKeyboardState,
|
|
362
|
+
animatedAnimationState
|
|
363
|
+
);
|
|
364
|
+
// dynamic
|
|
365
|
+
const animatedIndex = useDerivedValue(() => {
|
|
366
|
+
const { detents } = animatedDetentsState.get();
|
|
367
|
+
if (!detents || detents.length === 0) {
|
|
368
|
+
return -1;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
const adjustedSnapPoints = detents.slice().reverse();
|
|
372
|
+
const adjustedSnapPointsIndexes = detents
|
|
373
|
+
.slice()
|
|
374
|
+
.map((_, index: number) => index)
|
|
375
|
+
.reverse();
|
|
376
|
+
|
|
377
|
+
const { containerHeight } = animatedLayoutState.get();
|
|
378
|
+
/**
|
|
379
|
+
* we add the close state index `-1`
|
|
380
|
+
*/
|
|
381
|
+
adjustedSnapPoints.push(containerHeight);
|
|
382
|
+
adjustedSnapPointsIndexes.push(-1);
|
|
383
|
+
|
|
384
|
+
const currentIndex = isLayoutCalculated.value
|
|
385
|
+
? interpolate(
|
|
386
|
+
animatedPosition.value,
|
|
387
|
+
adjustedSnapPoints,
|
|
388
|
+
adjustedSnapPointsIndexes,
|
|
389
|
+
Extrapolation.CLAMP
|
|
390
|
+
)
|
|
391
|
+
: -1;
|
|
392
|
+
|
|
393
|
+
const {
|
|
394
|
+
status: animationStatus,
|
|
395
|
+
source: animationSource,
|
|
396
|
+
nextIndex,
|
|
397
|
+
nextPosition,
|
|
398
|
+
} = animatedAnimationState.get();
|
|
399
|
+
/**
|
|
400
|
+
* if the sheet is currently running an animation by the keyboard opening,
|
|
401
|
+
* then we clamp the index on android with resize keyboard mode.
|
|
402
|
+
*/
|
|
403
|
+
if (
|
|
404
|
+
android_keyboardInputMode === KEYBOARD_INPUT_MODE.adjustResize &&
|
|
405
|
+
animationStatus === ANIMATION_STATUS.RUNNING &&
|
|
406
|
+
animationSource === ANIMATION_SOURCE.KEYBOARD &&
|
|
407
|
+
isInTemporaryPosition.value
|
|
408
|
+
) {
|
|
409
|
+
return Math.max(animatedCurrentIndex.value, currentIndex);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* if the sheet is currently running an animation by snap point change - usually caused
|
|
414
|
+
* by dynamic content height -, then we return the next position index.
|
|
415
|
+
*/
|
|
416
|
+
if (
|
|
417
|
+
animationStatus === ANIMATION_STATUS.RUNNING &&
|
|
418
|
+
animationSource === ANIMATION_SOURCE.SNAP_POINT_CHANGE &&
|
|
419
|
+
nextIndex !== undefined &&
|
|
420
|
+
nextPosition !== undefined
|
|
421
|
+
) {
|
|
422
|
+
return nextIndex;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
return currentIndex;
|
|
426
|
+
}, [
|
|
427
|
+
android_keyboardInputMode,
|
|
428
|
+
animatedAnimationState,
|
|
429
|
+
animatedLayoutState,
|
|
430
|
+
animatedCurrentIndex,
|
|
431
|
+
animatedPosition,
|
|
432
|
+
animatedDetentsState,
|
|
433
|
+
isInTemporaryPosition,
|
|
434
|
+
isLayoutCalculated,
|
|
435
|
+
]);
|
|
436
|
+
//#endregion
|
|
437
|
+
|
|
438
|
+
//#region private methods
|
|
439
|
+
const handleOnChange = useCallback(
|
|
440
|
+
function handleOnChange(index: number, position: number) {
|
|
441
|
+
if (__DEV__) {
|
|
442
|
+
print({
|
|
443
|
+
component: 'BottomSheet',
|
|
444
|
+
method: 'handleOnChange',
|
|
445
|
+
category: 'callback',
|
|
446
|
+
params: {
|
|
447
|
+
index,
|
|
448
|
+
position,
|
|
449
|
+
},
|
|
450
|
+
});
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
if (!_providedOnChange) {
|
|
454
|
+
return;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
const { dynamicDetentIndex } = animatedDetentsState.get();
|
|
458
|
+
|
|
459
|
+
_providedOnChange(
|
|
460
|
+
index,
|
|
461
|
+
position,
|
|
462
|
+
index === dynamicDetentIndex
|
|
463
|
+
? SNAP_POINT_TYPE.DYNAMIC
|
|
464
|
+
: SNAP_POINT_TYPE.PROVIDED
|
|
465
|
+
);
|
|
466
|
+
},
|
|
467
|
+
[_providedOnChange, animatedDetentsState]
|
|
468
|
+
);
|
|
469
|
+
const handleOnAnimate = useCallback(
|
|
470
|
+
function handleOnAnimate(targetIndex: number, targetPosition: number) {
|
|
471
|
+
if (__DEV__) {
|
|
472
|
+
print({
|
|
473
|
+
component: 'BottomSheet',
|
|
474
|
+
method: 'handleOnAnimate',
|
|
475
|
+
category: 'callback',
|
|
476
|
+
params: {
|
|
477
|
+
toIndex: targetIndex,
|
|
478
|
+
toPosition: targetPosition,
|
|
479
|
+
fromIndex: animatedCurrentIndex.value,
|
|
480
|
+
fromPosition: animatedPosition.value,
|
|
481
|
+
},
|
|
482
|
+
});
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
if (targetIndex === animatedCurrentIndex.get()) {
|
|
486
|
+
return;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
if (!_providedOnAnimate) {
|
|
490
|
+
return;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
_providedOnAnimate(
|
|
494
|
+
animatedCurrentIndex.value,
|
|
495
|
+
targetIndex,
|
|
496
|
+
animatedPosition.value,
|
|
497
|
+
targetPosition
|
|
498
|
+
);
|
|
499
|
+
},
|
|
500
|
+
[_providedOnAnimate, animatedCurrentIndex, animatedPosition]
|
|
501
|
+
);
|
|
502
|
+
const handleOnClose = useCallback(
|
|
503
|
+
function handleOnClose() {
|
|
504
|
+
if (__DEV__) {
|
|
505
|
+
print({
|
|
506
|
+
component: 'BottomSheet',
|
|
507
|
+
method: 'handleOnClose',
|
|
508
|
+
category: 'callback',
|
|
509
|
+
});
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
if (!_providedOnClose) {
|
|
513
|
+
return;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
_providedOnClose();
|
|
517
|
+
},
|
|
518
|
+
[_providedOnClose]
|
|
519
|
+
);
|
|
520
|
+
//#endregion
|
|
521
|
+
|
|
522
|
+
//#region animation
|
|
523
|
+
const stopAnimation = useCallback(() => {
|
|
524
|
+
'worklet';
|
|
525
|
+
cancelAnimation(animatedPosition);
|
|
526
|
+
animatedAnimationState.set({
|
|
527
|
+
status: ANIMATION_STATUS.STOPPED,
|
|
528
|
+
source: ANIMATION_SOURCE.NONE,
|
|
529
|
+
});
|
|
530
|
+
}, [animatedPosition, animatedAnimationState]);
|
|
531
|
+
const animateToPositionCompleted = useCallback(
|
|
532
|
+
function animateToPositionCompleted(isFinished?: boolean) {
|
|
533
|
+
'worklet';
|
|
534
|
+
if (!isFinished) {
|
|
535
|
+
return;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
const { nextIndex, nextPosition } = animatedAnimationState.get();
|
|
539
|
+
|
|
540
|
+
if (__DEV__) {
|
|
541
|
+
runOnJS(print)({
|
|
542
|
+
component: 'BottomSheet',
|
|
543
|
+
method: 'animateToPositionCompleted',
|
|
544
|
+
params: {
|
|
545
|
+
currentIndex: animatedCurrentIndex.value,
|
|
546
|
+
nextIndex,
|
|
547
|
+
nextPosition,
|
|
548
|
+
},
|
|
549
|
+
});
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
if (nextIndex === undefined || nextPosition === undefined) {
|
|
553
|
+
return;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
// callbacks
|
|
557
|
+
if (nextIndex !== animatedCurrentIndex.get()) {
|
|
558
|
+
runOnJS(handleOnChange)(nextIndex, nextPosition);
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
if (nextIndex === -1) {
|
|
562
|
+
runOnJS(handleOnClose)();
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
animatedCurrentIndex.set(nextIndex);
|
|
566
|
+
|
|
567
|
+
// reset values
|
|
568
|
+
animatedContainerHeightDidChange.set(false);
|
|
569
|
+
isAnimatedOnMount.set(true);
|
|
570
|
+
animatedAnimationState.set({
|
|
571
|
+
status: ANIMATION_STATUS.STOPPED,
|
|
572
|
+
source: ANIMATION_SOURCE.NONE,
|
|
573
|
+
nextIndex: undefined,
|
|
574
|
+
nextPosition: undefined,
|
|
575
|
+
isForcedClosing: undefined,
|
|
576
|
+
});
|
|
577
|
+
},
|
|
578
|
+
[
|
|
579
|
+
handleOnChange,
|
|
580
|
+
handleOnClose,
|
|
581
|
+
animatedCurrentIndex,
|
|
582
|
+
animatedAnimationState,
|
|
583
|
+
animatedContainerHeightDidChange,
|
|
584
|
+
isAnimatedOnMount,
|
|
585
|
+
]
|
|
586
|
+
);
|
|
587
|
+
const animateToPosition: AnimateToPositionType = useCallback(
|
|
588
|
+
function animateToPosition(
|
|
589
|
+
position: number,
|
|
590
|
+
source: ANIMATION_SOURCE,
|
|
591
|
+
velocity = 0,
|
|
592
|
+
configs?: WithTimingConfig | WithSpringConfig
|
|
593
|
+
) {
|
|
594
|
+
'worklet';
|
|
595
|
+
if (__DEV__) {
|
|
596
|
+
runOnJS(print)({
|
|
597
|
+
component: 'BottomSheet',
|
|
598
|
+
method: 'animateToPosition',
|
|
599
|
+
params: {
|
|
600
|
+
currentPosition: animatedPosition.value,
|
|
601
|
+
nextPosition: position,
|
|
602
|
+
source,
|
|
603
|
+
},
|
|
604
|
+
});
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
if (position === undefined) {
|
|
608
|
+
return;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
if (position === animatedPosition.get()) {
|
|
612
|
+
return;
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
// early exit if there is a running animation to
|
|
616
|
+
// the same position
|
|
617
|
+
const { status: animationStatus, nextPosition } =
|
|
618
|
+
animatedAnimationState.get();
|
|
619
|
+
if (
|
|
620
|
+
animationStatus === ANIMATION_STATUS.RUNNING &&
|
|
621
|
+
position === nextPosition
|
|
622
|
+
) {
|
|
623
|
+
return;
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
// stop animation if it is running
|
|
627
|
+
if (animationStatus === ANIMATION_STATUS.RUNNING) {
|
|
628
|
+
stopAnimation();
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
/**
|
|
632
|
+
* offset the position if keyboard is shown and behavior not extend.
|
|
633
|
+
*/
|
|
634
|
+
let offset = 0;
|
|
635
|
+
|
|
636
|
+
const { status, heightWithinContainer } = animatedKeyboardState.get();
|
|
637
|
+
const sheetState = animatedSheetState.get();
|
|
638
|
+
if (
|
|
639
|
+
status === KEYBOARD_STATUS.SHOWN &&
|
|
640
|
+
keyboardBehavior !== KEYBOARD_BEHAVIOR.extend &&
|
|
641
|
+
([
|
|
642
|
+
ANIMATION_SOURCE.KEYBOARD,
|
|
643
|
+
ANIMATION_SOURCE.SNAP_POINT_CHANGE,
|
|
644
|
+
].includes(source) ||
|
|
645
|
+
sheetState === SHEET_STATE.OVER_EXTENDED)
|
|
646
|
+
) {
|
|
647
|
+
offset = heightWithinContainer;
|
|
648
|
+
}
|
|
649
|
+
const { detents, closedDetentPosition, highestDetentPosition } =
|
|
650
|
+
animatedDetentsState.get();
|
|
651
|
+
let index = detents?.indexOf(position + offset) ?? -1;
|
|
652
|
+
|
|
653
|
+
/**
|
|
654
|
+
* because keyboard position is not part of the detents array,
|
|
655
|
+
* we will need to manually set the index to the highest detent index.
|
|
656
|
+
*/
|
|
657
|
+
if (
|
|
658
|
+
index === -1 &&
|
|
659
|
+
status === KEYBOARD_STATUS.SHOWN &&
|
|
660
|
+
position !== closedDetentPosition
|
|
661
|
+
) {
|
|
662
|
+
index = highestDetentPosition ?? DEFAULT_KEYBOARD_INDEX;
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
/**
|
|
666
|
+
* set the animation state
|
|
667
|
+
*/
|
|
668
|
+
animatedAnimationState.set(state => {
|
|
669
|
+
'worklet';
|
|
670
|
+
return {
|
|
671
|
+
...state,
|
|
672
|
+
status: ANIMATION_STATUS.RUNNING,
|
|
673
|
+
source,
|
|
674
|
+
nextIndex: index,
|
|
675
|
+
nextPosition: position,
|
|
676
|
+
};
|
|
677
|
+
});
|
|
678
|
+
|
|
679
|
+
/**
|
|
680
|
+
* fire `onAnimate` callback
|
|
681
|
+
*/
|
|
682
|
+
runOnJS(handleOnAnimate)(index, position);
|
|
683
|
+
|
|
684
|
+
/**
|
|
685
|
+
* start animation
|
|
686
|
+
*/
|
|
687
|
+
animatedPosition.value = animate({
|
|
688
|
+
point: position,
|
|
689
|
+
configs: configs || _providedAnimationConfigs,
|
|
690
|
+
velocity,
|
|
691
|
+
overrideReduceMotion: _providedOverrideReduceMotion,
|
|
692
|
+
onComplete: animateToPositionCompleted,
|
|
693
|
+
});
|
|
694
|
+
},
|
|
695
|
+
[
|
|
696
|
+
handleOnAnimate,
|
|
697
|
+
stopAnimation,
|
|
698
|
+
animateToPositionCompleted,
|
|
699
|
+
keyboardBehavior,
|
|
700
|
+
_providedAnimationConfigs,
|
|
701
|
+
_providedOverrideReduceMotion,
|
|
702
|
+
animatedDetentsState,
|
|
703
|
+
animatedAnimationState,
|
|
704
|
+
animatedKeyboardState,
|
|
705
|
+
animatedPosition,
|
|
706
|
+
animatedSheetState,
|
|
707
|
+
]
|
|
708
|
+
);
|
|
709
|
+
/**
|
|
710
|
+
* Set to position without animation.
|
|
711
|
+
*
|
|
712
|
+
* @param targetPosition position to be set.
|
|
713
|
+
*/
|
|
714
|
+
const setToPosition = useCallback(
|
|
715
|
+
function setToPosition(targetPosition: number) {
|
|
716
|
+
'worklet';
|
|
717
|
+
if (targetPosition === undefined || targetPosition !== targetPosition) {
|
|
718
|
+
return;
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
if (
|
|
722
|
+
targetPosition === animatedPosition.get() &&
|
|
723
|
+
!animatedContainerHeightDidChange.value
|
|
724
|
+
) {
|
|
725
|
+
return;
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
const { status: animationStatus, nextPosition } =
|
|
729
|
+
animatedAnimationState.get();
|
|
730
|
+
|
|
731
|
+
// early exit if there is a running animation to
|
|
732
|
+
// the same position
|
|
733
|
+
if (
|
|
734
|
+
animationStatus === ANIMATION_STATUS.RUNNING &&
|
|
735
|
+
targetPosition === nextPosition
|
|
736
|
+
) {
|
|
737
|
+
return;
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
if (__DEV__) {
|
|
741
|
+
runOnJS(print)({
|
|
742
|
+
component: 'BottomSheet',
|
|
743
|
+
method: 'setToPosition',
|
|
744
|
+
params: {
|
|
745
|
+
currentPosition: animatedPosition.value,
|
|
746
|
+
targetPosition,
|
|
747
|
+
},
|
|
748
|
+
});
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
/**
|
|
752
|
+
* store next position
|
|
753
|
+
*/
|
|
754
|
+
const { detents } = animatedDetentsState.get();
|
|
755
|
+
const index = detents?.indexOf(targetPosition) ?? -1;
|
|
756
|
+
animatedAnimationState.set(state => {
|
|
757
|
+
'worklet';
|
|
758
|
+
return {
|
|
759
|
+
...state,
|
|
760
|
+
nextPosition: targetPosition,
|
|
761
|
+
nextIndex: index,
|
|
762
|
+
};
|
|
763
|
+
});
|
|
764
|
+
|
|
765
|
+
stopAnimation();
|
|
766
|
+
|
|
767
|
+
// set values
|
|
768
|
+
animatedPosition.value = targetPosition;
|
|
769
|
+
animatedContainerHeightDidChange.value = false;
|
|
770
|
+
},
|
|
771
|
+
[
|
|
772
|
+
stopAnimation,
|
|
773
|
+
animatedPosition,
|
|
774
|
+
animatedContainerHeightDidChange,
|
|
775
|
+
animatedAnimationState,
|
|
776
|
+
animatedDetentsState,
|
|
777
|
+
]
|
|
778
|
+
);
|
|
779
|
+
//#endregion
|
|
780
|
+
|
|
781
|
+
//#region private methods
|
|
782
|
+
/**
|
|
783
|
+
* Calculate and evaluate the current position based on multiple
|
|
784
|
+
* local states.
|
|
785
|
+
*/
|
|
786
|
+
const getEvaluatedPosition = useCallback(
|
|
787
|
+
function getEvaluatedPosition(source: ANIMATION_SOURCE) {
|
|
788
|
+
'worklet';
|
|
789
|
+
const currentIndex = animatedCurrentIndex.value;
|
|
790
|
+
const { detents, highestDetentPosition, closedDetentPosition } =
|
|
791
|
+
animatedDetentsState.get();
|
|
792
|
+
const keyboardStatus = animatedKeyboardState.get().status;
|
|
793
|
+
|
|
794
|
+
if (
|
|
795
|
+
detents === undefined ||
|
|
796
|
+
highestDetentPosition === undefined ||
|
|
797
|
+
closedDetentPosition === undefined
|
|
798
|
+
) {
|
|
799
|
+
return;
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
/**
|
|
803
|
+
* if the keyboard blur behavior is restore and keyboard is hidden,
|
|
804
|
+
* then we return the previous snap point.
|
|
805
|
+
*/
|
|
806
|
+
if (
|
|
807
|
+
source === ANIMATION_SOURCE.KEYBOARD &&
|
|
808
|
+
keyboardBlurBehavior === KEYBOARD_BLUR_BEHAVIOR.restore &&
|
|
809
|
+
keyboardStatus === KEYBOARD_STATUS.HIDDEN &&
|
|
810
|
+
animatedContentGestureState.value !== State.ACTIVE &&
|
|
811
|
+
animatedHandleGestureState.value !== State.ACTIVE
|
|
812
|
+
) {
|
|
813
|
+
isInTemporaryPosition.value = false;
|
|
814
|
+
const nextPosition = detents[currentIndex];
|
|
815
|
+
return nextPosition;
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
/**
|
|
819
|
+
* if the keyboard appearance behavior is extend and keyboard is shown,
|
|
820
|
+
* then we return the heights snap point.
|
|
821
|
+
*/
|
|
822
|
+
if (
|
|
823
|
+
keyboardBehavior === KEYBOARD_BEHAVIOR.extend &&
|
|
824
|
+
keyboardStatus === KEYBOARD_STATUS.SHOWN
|
|
825
|
+
) {
|
|
826
|
+
return highestDetentPosition;
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
/**
|
|
830
|
+
* if the keyboard appearance behavior is fill parent and keyboard is shown,
|
|
831
|
+
* then we return 0 ( full screen ).
|
|
832
|
+
*/
|
|
833
|
+
if (
|
|
834
|
+
keyboardBehavior === KEYBOARD_BEHAVIOR.fillParent &&
|
|
835
|
+
keyboardStatus === KEYBOARD_STATUS.SHOWN
|
|
836
|
+
) {
|
|
837
|
+
isInTemporaryPosition.value = true;
|
|
838
|
+
return 0;
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
/**
|
|
842
|
+
* if the keyboard appearance behavior is interactive and keyboard is shown,
|
|
843
|
+
* then we return the heights points minus the keyboard in container height.
|
|
844
|
+
*/
|
|
845
|
+
if (
|
|
846
|
+
keyboardBehavior === KEYBOARD_BEHAVIOR.interactive &&
|
|
847
|
+
keyboardStatus === KEYBOARD_STATUS.SHOWN &&
|
|
848
|
+
// ensure that this logic does not run on android
|
|
849
|
+
// with resize input mode
|
|
850
|
+
!(
|
|
851
|
+
Platform.OS === 'android' &&
|
|
852
|
+
android_keyboardInputMode === 'adjustResize'
|
|
853
|
+
)
|
|
854
|
+
) {
|
|
855
|
+
isInTemporaryPosition.value = true;
|
|
856
|
+
const keyboardHeightInContainer =
|
|
857
|
+
animatedKeyboardState.get().heightWithinContainer;
|
|
858
|
+
return Math.max(0, highestDetentPosition - keyboardHeightInContainer);
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
/**
|
|
862
|
+
* if the bottom sheet is in temporary position, then we return
|
|
863
|
+
* the current position.
|
|
864
|
+
*/
|
|
865
|
+
if (isInTemporaryPosition.value) {
|
|
866
|
+
return animatedPosition.value;
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
/**
|
|
870
|
+
* if the bottom sheet did not animate on mount,
|
|
871
|
+
* then we return the provided index or the closed position.
|
|
872
|
+
*/
|
|
873
|
+
if (!isAnimatedOnMount.value) {
|
|
874
|
+
return _providedIndex === -1
|
|
875
|
+
? closedDetentPosition
|
|
876
|
+
: detents[_providedIndex];
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
const { status, nextIndex, nextPosition } =
|
|
880
|
+
animatedAnimationState.get();
|
|
881
|
+
|
|
882
|
+
/**
|
|
883
|
+
* if the evaluated position is for a snap change source while the sheet is currently running
|
|
884
|
+
* an animation and the next position is different than the detent at next index,
|
|
885
|
+
* then we return the detent at next index.
|
|
886
|
+
*
|
|
887
|
+
* https://github.com/gorhom/react-native-bottom-sheet/issues/2431
|
|
888
|
+
*/
|
|
889
|
+
if (
|
|
890
|
+
source === ANIMATION_SOURCE.SNAP_POINT_CHANGE &&
|
|
891
|
+
status === ANIMATION_STATUS.RUNNING &&
|
|
892
|
+
nextIndex !== undefined &&
|
|
893
|
+
nextPosition !== undefined &&
|
|
894
|
+
detents[nextIndex] !== nextPosition
|
|
895
|
+
) {
|
|
896
|
+
return detents[nextIndex];
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
/**
|
|
900
|
+
* return the current index position.
|
|
901
|
+
*/
|
|
902
|
+
return detents[currentIndex];
|
|
903
|
+
},
|
|
904
|
+
[
|
|
905
|
+
animatedContentGestureState,
|
|
906
|
+
animatedCurrentIndex,
|
|
907
|
+
animatedHandleGestureState,
|
|
908
|
+
animatedAnimationState,
|
|
909
|
+
animatedKeyboardState,
|
|
910
|
+
animatedPosition,
|
|
911
|
+
animatedDetentsState,
|
|
912
|
+
isInTemporaryPosition,
|
|
913
|
+
isAnimatedOnMount,
|
|
914
|
+
keyboardBehavior,
|
|
915
|
+
keyboardBlurBehavior,
|
|
916
|
+
_providedIndex,
|
|
917
|
+
android_keyboardInputMode,
|
|
918
|
+
]
|
|
919
|
+
);
|
|
920
|
+
|
|
921
|
+
/**
|
|
922
|
+
* Evaluate the bottom sheet position based based on a event source and other local states.
|
|
923
|
+
*/
|
|
924
|
+
const evaluatePosition = useCallback(
|
|
925
|
+
function evaluatePosition(
|
|
926
|
+
source: ANIMATION_SOURCE,
|
|
927
|
+
animationConfigs?: WithSpringConfig | WithTimingConfig
|
|
928
|
+
) {
|
|
929
|
+
'worklet';
|
|
930
|
+
const {
|
|
931
|
+
status: animationStatus,
|
|
932
|
+
nextIndex,
|
|
933
|
+
isForcedClosing,
|
|
934
|
+
} = animatedAnimationState.get();
|
|
935
|
+
|
|
936
|
+
const { detents, closedDetentPosition } = animatedDetentsState.get();
|
|
937
|
+
if (
|
|
938
|
+
detents === undefined ||
|
|
939
|
+
detents.length === 0 ||
|
|
940
|
+
closedDetentPosition === undefined
|
|
941
|
+
) {
|
|
942
|
+
return;
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
/**
|
|
946
|
+
* if a force closing is running and source not from user, then we early exit
|
|
947
|
+
*/
|
|
948
|
+
if (isForcedClosing && source !== ANIMATION_SOURCE.USER) {
|
|
949
|
+
return;
|
|
950
|
+
}
|
|
951
|
+
/**
|
|
952
|
+
* when evaluating the position while layout is not calculated, then we early exit till it is.
|
|
953
|
+
*/
|
|
954
|
+
if (!isLayoutCalculated.value) {
|
|
955
|
+
return;
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
const proposedPosition = getEvaluatedPosition(source);
|
|
959
|
+
if (proposedPosition === undefined) {
|
|
960
|
+
return;
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
/**
|
|
964
|
+
* when evaluating the position while the mount animation not been handled,
|
|
965
|
+
* then we evaluate on mount use cases.
|
|
966
|
+
*/
|
|
967
|
+
if (!isAnimatedOnMount.value) {
|
|
968
|
+
/**
|
|
969
|
+
* if animate on mount is set to true, then we animate to the propose position,
|
|
970
|
+
* else, we set the position with out animation.
|
|
971
|
+
*/
|
|
972
|
+
if (animateOnMount) {
|
|
973
|
+
animateToPosition(
|
|
974
|
+
proposedPosition,
|
|
975
|
+
ANIMATION_SOURCE.MOUNT,
|
|
976
|
+
undefined,
|
|
977
|
+
animationConfigs
|
|
978
|
+
);
|
|
979
|
+
} else {
|
|
980
|
+
setToPosition(proposedPosition);
|
|
981
|
+
isAnimatedOnMount.value = true;
|
|
982
|
+
}
|
|
983
|
+
return;
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
/**
|
|
987
|
+
* when evaluating the position while the bottom sheet is animating.
|
|
988
|
+
*/
|
|
989
|
+
if (animationStatus === ANIMATION_STATUS.RUNNING) {
|
|
990
|
+
const nextPositionIndex = nextIndex ?? INITIAL_VALUE;
|
|
991
|
+
/**
|
|
992
|
+
* when evaluating the position while the bottom sheet is
|
|
993
|
+
* closing, then we force closing the bottom sheet with no animation.
|
|
994
|
+
*/
|
|
995
|
+
if (nextPositionIndex === -1 && !isInTemporaryPosition.value) {
|
|
996
|
+
setToPosition(closedDetentPosition);
|
|
997
|
+
return;
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
/**
|
|
1001
|
+
* when evaluating the position while it's animating to
|
|
1002
|
+
* a position other than the current position, then we
|
|
1003
|
+
* restart the animation.
|
|
1004
|
+
*/
|
|
1005
|
+
if (nextPositionIndex !== animatedCurrentIndex.value) {
|
|
1006
|
+
animateToPosition(
|
|
1007
|
+
detents[nextPositionIndex],
|
|
1008
|
+
source,
|
|
1009
|
+
undefined,
|
|
1010
|
+
animationConfigs
|
|
1011
|
+
);
|
|
1012
|
+
return;
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
/**
|
|
1017
|
+
* when evaluating the position while the bottom sheet is in closed
|
|
1018
|
+
* position and not animating, we re-set the position to closed position.
|
|
1019
|
+
*/
|
|
1020
|
+
if (
|
|
1021
|
+
animationStatus !== ANIMATION_STATUS.RUNNING &&
|
|
1022
|
+
animatedCurrentIndex.value === -1
|
|
1023
|
+
) {
|
|
1024
|
+
/**
|
|
1025
|
+
* early exit if reduce motion is enabled and index is out of sync with position.
|
|
1026
|
+
*/
|
|
1027
|
+
if (
|
|
1028
|
+
reduceMotion &&
|
|
1029
|
+
detents[animatedIndex.value] !== animatedPosition.value
|
|
1030
|
+
) {
|
|
1031
|
+
return;
|
|
1032
|
+
}
|
|
1033
|
+
setToPosition(closedDetentPosition);
|
|
1034
|
+
return;
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
/**
|
|
1038
|
+
* when evaluating the position after the container resize, then we
|
|
1039
|
+
* force the bottom sheet to the proposed position with no
|
|
1040
|
+
* animation.
|
|
1041
|
+
*/
|
|
1042
|
+
if (animatedContainerHeightDidChange.value) {
|
|
1043
|
+
setToPosition(proposedPosition);
|
|
1044
|
+
return;
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
/**
|
|
1048
|
+
* we fall back to the proposed position.
|
|
1049
|
+
*/
|
|
1050
|
+
animateToPosition(
|
|
1051
|
+
proposedPosition,
|
|
1052
|
+
source,
|
|
1053
|
+
undefined,
|
|
1054
|
+
animationConfigs
|
|
1055
|
+
);
|
|
1056
|
+
},
|
|
1057
|
+
[
|
|
1058
|
+
getEvaluatedPosition,
|
|
1059
|
+
animateToPosition,
|
|
1060
|
+
setToPosition,
|
|
1061
|
+
reduceMotion,
|
|
1062
|
+
animateOnMount,
|
|
1063
|
+
animatedAnimationState,
|
|
1064
|
+
animatedContainerHeightDidChange,
|
|
1065
|
+
animatedCurrentIndex,
|
|
1066
|
+
animatedIndex,
|
|
1067
|
+
animatedPosition,
|
|
1068
|
+
animatedDetentsState,
|
|
1069
|
+
isAnimatedOnMount,
|
|
1070
|
+
isInTemporaryPosition,
|
|
1071
|
+
isLayoutCalculated,
|
|
1072
|
+
]
|
|
1073
|
+
);
|
|
1074
|
+
//#endregion
|
|
1075
|
+
|
|
1076
|
+
//#region public methods
|
|
1077
|
+
const handleSnapToIndex = useStableCallback(function handleSnapToIndex(
|
|
1078
|
+
index: number,
|
|
1079
|
+
animationConfigs?: WithSpringConfig | WithTimingConfig
|
|
1080
|
+
) {
|
|
1081
|
+
const { detents } = animatedDetentsState.get();
|
|
1082
|
+
const isLayoutReady = isLayoutCalculated.get();
|
|
1083
|
+
|
|
1084
|
+
if (detents === undefined || detents.length === 0) {
|
|
1085
|
+
return;
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
// early exit if layout is not ready yet.
|
|
1089
|
+
if (!isLayoutReady) {
|
|
1090
|
+
return;
|
|
1091
|
+
}
|
|
1092
|
+
|
|
1093
|
+
invariant(
|
|
1094
|
+
index >= -1 && index <= detents.length - 1,
|
|
1095
|
+
`'index' was provided but out of the provided snap points range! expected value to be between -1, ${
|
|
1096
|
+
detents.length - 1
|
|
1097
|
+
}`
|
|
1098
|
+
);
|
|
1099
|
+
|
|
1100
|
+
if (__DEV__) {
|
|
1101
|
+
print({
|
|
1102
|
+
component: 'BottomSheet',
|
|
1103
|
+
method: 'handleSnapToIndex',
|
|
1104
|
+
params: {
|
|
1105
|
+
index,
|
|
1106
|
+
},
|
|
1107
|
+
});
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
const targetPosition = detents[index];
|
|
1111
|
+
|
|
1112
|
+
/**
|
|
1113
|
+
* exit method if :
|
|
1114
|
+
* - layout is not calculated.
|
|
1115
|
+
* - already animating to next position.
|
|
1116
|
+
* - sheet is forced closing.
|
|
1117
|
+
*/
|
|
1118
|
+
const { nextPosition, nextIndex, isForcedClosing } =
|
|
1119
|
+
animatedAnimationState.get();
|
|
1120
|
+
if (
|
|
1121
|
+
!isLayoutCalculated.value ||
|
|
1122
|
+
index === nextIndex ||
|
|
1123
|
+
targetPosition === nextPosition ||
|
|
1124
|
+
isForcedClosing
|
|
1125
|
+
) {
|
|
1126
|
+
return;
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1129
|
+
/**
|
|
1130
|
+
* reset temporary position boolean.
|
|
1131
|
+
*/
|
|
1132
|
+
isInTemporaryPosition.value = false;
|
|
1133
|
+
|
|
1134
|
+
runOnUI(animateToPosition)(
|
|
1135
|
+
targetPosition,
|
|
1136
|
+
ANIMATION_SOURCE.USER,
|
|
1137
|
+
0,
|
|
1138
|
+
animationConfigs
|
|
1139
|
+
);
|
|
1140
|
+
});
|
|
1141
|
+
const handleSnapToPosition = useCallback(
|
|
1142
|
+
function handleSnapToPosition(
|
|
1143
|
+
position: number | string,
|
|
1144
|
+
animationConfigs?: WithSpringConfig | WithTimingConfig
|
|
1145
|
+
) {
|
|
1146
|
+
'worklet';
|
|
1147
|
+
if (__DEV__) {
|
|
1148
|
+
print({
|
|
1149
|
+
component: 'BottomSheet',
|
|
1150
|
+
method: 'handleSnapToPosition',
|
|
1151
|
+
params: {
|
|
1152
|
+
position,
|
|
1153
|
+
},
|
|
1154
|
+
});
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1157
|
+
const { containerHeight } = animatedLayoutState.get();
|
|
1158
|
+
/**
|
|
1159
|
+
* normalized provided position.
|
|
1160
|
+
*/
|
|
1161
|
+
const targetPosition = normalizeSnapPoint(position, containerHeight);
|
|
1162
|
+
|
|
1163
|
+
/**
|
|
1164
|
+
* exit method if :
|
|
1165
|
+
* - layout is not calculated.
|
|
1166
|
+
* - already animating to next position.
|
|
1167
|
+
* - sheet is forced closing.
|
|
1168
|
+
*/
|
|
1169
|
+
const { nextPosition, isForcedClosing } = animatedAnimationState.get();
|
|
1170
|
+
if (
|
|
1171
|
+
!isLayoutCalculated ||
|
|
1172
|
+
targetPosition === nextPosition ||
|
|
1173
|
+
isForcedClosing
|
|
1174
|
+
) {
|
|
1175
|
+
return;
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1178
|
+
/**
|
|
1179
|
+
* mark the new position as temporary.
|
|
1180
|
+
*/
|
|
1181
|
+
isInTemporaryPosition.value = true;
|
|
1182
|
+
|
|
1183
|
+
runOnUI(animateToPosition)(
|
|
1184
|
+
targetPosition,
|
|
1185
|
+
ANIMATION_SOURCE.USER,
|
|
1186
|
+
0,
|
|
1187
|
+
animationConfigs
|
|
1188
|
+
);
|
|
1189
|
+
},
|
|
1190
|
+
[
|
|
1191
|
+
animateToPosition,
|
|
1192
|
+
isInTemporaryPosition,
|
|
1193
|
+
isLayoutCalculated,
|
|
1194
|
+
animatedLayoutState,
|
|
1195
|
+
animatedAnimationState,
|
|
1196
|
+
]
|
|
1197
|
+
);
|
|
1198
|
+
const handleClose = useCallback(
|
|
1199
|
+
function handleClose(
|
|
1200
|
+
animationConfigs?: WithSpringConfig | WithTimingConfig
|
|
1201
|
+
) {
|
|
1202
|
+
if (__DEV__) {
|
|
1203
|
+
print({
|
|
1204
|
+
component: 'BottomSheet',
|
|
1205
|
+
method: 'handleClose',
|
|
1206
|
+
});
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1209
|
+
const closedDetentPosition =
|
|
1210
|
+
animatedDetentsState.get().closedDetentPosition;
|
|
1211
|
+
if (closedDetentPosition === undefined) {
|
|
1212
|
+
return;
|
|
1213
|
+
}
|
|
1214
|
+
|
|
1215
|
+
const targetPosition = closedDetentPosition;
|
|
1216
|
+
|
|
1217
|
+
/**
|
|
1218
|
+
* exit method if :
|
|
1219
|
+
* - layout is not calculated.
|
|
1220
|
+
* - already animating to next position.
|
|
1221
|
+
* - sheet is forced closing.
|
|
1222
|
+
*/
|
|
1223
|
+
const { nextPosition, isForcedClosing } = animatedAnimationState.get();
|
|
1224
|
+
if (
|
|
1225
|
+
!isLayoutCalculated.value ||
|
|
1226
|
+
targetPosition === nextPosition ||
|
|
1227
|
+
isForcedClosing
|
|
1228
|
+
) {
|
|
1229
|
+
return;
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
/**
|
|
1233
|
+
* reset temporary position variable.
|
|
1234
|
+
*/
|
|
1235
|
+
isInTemporaryPosition.value = false;
|
|
1236
|
+
|
|
1237
|
+
runOnUI(animateToPosition)(
|
|
1238
|
+
targetPosition,
|
|
1239
|
+
ANIMATION_SOURCE.USER,
|
|
1240
|
+
0,
|
|
1241
|
+
animationConfigs
|
|
1242
|
+
);
|
|
1243
|
+
},
|
|
1244
|
+
[
|
|
1245
|
+
animateToPosition,
|
|
1246
|
+
isLayoutCalculated,
|
|
1247
|
+
isInTemporaryPosition,
|
|
1248
|
+
animatedDetentsState,
|
|
1249
|
+
animatedAnimationState,
|
|
1250
|
+
]
|
|
1251
|
+
);
|
|
1252
|
+
const handleForceClose = useCallback(
|
|
1253
|
+
function handleForceClose(
|
|
1254
|
+
animationConfigs?: WithSpringConfig | WithTimingConfig
|
|
1255
|
+
) {
|
|
1256
|
+
if (__DEV__) {
|
|
1257
|
+
print({
|
|
1258
|
+
component: 'BottomSheet',
|
|
1259
|
+
method: 'handleForceClose',
|
|
1260
|
+
});
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1263
|
+
const closedDetentPosition =
|
|
1264
|
+
animatedDetentsState.get().closedDetentPosition;
|
|
1265
|
+
if (closedDetentPosition === undefined) {
|
|
1266
|
+
return;
|
|
1267
|
+
}
|
|
1268
|
+
|
|
1269
|
+
const targetPosition = closedDetentPosition;
|
|
1270
|
+
|
|
1271
|
+
/**
|
|
1272
|
+
* exit method if :
|
|
1273
|
+
* - already animating to next position.
|
|
1274
|
+
* - sheet is forced closing.
|
|
1275
|
+
*/
|
|
1276
|
+
const { nextPosition, isForcedClosing } = animatedAnimationState.get();
|
|
1277
|
+
if (targetPosition === nextPosition || isForcedClosing) {
|
|
1278
|
+
return;
|
|
1279
|
+
}
|
|
1280
|
+
|
|
1281
|
+
/**
|
|
1282
|
+
* reset temporary position variable.
|
|
1283
|
+
*/
|
|
1284
|
+
isInTemporaryPosition.value = false;
|
|
1285
|
+
|
|
1286
|
+
/**
|
|
1287
|
+
* set force closing variable.
|
|
1288
|
+
*/
|
|
1289
|
+
animatedAnimationState.set(state => {
|
|
1290
|
+
'worklet';
|
|
1291
|
+
return {
|
|
1292
|
+
...state,
|
|
1293
|
+
isForcedClosing: true,
|
|
1294
|
+
};
|
|
1295
|
+
});
|
|
1296
|
+
|
|
1297
|
+
runOnUI(animateToPosition)(
|
|
1298
|
+
targetPosition,
|
|
1299
|
+
ANIMATION_SOURCE.USER,
|
|
1300
|
+
0,
|
|
1301
|
+
animationConfigs
|
|
1302
|
+
);
|
|
1303
|
+
},
|
|
1304
|
+
[
|
|
1305
|
+
animateToPosition,
|
|
1306
|
+
isInTemporaryPosition,
|
|
1307
|
+
animatedDetentsState,
|
|
1308
|
+
animatedAnimationState,
|
|
1309
|
+
]
|
|
1310
|
+
);
|
|
1311
|
+
const handleExpand = useCallback(
|
|
1312
|
+
function handleExpand(
|
|
1313
|
+
animationConfigs?: WithSpringConfig | WithTimingConfig
|
|
1314
|
+
) {
|
|
1315
|
+
if (__DEV__) {
|
|
1316
|
+
print({
|
|
1317
|
+
component: 'BottomSheet',
|
|
1318
|
+
method: 'handleExpand',
|
|
1319
|
+
});
|
|
1320
|
+
}
|
|
1321
|
+
|
|
1322
|
+
const { detents } = animatedDetentsState.get();
|
|
1323
|
+
if (detents === undefined || detents.length === 0) {
|
|
1324
|
+
return;
|
|
1325
|
+
}
|
|
1326
|
+
|
|
1327
|
+
const targetIndex = detents.length - 1;
|
|
1328
|
+
const targetPosition = detents[targetIndex];
|
|
1329
|
+
|
|
1330
|
+
/**
|
|
1331
|
+
* exit method if :
|
|
1332
|
+
* - layout is not calculated.
|
|
1333
|
+
* - already animating to next position.
|
|
1334
|
+
* - sheet is forced closing.
|
|
1335
|
+
*/
|
|
1336
|
+
const { nextPosition, nextIndex, isForcedClosing } =
|
|
1337
|
+
animatedAnimationState.get();
|
|
1338
|
+
if (
|
|
1339
|
+
!isLayoutCalculated.value ||
|
|
1340
|
+
targetIndex === nextIndex ||
|
|
1341
|
+
targetPosition === nextPosition ||
|
|
1342
|
+
isForcedClosing
|
|
1343
|
+
) {
|
|
1344
|
+
return;
|
|
1345
|
+
}
|
|
1346
|
+
|
|
1347
|
+
/**
|
|
1348
|
+
* reset temporary position boolean.
|
|
1349
|
+
*/
|
|
1350
|
+
isInTemporaryPosition.value = false;
|
|
1351
|
+
|
|
1352
|
+
runOnUI(animateToPosition)(
|
|
1353
|
+
targetPosition,
|
|
1354
|
+
ANIMATION_SOURCE.USER,
|
|
1355
|
+
0,
|
|
1356
|
+
animationConfigs
|
|
1357
|
+
);
|
|
1358
|
+
},
|
|
1359
|
+
[
|
|
1360
|
+
animateToPosition,
|
|
1361
|
+
isInTemporaryPosition,
|
|
1362
|
+
isLayoutCalculated,
|
|
1363
|
+
animatedDetentsState,
|
|
1364
|
+
animatedAnimationState,
|
|
1365
|
+
]
|
|
1366
|
+
);
|
|
1367
|
+
const handleCollapse = useCallback(
|
|
1368
|
+
function handleCollapse(
|
|
1369
|
+
animationConfigs?: WithSpringConfig | WithTimingConfig
|
|
1370
|
+
) {
|
|
1371
|
+
if (__DEV__) {
|
|
1372
|
+
print({
|
|
1373
|
+
component: 'BottomSheet',
|
|
1374
|
+
method: 'handleCollapse',
|
|
1375
|
+
});
|
|
1376
|
+
}
|
|
1377
|
+
|
|
1378
|
+
const { detents } = animatedDetentsState.get();
|
|
1379
|
+
if (detents === undefined || detents.length === 0) {
|
|
1380
|
+
return;
|
|
1381
|
+
}
|
|
1382
|
+
|
|
1383
|
+
const targetPosition = detents[0];
|
|
1384
|
+
|
|
1385
|
+
/**
|
|
1386
|
+
* exit method if :
|
|
1387
|
+
* - layout is not calculated.
|
|
1388
|
+
* - already animating to next position.
|
|
1389
|
+
* - sheet is forced closing.
|
|
1390
|
+
*/
|
|
1391
|
+
const { nextPosition, nextIndex, isForcedClosing } =
|
|
1392
|
+
animatedAnimationState.get();
|
|
1393
|
+
if (
|
|
1394
|
+
!isLayoutCalculated ||
|
|
1395
|
+
nextIndex === 0 ||
|
|
1396
|
+
targetPosition === nextPosition ||
|
|
1397
|
+
isForcedClosing
|
|
1398
|
+
) {
|
|
1399
|
+
return;
|
|
1400
|
+
}
|
|
1401
|
+
|
|
1402
|
+
/**
|
|
1403
|
+
* reset temporary position boolean.
|
|
1404
|
+
*/
|
|
1405
|
+
isInTemporaryPosition.value = false;
|
|
1406
|
+
|
|
1407
|
+
runOnUI(animateToPosition)(
|
|
1408
|
+
targetPosition,
|
|
1409
|
+
ANIMATION_SOURCE.USER,
|
|
1410
|
+
0,
|
|
1411
|
+
animationConfigs
|
|
1412
|
+
);
|
|
1413
|
+
},
|
|
1414
|
+
[
|
|
1415
|
+
animateToPosition,
|
|
1416
|
+
isLayoutCalculated,
|
|
1417
|
+
isInTemporaryPosition,
|
|
1418
|
+
animatedDetentsState,
|
|
1419
|
+
animatedAnimationState,
|
|
1420
|
+
]
|
|
1421
|
+
);
|
|
1422
|
+
|
|
1423
|
+
useImperativeHandle(ref, () => ({
|
|
1424
|
+
snapToIndex: handleSnapToIndex,
|
|
1425
|
+
snapToPosition: handleSnapToPosition,
|
|
1426
|
+
expand: handleExpand,
|
|
1427
|
+
collapse: handleCollapse,
|
|
1428
|
+
close: handleClose,
|
|
1429
|
+
forceClose: handleForceClose,
|
|
1430
|
+
}));
|
|
1431
|
+
//#endregion
|
|
1432
|
+
|
|
1433
|
+
//#region contexts variables
|
|
1434
|
+
const internalContextVariables = useMemo(
|
|
1435
|
+
() => ({
|
|
1436
|
+
textInputNodesRef,
|
|
1437
|
+
enableContentPanningGesture,
|
|
1438
|
+
enableDynamicSizing,
|
|
1439
|
+
overDragResistanceFactor,
|
|
1440
|
+
enableOverDrag,
|
|
1441
|
+
enablePanDownToClose,
|
|
1442
|
+
animatedAnimationState,
|
|
1443
|
+
animatedSheetState,
|
|
1444
|
+
animatedScrollableState,
|
|
1445
|
+
animatedScrollableStatus,
|
|
1446
|
+
animatedContentGestureState,
|
|
1447
|
+
animatedHandleGestureState,
|
|
1448
|
+
animatedKeyboardState,
|
|
1449
|
+
animatedLayoutState,
|
|
1450
|
+
animatedIndex,
|
|
1451
|
+
animatedPosition,
|
|
1452
|
+
animatedSheetHeight,
|
|
1453
|
+
animatedDetentsState,
|
|
1454
|
+
isInTemporaryPosition,
|
|
1455
|
+
simultaneousHandlers: _providedSimultaneousHandlers,
|
|
1456
|
+
waitFor: _providedWaitFor,
|
|
1457
|
+
activeOffsetX: _providedActiveOffsetX,
|
|
1458
|
+
activeOffsetY: _providedActiveOffsetY,
|
|
1459
|
+
failOffsetX: _providedFailOffsetX,
|
|
1460
|
+
failOffsetY: _providedFailOffsetY,
|
|
1461
|
+
enableBlurKeyboardOnGesture,
|
|
1462
|
+
animateToPosition,
|
|
1463
|
+
stopAnimation,
|
|
1464
|
+
setScrollableRef,
|
|
1465
|
+
removeScrollableRef,
|
|
1466
|
+
}),
|
|
1467
|
+
[
|
|
1468
|
+
textInputNodesRef,
|
|
1469
|
+
animatedIndex,
|
|
1470
|
+
animatedPosition,
|
|
1471
|
+
animatedSheetHeight,
|
|
1472
|
+
animatedLayoutState,
|
|
1473
|
+
animatedContentGestureState,
|
|
1474
|
+
animatedHandleGestureState,
|
|
1475
|
+
animatedAnimationState,
|
|
1476
|
+
animatedKeyboardState,
|
|
1477
|
+
animatedSheetState,
|
|
1478
|
+
animatedScrollableState,
|
|
1479
|
+
animatedScrollableStatus,
|
|
1480
|
+
animatedDetentsState,
|
|
1481
|
+
isInTemporaryPosition,
|
|
1482
|
+
enableContentPanningGesture,
|
|
1483
|
+
overDragResistanceFactor,
|
|
1484
|
+
enableOverDrag,
|
|
1485
|
+
enablePanDownToClose,
|
|
1486
|
+
enableDynamicSizing,
|
|
1487
|
+
enableBlurKeyboardOnGesture,
|
|
1488
|
+
_providedSimultaneousHandlers,
|
|
1489
|
+
_providedWaitFor,
|
|
1490
|
+
_providedActiveOffsetX,
|
|
1491
|
+
_providedActiveOffsetY,
|
|
1492
|
+
_providedFailOffsetX,
|
|
1493
|
+
_providedFailOffsetY,
|
|
1494
|
+
setScrollableRef,
|
|
1495
|
+
removeScrollableRef,
|
|
1496
|
+
animateToPosition,
|
|
1497
|
+
stopAnimation,
|
|
1498
|
+
]
|
|
1499
|
+
);
|
|
1500
|
+
const externalContextVariables = useMemo(
|
|
1501
|
+
() => ({
|
|
1502
|
+
animatedIndex,
|
|
1503
|
+
animatedPosition,
|
|
1504
|
+
snapToIndex: handleSnapToIndex,
|
|
1505
|
+
snapToPosition: handleSnapToPosition,
|
|
1506
|
+
expand: handleExpand,
|
|
1507
|
+
collapse: handleCollapse,
|
|
1508
|
+
close: handleClose,
|
|
1509
|
+
forceClose: handleForceClose,
|
|
1510
|
+
}),
|
|
1511
|
+
[
|
|
1512
|
+
animatedIndex,
|
|
1513
|
+
animatedPosition,
|
|
1514
|
+
handleSnapToIndex,
|
|
1515
|
+
handleSnapToPosition,
|
|
1516
|
+
handleExpand,
|
|
1517
|
+
handleCollapse,
|
|
1518
|
+
handleClose,
|
|
1519
|
+
handleForceClose,
|
|
1520
|
+
]
|
|
1521
|
+
);
|
|
1522
|
+
//#endregion
|
|
1523
|
+
|
|
1524
|
+
//#region effects
|
|
1525
|
+
useAnimatedReaction(
|
|
1526
|
+
() => animatedLayoutState.get().containerHeight,
|
|
1527
|
+
(result, previous) => {
|
|
1528
|
+
if (result === INITIAL_LAYOUT_VALUE) {
|
|
1529
|
+
return;
|
|
1530
|
+
}
|
|
1531
|
+
|
|
1532
|
+
animatedContainerHeightDidChange.value = result !== previous;
|
|
1533
|
+
|
|
1534
|
+
const { closedDetentPosition } = animatedDetentsState.get();
|
|
1535
|
+
if (closedDetentPosition === undefined) {
|
|
1536
|
+
return;
|
|
1537
|
+
}
|
|
1538
|
+
|
|
1539
|
+
/**
|
|
1540
|
+
* When user close the bottom sheet while the keyboard open on Android with
|
|
1541
|
+
* software keyboard layout mode set to resize, the close position would be
|
|
1542
|
+
* set to the container height - the keyboard height, and when the keyboard
|
|
1543
|
+
* closes, the container height and here we restart the animation again.
|
|
1544
|
+
*
|
|
1545
|
+
* [read more](https://github.com/gorhom/react-native-bottom-sheet/issues/2163)
|
|
1546
|
+
*/
|
|
1547
|
+
const {
|
|
1548
|
+
status: animationStatus,
|
|
1549
|
+
source: animationSource,
|
|
1550
|
+
nextIndex,
|
|
1551
|
+
} = animatedAnimationState.get();
|
|
1552
|
+
if (
|
|
1553
|
+
animationStatus === ANIMATION_STATUS.RUNNING &&
|
|
1554
|
+
animationSource === ANIMATION_SOURCE.GESTURE &&
|
|
1555
|
+
nextIndex === -1
|
|
1556
|
+
) {
|
|
1557
|
+
animateToPosition(closedDetentPosition, ANIMATION_SOURCE.GESTURE);
|
|
1558
|
+
}
|
|
1559
|
+
|
|
1560
|
+
/**
|
|
1561
|
+
* On Android with adjustResize, when the container grows back after
|
|
1562
|
+
* keyboard dismiss, force evaluate position to avoid race condition
|
|
1563
|
+
* between keyboardDidHide and container resize events.
|
|
1564
|
+
*/
|
|
1565
|
+
if (
|
|
1566
|
+
Platform.OS === 'android' &&
|
|
1567
|
+
android_keyboardInputMode === KEYBOARD_INPUT_MODE.adjustResize &&
|
|
1568
|
+
previous !== null &&
|
|
1569
|
+
previous !== INITIAL_LAYOUT_VALUE &&
|
|
1570
|
+
result > previous
|
|
1571
|
+
) {
|
|
1572
|
+
evaluatePosition(ANIMATION_SOURCE.CONTAINER_RESIZE);
|
|
1573
|
+
}
|
|
1574
|
+
},
|
|
1575
|
+
[
|
|
1576
|
+
animatedContainerHeightDidChange,
|
|
1577
|
+
animatedAnimationState,
|
|
1578
|
+
animatedDetentsState,
|
|
1579
|
+
android_keyboardInputMode,
|
|
1580
|
+
evaluatePosition,
|
|
1581
|
+
]
|
|
1582
|
+
);
|
|
1583
|
+
|
|
1584
|
+
/**
|
|
1585
|
+
* Reaction to the `snapPoints` change, to insure that the sheet position reflect
|
|
1586
|
+
* to the current point correctly.
|
|
1587
|
+
*
|
|
1588
|
+
* @alias OnSnapPointsChange
|
|
1589
|
+
*/
|
|
1590
|
+
useAnimatedReaction(
|
|
1591
|
+
() => animatedDetentsState.get().detents,
|
|
1592
|
+
(result, previous) => {
|
|
1593
|
+
/**
|
|
1594
|
+
* if values did not change, and did handle on mount animation
|
|
1595
|
+
* then we early exit the method.
|
|
1596
|
+
*/
|
|
1597
|
+
if (
|
|
1598
|
+
JSON.stringify(result) === JSON.stringify(previous) &&
|
|
1599
|
+
isAnimatedOnMount.value
|
|
1600
|
+
) {
|
|
1601
|
+
return;
|
|
1602
|
+
}
|
|
1603
|
+
|
|
1604
|
+
/**
|
|
1605
|
+
* if layout is not calculated yet, then we exit the method.
|
|
1606
|
+
*/
|
|
1607
|
+
if (!isLayoutCalculated.value) {
|
|
1608
|
+
return;
|
|
1609
|
+
}
|
|
1610
|
+
|
|
1611
|
+
if (__DEV__) {
|
|
1612
|
+
runOnJS(print)({
|
|
1613
|
+
component: 'BottomSheet',
|
|
1614
|
+
method: 'useAnimatedReaction::OnSnapPointChange',
|
|
1615
|
+
category: 'effect',
|
|
1616
|
+
params: {
|
|
1617
|
+
result,
|
|
1618
|
+
},
|
|
1619
|
+
});
|
|
1620
|
+
}
|
|
1621
|
+
|
|
1622
|
+
evaluatePosition(ANIMATION_SOURCE.SNAP_POINT_CHANGE);
|
|
1623
|
+
},
|
|
1624
|
+
[isLayoutCalculated, isAnimatedOnMount, animatedDetentsState]
|
|
1625
|
+
);
|
|
1626
|
+
|
|
1627
|
+
/**
|
|
1628
|
+
* Reaction to the keyboard state change.
|
|
1629
|
+
*
|
|
1630
|
+
* @alias OnKeyboardStateChange
|
|
1631
|
+
*/
|
|
1632
|
+
useAnimatedReaction(
|
|
1633
|
+
() =>
|
|
1634
|
+
animatedKeyboardState.get().status + animatedKeyboardState.get().height,
|
|
1635
|
+
(result, _previousResult) => {
|
|
1636
|
+
/**
|
|
1637
|
+
* if keyboard state is equal to the previous state, then exit the method
|
|
1638
|
+
*/
|
|
1639
|
+
if (result === _previousResult) {
|
|
1640
|
+
return;
|
|
1641
|
+
}
|
|
1642
|
+
|
|
1643
|
+
const { status, height, easing, duration, target } =
|
|
1644
|
+
animatedKeyboardState.get();
|
|
1645
|
+
|
|
1646
|
+
/**
|
|
1647
|
+
* if state is undetermined, then we early exit.
|
|
1648
|
+
*/
|
|
1649
|
+
if (status === KEYBOARD_STATUS.UNDETERMINED) {
|
|
1650
|
+
return;
|
|
1651
|
+
}
|
|
1652
|
+
|
|
1653
|
+
const { status: animationStatus, source: animationSource } =
|
|
1654
|
+
animatedAnimationState.get();
|
|
1655
|
+
/**
|
|
1656
|
+
* if keyboard is hidden by customer gesture, then we early exit.
|
|
1657
|
+
*/
|
|
1658
|
+
if (
|
|
1659
|
+
status === KEYBOARD_STATUS.HIDDEN &&
|
|
1660
|
+
animationStatus === ANIMATION_STATUS.RUNNING &&
|
|
1661
|
+
animationSource === ANIMATION_SOURCE.GESTURE
|
|
1662
|
+
) {
|
|
1663
|
+
return;
|
|
1664
|
+
}
|
|
1665
|
+
|
|
1666
|
+
/**
|
|
1667
|
+
* Calculate the keyboard height in the container.
|
|
1668
|
+
*/
|
|
1669
|
+
const containerOffset = animatedLayoutState.get().containerOffset;
|
|
1670
|
+
let heightWithinContainer =
|
|
1671
|
+
height === 0
|
|
1672
|
+
? 0
|
|
1673
|
+
: $modal
|
|
1674
|
+
? Math.abs(
|
|
1675
|
+
height - Math.abs(bottomInset - containerOffset.bottom)
|
|
1676
|
+
)
|
|
1677
|
+
: Math.abs(height - containerOffset.bottom);
|
|
1678
|
+
|
|
1679
|
+
if (__DEV__) {
|
|
1680
|
+
runOnJS(print)({
|
|
1681
|
+
component: 'BottomSheet',
|
|
1682
|
+
method: 'useAnimatedReaction::OnKeyboardStateChange',
|
|
1683
|
+
category: 'effect',
|
|
1684
|
+
params: {
|
|
1685
|
+
status,
|
|
1686
|
+
height,
|
|
1687
|
+
heightWithinContainer,
|
|
1688
|
+
containerOffset,
|
|
1689
|
+
},
|
|
1690
|
+
});
|
|
1691
|
+
}
|
|
1692
|
+
|
|
1693
|
+
/**
|
|
1694
|
+
* if platform is android and the input mode is resize, then exit the method
|
|
1695
|
+
*/
|
|
1696
|
+
if (
|
|
1697
|
+
Platform.OS === 'android' &&
|
|
1698
|
+
android_keyboardInputMode === KEYBOARD_INPUT_MODE.adjustResize
|
|
1699
|
+
) {
|
|
1700
|
+
heightWithinContainer = 0;
|
|
1701
|
+
|
|
1702
|
+
if (keyboardBehavior === KEYBOARD_BEHAVIOR.interactive) {
|
|
1703
|
+
animatedKeyboardState.set({
|
|
1704
|
+
target,
|
|
1705
|
+
status,
|
|
1706
|
+
height,
|
|
1707
|
+
easing,
|
|
1708
|
+
duration,
|
|
1709
|
+
heightWithinContainer,
|
|
1710
|
+
});
|
|
1711
|
+
return;
|
|
1712
|
+
}
|
|
1713
|
+
}
|
|
1714
|
+
animatedKeyboardState.set(state => ({
|
|
1715
|
+
...state,
|
|
1716
|
+
heightWithinContainer,
|
|
1717
|
+
}));
|
|
1718
|
+
|
|
1719
|
+
/**
|
|
1720
|
+
* if user is interacting with sheet, then exit the method
|
|
1721
|
+
*/
|
|
1722
|
+
const hasActiveGesture =
|
|
1723
|
+
animatedContentGestureState.value === State.ACTIVE ||
|
|
1724
|
+
animatedContentGestureState.value === State.BEGAN ||
|
|
1725
|
+
animatedHandleGestureState.value === State.ACTIVE ||
|
|
1726
|
+
animatedHandleGestureState.value === State.BEGAN;
|
|
1727
|
+
if (hasActiveGesture) {
|
|
1728
|
+
return;
|
|
1729
|
+
}
|
|
1730
|
+
|
|
1731
|
+
/**
|
|
1732
|
+
* if new keyboard state is hidden and blur behavior is none, then exit the method
|
|
1733
|
+
*/
|
|
1734
|
+
if (
|
|
1735
|
+
status === KEYBOARD_STATUS.HIDDEN &&
|
|
1736
|
+
keyboardBlurBehavior === KEYBOARD_BLUR_BEHAVIOR.none
|
|
1737
|
+
) {
|
|
1738
|
+
return;
|
|
1739
|
+
}
|
|
1740
|
+
|
|
1741
|
+
const animationConfigs = getKeyboardAnimationConfigs(easing, duration);
|
|
1742
|
+
evaluatePosition(ANIMATION_SOURCE.KEYBOARD, animationConfigs);
|
|
1743
|
+
},
|
|
1744
|
+
[
|
|
1745
|
+
$modal,
|
|
1746
|
+
bottomInset,
|
|
1747
|
+
keyboardBehavior,
|
|
1748
|
+
keyboardBlurBehavior,
|
|
1749
|
+
android_keyboardInputMode,
|
|
1750
|
+
animatedKeyboardState,
|
|
1751
|
+
animatedLayoutState,
|
|
1752
|
+
getEvaluatedPosition,
|
|
1753
|
+
]
|
|
1754
|
+
);
|
|
1755
|
+
|
|
1756
|
+
/**
|
|
1757
|
+
* sets provided animated position
|
|
1758
|
+
*/
|
|
1759
|
+
useAnimatedReaction(
|
|
1760
|
+
() => animatedPosition.value,
|
|
1761
|
+
_animatedPosition => {
|
|
1762
|
+
if (_providedAnimatedPosition) {
|
|
1763
|
+
_providedAnimatedPosition.value = _animatedPosition + topInset;
|
|
1764
|
+
}
|
|
1765
|
+
},
|
|
1766
|
+
[_providedAnimatedPosition, topInset]
|
|
1767
|
+
);
|
|
1768
|
+
|
|
1769
|
+
/**
|
|
1770
|
+
* sets provided animated index
|
|
1771
|
+
*/
|
|
1772
|
+
useAnimatedReaction(
|
|
1773
|
+
() => animatedIndex.value,
|
|
1774
|
+
_animatedIndex => {
|
|
1775
|
+
if (_providedAnimatedIndex) {
|
|
1776
|
+
_providedAnimatedIndex.value = _animatedIndex;
|
|
1777
|
+
}
|
|
1778
|
+
},
|
|
1779
|
+
[_providedAnimatedIndex]
|
|
1780
|
+
);
|
|
1781
|
+
|
|
1782
|
+
/**
|
|
1783
|
+
* React to `index` prop to snap the sheet to the new position.
|
|
1784
|
+
*
|
|
1785
|
+
* @alias onIndexChange
|
|
1786
|
+
*/
|
|
1787
|
+
useEffect(() => {
|
|
1788
|
+
// early exit, if animate on mount is set and it did not animate yet.
|
|
1789
|
+
if (animateOnMount && !isAnimatedOnMount.value) {
|
|
1790
|
+
return;
|
|
1791
|
+
}
|
|
1792
|
+
|
|
1793
|
+
handleSnapToIndex(_providedIndex);
|
|
1794
|
+
}, [animateOnMount, _providedIndex, isAnimatedOnMount, handleSnapToIndex]);
|
|
1795
|
+
//#endregion
|
|
1796
|
+
|
|
1797
|
+
// render
|
|
1798
|
+
return (
|
|
1799
|
+
<BottomSheetProvider value={externalContextVariables}>
|
|
1800
|
+
<BottomSheetInternalProvider value={internalContextVariables}>
|
|
1801
|
+
<BottomSheetGestureHandlersProvider
|
|
1802
|
+
gestureEventsHandlersHook={gestureEventsHandlersHook}
|
|
1803
|
+
>
|
|
1804
|
+
{BackdropComponent ? (
|
|
1805
|
+
<BackdropComponent
|
|
1806
|
+
animatedIndex={animatedIndex}
|
|
1807
|
+
animatedPosition={animatedPosition}
|
|
1808
|
+
style={StyleSheet.absoluteFillObject}
|
|
1809
|
+
/>
|
|
1810
|
+
) : null}
|
|
1811
|
+
<BottomSheetHostingContainer
|
|
1812
|
+
key="BottomSheetContainer"
|
|
1813
|
+
shouldCalculateHeight={!$modal}
|
|
1814
|
+
layoutState={animatedLayoutState}
|
|
1815
|
+
containerLayoutState={containerLayoutState}
|
|
1816
|
+
topInset={topInset}
|
|
1817
|
+
bottomInset={bottomInset}
|
|
1818
|
+
detached={detached}
|
|
1819
|
+
style={_providedContainerStyle}
|
|
1820
|
+
>
|
|
1821
|
+
<BottomSheetBody style={style}>
|
|
1822
|
+
{backgroundComponent === null ? null : (
|
|
1823
|
+
<BottomSheetBackgroundContainer
|
|
1824
|
+
key="BottomSheetBackgroundContainer"
|
|
1825
|
+
animatedIndex={animatedIndex}
|
|
1826
|
+
animatedPosition={animatedPosition}
|
|
1827
|
+
backgroundComponent={backgroundComponent}
|
|
1828
|
+
backgroundStyle={_providedBackgroundStyle}
|
|
1829
|
+
/>
|
|
1830
|
+
)}
|
|
1831
|
+
<BottomSheetContent
|
|
1832
|
+
pointerEvents="box-none"
|
|
1833
|
+
accessible={_providedAccessible ?? undefined}
|
|
1834
|
+
accessibilityRole={_providedAccessibilityRole ?? undefined}
|
|
1835
|
+
accessibilityLabel={_providedAccessibilityLabel ?? undefined}
|
|
1836
|
+
keyboardBehavior={keyboardBehavior}
|
|
1837
|
+
detached={detached}
|
|
1838
|
+
>
|
|
1839
|
+
{children}
|
|
1840
|
+
{footerComponent ? (
|
|
1841
|
+
<BottomSheetFooterContainer
|
|
1842
|
+
footerComponent={footerComponent}
|
|
1843
|
+
/>
|
|
1844
|
+
) : null}
|
|
1845
|
+
</BottomSheetContent>
|
|
1846
|
+
{handleComponent !== null ? (
|
|
1847
|
+
<BottomSheetHandleContainer
|
|
1848
|
+
key="BottomSheetHandleContainer"
|
|
1849
|
+
animatedIndex={animatedIndex}
|
|
1850
|
+
animatedPosition={animatedPosition}
|
|
1851
|
+
enableHandlePanningGesture={enableHandlePanningGesture}
|
|
1852
|
+
enableOverDrag={enableOverDrag}
|
|
1853
|
+
enablePanDownToClose={enablePanDownToClose}
|
|
1854
|
+
overDragResistanceFactor={overDragResistanceFactor}
|
|
1855
|
+
keyboardBehavior={keyboardBehavior}
|
|
1856
|
+
handleComponent={handleComponent}
|
|
1857
|
+
handleStyle={_providedHandleStyle}
|
|
1858
|
+
handleIndicatorStyle={_providedHandleIndicatorStyle}
|
|
1859
|
+
/>
|
|
1860
|
+
) : null}
|
|
1861
|
+
</BottomSheetBody>
|
|
1862
|
+
{/* <BottomSheetDebugView
|
|
1863
|
+
values={{
|
|
1864
|
+
index: animatedIndex,
|
|
1865
|
+
position: animatedPosition,
|
|
1866
|
+
sheetStatus: animatedSheetState,
|
|
1867
|
+
scrollableStatus: animatedScrollableStatus,
|
|
1868
|
+
layoutState: animatedLayoutState,
|
|
1869
|
+
detentsState: animatedDetentsState,
|
|
1870
|
+
sheetHeight: animatedSheetHeight,
|
|
1871
|
+
isLayoutCalculated,
|
|
1872
|
+
}}
|
|
1873
|
+
/> */}
|
|
1874
|
+
</BottomSheetHostingContainer>
|
|
1875
|
+
</BottomSheetGestureHandlersProvider>
|
|
1876
|
+
</BottomSheetInternalProvider>
|
|
1877
|
+
</BottomSheetProvider>
|
|
1878
|
+
);
|
|
1879
|
+
}
|
|
1880
|
+
);
|
|
1881
|
+
|
|
1882
|
+
const BottomSheet = memo(BottomSheetComponent);
|
|
1883
|
+
BottomSheet.displayName = 'BottomSheet';
|
|
1884
|
+
|
|
1885
|
+
export default BottomSheet;
|