@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,482 @@
|
|
|
1
|
+
import { Portal, usePortal } from '@gorhom/portal';
|
|
2
|
+
import React, {
|
|
3
|
+
forwardRef,
|
|
4
|
+
memo,
|
|
5
|
+
type RefObject,
|
|
6
|
+
useCallback,
|
|
7
|
+
useImperativeHandle,
|
|
8
|
+
useMemo,
|
|
9
|
+
useRef,
|
|
10
|
+
useState,
|
|
11
|
+
} from 'react';
|
|
12
|
+
import type { SNAP_POINT_TYPE } from '../../constants';
|
|
13
|
+
import { useBottomSheetModalInternal } from '../../hooks';
|
|
14
|
+
import type { BottomSheetMethods, BottomSheetModalMethods } from '../../types';
|
|
15
|
+
import { print } from '../../utilities';
|
|
16
|
+
import { id } from '../../utilities/id';
|
|
17
|
+
import BottomSheet from '../bottomSheet';
|
|
18
|
+
import {
|
|
19
|
+
DEFAULT_ENABLE_DISMISS_ON_CLOSE,
|
|
20
|
+
DEFAULT_STACK_BEHAVIOR,
|
|
21
|
+
} from './constants';
|
|
22
|
+
import type {
|
|
23
|
+
BottomSheetModalPrivateMethods,
|
|
24
|
+
BottomSheetModalProps,
|
|
25
|
+
BottomSheetModalState,
|
|
26
|
+
} from './types';
|
|
27
|
+
|
|
28
|
+
const INITIAL_STATE: BottomSheetModalState = {
|
|
29
|
+
mount: false,
|
|
30
|
+
data: undefined,
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
// biome-ignore lint/suspicious/noExplicitAny: Using 'any' allows users to define their own strict types for 'data' property.
|
|
34
|
+
type BottomSheetModal<T = any> = BottomSheetModalMethods<T>;
|
|
35
|
+
|
|
36
|
+
// biome-ignore lint/suspicious/noExplicitAny: Using 'any' allows users to define their own strict types for 'data' property.
|
|
37
|
+
function BottomSheetModalComponent<T = any>(
|
|
38
|
+
props: BottomSheetModalProps<T>,
|
|
39
|
+
ref: React.ForwardedRef<BottomSheetModal<T>>
|
|
40
|
+
) {
|
|
41
|
+
const {
|
|
42
|
+
// modal props
|
|
43
|
+
name,
|
|
44
|
+
stackBehavior = DEFAULT_STACK_BEHAVIOR,
|
|
45
|
+
enableDismissOnClose = DEFAULT_ENABLE_DISMISS_ON_CLOSE,
|
|
46
|
+
onDismiss: _providedOnDismiss,
|
|
47
|
+
onAnimate: _providedOnAnimate,
|
|
48
|
+
|
|
49
|
+
// bottom sheet props
|
|
50
|
+
index = 0,
|
|
51
|
+
snapPoints,
|
|
52
|
+
enablePanDownToClose = true,
|
|
53
|
+
animateOnMount = true,
|
|
54
|
+
containerComponent: ContainerComponent = React.Fragment,
|
|
55
|
+
|
|
56
|
+
// callbacks
|
|
57
|
+
onChange: _providedOnChange,
|
|
58
|
+
|
|
59
|
+
// components
|
|
60
|
+
children: Content,
|
|
61
|
+
...bottomSheetProps
|
|
62
|
+
} = props;
|
|
63
|
+
|
|
64
|
+
//#region state
|
|
65
|
+
const [{ mount, data }, setState] =
|
|
66
|
+
useState<BottomSheetModalState<T>>(INITIAL_STATE);
|
|
67
|
+
//#endregion
|
|
68
|
+
|
|
69
|
+
//#region hooks
|
|
70
|
+
const {
|
|
71
|
+
hostName,
|
|
72
|
+
containerLayoutState,
|
|
73
|
+
mountSheet,
|
|
74
|
+
unmountSheet,
|
|
75
|
+
willUnmountSheet,
|
|
76
|
+
} = useBottomSheetModalInternal();
|
|
77
|
+
const { removePortal: unmountPortal } = usePortal(hostName);
|
|
78
|
+
//#endregion
|
|
79
|
+
|
|
80
|
+
//#region refs
|
|
81
|
+
const bottomSheetRef = useRef<BottomSheet>(null);
|
|
82
|
+
const currentIndexRef = useRef(!animateOnMount ? index : -1);
|
|
83
|
+
const nextIndexRef = useRef<number | null>(null);
|
|
84
|
+
const restoreIndexRef = useRef(-1);
|
|
85
|
+
const minimized = useRef(false);
|
|
86
|
+
const forcedDismissed = useRef(false);
|
|
87
|
+
const mounted = useRef(false);
|
|
88
|
+
mounted.current = mount;
|
|
89
|
+
//#endregion
|
|
90
|
+
|
|
91
|
+
//#region variables
|
|
92
|
+
const key = useMemo(() => name || `bottom-sheet-modal-${id()}`, [name]);
|
|
93
|
+
//#endregion
|
|
94
|
+
|
|
95
|
+
//#region private methods
|
|
96
|
+
const resetVariables = useCallback(function resetVariables() {
|
|
97
|
+
print({
|
|
98
|
+
component: BottomSheetModal.name,
|
|
99
|
+
method: resetVariables.name,
|
|
100
|
+
});
|
|
101
|
+
currentIndexRef.current = -1;
|
|
102
|
+
restoreIndexRef.current = -1;
|
|
103
|
+
minimized.current = false;
|
|
104
|
+
mounted.current = false;
|
|
105
|
+
forcedDismissed.current = false;
|
|
106
|
+
}, []);
|
|
107
|
+
const unmount = useCallback(
|
|
108
|
+
function unmount() {
|
|
109
|
+
if (__DEV__) {
|
|
110
|
+
print({
|
|
111
|
+
component: BottomSheetModal.name,
|
|
112
|
+
method: unmount.name,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
const _mounted = mounted.current;
|
|
116
|
+
|
|
117
|
+
// reset variables
|
|
118
|
+
resetVariables();
|
|
119
|
+
|
|
120
|
+
// unmount sheet and portal
|
|
121
|
+
unmountSheet(key);
|
|
122
|
+
unmountPortal(key);
|
|
123
|
+
|
|
124
|
+
// unmount the node, if sheet is still mounted
|
|
125
|
+
if (_mounted) {
|
|
126
|
+
setState(INITIAL_STATE);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// fire `onDismiss` callback
|
|
130
|
+
if (_providedOnDismiss) {
|
|
131
|
+
_providedOnDismiss();
|
|
132
|
+
}
|
|
133
|
+
},
|
|
134
|
+
[key, resetVariables, unmountSheet, unmountPortal, _providedOnDismiss]
|
|
135
|
+
);
|
|
136
|
+
//#endregion
|
|
137
|
+
|
|
138
|
+
//#region bottom sheet methods
|
|
139
|
+
const handleSnapToIndex = useCallback<BottomSheetMethods['snapToIndex']>(
|
|
140
|
+
(...args) => {
|
|
141
|
+
if (minimized.current) {
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
bottomSheetRef.current?.snapToIndex(...args);
|
|
145
|
+
},
|
|
146
|
+
[]
|
|
147
|
+
);
|
|
148
|
+
const handleSnapToPosition = useCallback<
|
|
149
|
+
BottomSheetMethods['snapToPosition']
|
|
150
|
+
>((...args) => {
|
|
151
|
+
if (minimized.current) {
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
bottomSheetRef.current?.snapToPosition(...args);
|
|
155
|
+
}, []);
|
|
156
|
+
const handleExpand: BottomSheetMethods['expand'] = useCallback((...args) => {
|
|
157
|
+
if (minimized.current) {
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
bottomSheetRef.current?.expand(...args);
|
|
161
|
+
}, []);
|
|
162
|
+
const handleCollapse: BottomSheetMethods['collapse'] = useCallback(
|
|
163
|
+
(...args) => {
|
|
164
|
+
if (minimized.current) {
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
bottomSheetRef.current?.collapse(...args);
|
|
168
|
+
},
|
|
169
|
+
[]
|
|
170
|
+
);
|
|
171
|
+
const handleClose: BottomSheetMethods['close'] = useCallback((...args) => {
|
|
172
|
+
if (minimized.current) {
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
bottomSheetRef.current?.close(...args);
|
|
176
|
+
}, []);
|
|
177
|
+
const handleForceClose: BottomSheetMethods['forceClose'] = useCallback(
|
|
178
|
+
(...args) => {
|
|
179
|
+
if (minimized.current) {
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
bottomSheetRef.current?.forceClose(...args);
|
|
183
|
+
},
|
|
184
|
+
[]
|
|
185
|
+
);
|
|
186
|
+
//#endregion
|
|
187
|
+
|
|
188
|
+
//#region bottom sheet modal methods
|
|
189
|
+
// biome-ignore lint/correctness/useExhaustiveDependencies(BottomSheetModal.name): used for debug only
|
|
190
|
+
// biome-ignore lint/correctness/useExhaustiveDependencies(ref): ref is a stable object
|
|
191
|
+
const handlePresent = useCallback(
|
|
192
|
+
function handlePresent(_data?: T) {
|
|
193
|
+
requestAnimationFrame(() => {
|
|
194
|
+
setState({
|
|
195
|
+
mount: true,
|
|
196
|
+
data: _data,
|
|
197
|
+
});
|
|
198
|
+
mountSheet(
|
|
199
|
+
key,
|
|
200
|
+
ref as unknown as RefObject<BottomSheetModalPrivateMethods>,
|
|
201
|
+
stackBehavior
|
|
202
|
+
);
|
|
203
|
+
ref;
|
|
204
|
+
|
|
205
|
+
if (__DEV__) {
|
|
206
|
+
print({
|
|
207
|
+
component: BottomSheetModal.name,
|
|
208
|
+
method: handlePresent.name,
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
},
|
|
213
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
214
|
+
[key, stackBehavior, mountSheet]
|
|
215
|
+
);
|
|
216
|
+
// biome-ignore lint/correctness/useExhaustiveDependencies(BottomSheetModal.name): used for debug only
|
|
217
|
+
const handleDismiss = useCallback<BottomSheetModalMethods['dismiss']>(
|
|
218
|
+
function handleDismiss(animationConfigs) {
|
|
219
|
+
if (__DEV__) {
|
|
220
|
+
print({
|
|
221
|
+
component: BottomSheetModal.name,
|
|
222
|
+
method: handleDismiss.name,
|
|
223
|
+
params: {
|
|
224
|
+
currentIndexRef: currentIndexRef.current,
|
|
225
|
+
minimized: minimized.current,
|
|
226
|
+
},
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const animating = nextIndexRef.current != null;
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* early exit, if not minimized, it is in closed position and not animating
|
|
234
|
+
*/
|
|
235
|
+
if (
|
|
236
|
+
currentIndexRef.current === -1 &&
|
|
237
|
+
minimized.current === false &&
|
|
238
|
+
!animating
|
|
239
|
+
) {
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* unmount and early exit, if minimized or it is in closed position and not animating
|
|
245
|
+
*/
|
|
246
|
+
if (
|
|
247
|
+
!animating &&
|
|
248
|
+
(minimized.current ||
|
|
249
|
+
(currentIndexRef.current === -1 && enablePanDownToClose))
|
|
250
|
+
) {
|
|
251
|
+
unmount();
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
willUnmountSheet(key);
|
|
255
|
+
forcedDismissed.current = true;
|
|
256
|
+
bottomSheetRef.current?.forceClose(animationConfigs);
|
|
257
|
+
},
|
|
258
|
+
[willUnmountSheet, unmount, key, enablePanDownToClose]
|
|
259
|
+
);
|
|
260
|
+
const handleMinimize = useCallback(
|
|
261
|
+
function handleMinimize() {
|
|
262
|
+
if (__DEV__) {
|
|
263
|
+
print({
|
|
264
|
+
component: BottomSheetModal.name,
|
|
265
|
+
method: handleMinimize.name,
|
|
266
|
+
params: {
|
|
267
|
+
minimized: minimized.current,
|
|
268
|
+
},
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
if (minimized.current) {
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
minimized.current = true;
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* if modal got minimized before it finish its mounting
|
|
278
|
+
* animation, we set the `restoreIndexRef` to the
|
|
279
|
+
* provided index.
|
|
280
|
+
*/
|
|
281
|
+
if (currentIndexRef.current === -1) {
|
|
282
|
+
restoreIndexRef.current = index;
|
|
283
|
+
} else {
|
|
284
|
+
restoreIndexRef.current = currentIndexRef.current;
|
|
285
|
+
}
|
|
286
|
+
bottomSheetRef.current?.close();
|
|
287
|
+
},
|
|
288
|
+
[index]
|
|
289
|
+
);
|
|
290
|
+
// biome-ignore lint/correctness/useExhaustiveDependencies(BottomSheetModal.name): used for debug only
|
|
291
|
+
const handleRestore = useCallback(function handleRestore() {
|
|
292
|
+
if (__DEV__) {
|
|
293
|
+
print({
|
|
294
|
+
component: BottomSheetModal.name,
|
|
295
|
+
method: handleRestore.name,
|
|
296
|
+
params: {
|
|
297
|
+
minimized: minimized.current,
|
|
298
|
+
forcedDismissed: forcedDismissed.current,
|
|
299
|
+
},
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
if (!minimized.current || forcedDismissed.current) {
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
minimized.current = false;
|
|
306
|
+
bottomSheetRef.current?.snapToIndex(restoreIndexRef.current);
|
|
307
|
+
}, []);
|
|
308
|
+
//#endregion
|
|
309
|
+
|
|
310
|
+
//#region callbacks
|
|
311
|
+
const handlePortalOnUnmount = useCallback(
|
|
312
|
+
function handlePortalOnUnmount() {
|
|
313
|
+
if (__DEV__) {
|
|
314
|
+
print({
|
|
315
|
+
component: BottomSheetModal.name,
|
|
316
|
+
method: handlePortalOnUnmount.name,
|
|
317
|
+
params: {
|
|
318
|
+
minimized: minimized.current,
|
|
319
|
+
forcedDismissed: forcedDismissed.current,
|
|
320
|
+
},
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* if modal is already been dismiss, we exit the method.
|
|
325
|
+
*/
|
|
326
|
+
if (currentIndexRef.current === -1 && minimized.current === false) {
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
mounted.current = false;
|
|
331
|
+
forcedDismissed.current = true;
|
|
332
|
+
|
|
333
|
+
if (minimized.current) {
|
|
334
|
+
unmount();
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
willUnmountSheet(key);
|
|
338
|
+
bottomSheetRef.current?.close();
|
|
339
|
+
},
|
|
340
|
+
[key, unmount, willUnmountSheet]
|
|
341
|
+
);
|
|
342
|
+
const handlePortalRender = useCallback(function handlePortalRender(
|
|
343
|
+
render: () => void
|
|
344
|
+
) {
|
|
345
|
+
if (mounted.current) {
|
|
346
|
+
render();
|
|
347
|
+
}
|
|
348
|
+
}, []);
|
|
349
|
+
// biome-ignore lint/correctness/useExhaustiveDependencies(BottomSheetModal.name): used for debug only
|
|
350
|
+
const handleBottomSheetOnChange = useCallback(
|
|
351
|
+
function handleBottomSheetOnChange(
|
|
352
|
+
_index: number,
|
|
353
|
+
_position: number,
|
|
354
|
+
_type: SNAP_POINT_TYPE
|
|
355
|
+
) {
|
|
356
|
+
if (__DEV__) {
|
|
357
|
+
print({
|
|
358
|
+
component: BottomSheetModal.name,
|
|
359
|
+
method: handleBottomSheetOnChange.name,
|
|
360
|
+
category: 'callback',
|
|
361
|
+
params: {
|
|
362
|
+
minimized: minimized.current,
|
|
363
|
+
forcedDismissed: forcedDismissed.current,
|
|
364
|
+
},
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
currentIndexRef.current = _index;
|
|
368
|
+
nextIndexRef.current = null;
|
|
369
|
+
|
|
370
|
+
if (_providedOnChange) {
|
|
371
|
+
_providedOnChange(_index, _position, _type);
|
|
372
|
+
}
|
|
373
|
+
},
|
|
374
|
+
[_providedOnChange]
|
|
375
|
+
);
|
|
376
|
+
const handleBottomSheetOnAnimate = useCallback(
|
|
377
|
+
(
|
|
378
|
+
fromIndex: number,
|
|
379
|
+
toIndex: number,
|
|
380
|
+
fromPosition: number,
|
|
381
|
+
toPosition: number
|
|
382
|
+
) => {
|
|
383
|
+
nextIndexRef.current = toIndex;
|
|
384
|
+
|
|
385
|
+
if (_providedOnAnimate) {
|
|
386
|
+
_providedOnAnimate(fromIndex, toIndex, fromPosition, toPosition);
|
|
387
|
+
}
|
|
388
|
+
},
|
|
389
|
+
[_providedOnAnimate]
|
|
390
|
+
);
|
|
391
|
+
// biome-ignore lint/correctness/useExhaustiveDependencies(BottomSheetModal.name): used for debug only
|
|
392
|
+
const handleBottomSheetOnClose = useCallback(
|
|
393
|
+
function handleBottomSheetOnClose() {
|
|
394
|
+
if (__DEV__) {
|
|
395
|
+
print({
|
|
396
|
+
component: BottomSheetModal.name,
|
|
397
|
+
method: handleBottomSheetOnClose.name,
|
|
398
|
+
category: 'callback',
|
|
399
|
+
params: {
|
|
400
|
+
minimized: minimized.current,
|
|
401
|
+
forcedDismissed: forcedDismissed.current,
|
|
402
|
+
},
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
if (minimized.current) {
|
|
407
|
+
return;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
if (enableDismissOnClose) {
|
|
411
|
+
unmount();
|
|
412
|
+
}
|
|
413
|
+
},
|
|
414
|
+
[enableDismissOnClose, unmount]
|
|
415
|
+
);
|
|
416
|
+
//#endregion
|
|
417
|
+
|
|
418
|
+
//#region expose methods
|
|
419
|
+
useImperativeHandle(ref, () => ({
|
|
420
|
+
// sheet
|
|
421
|
+
snapToIndex: handleSnapToIndex,
|
|
422
|
+
snapToPosition: handleSnapToPosition,
|
|
423
|
+
expand: handleExpand,
|
|
424
|
+
collapse: handleCollapse,
|
|
425
|
+
close: handleClose,
|
|
426
|
+
forceClose: handleForceClose,
|
|
427
|
+
// modal methods
|
|
428
|
+
dismiss: handleDismiss,
|
|
429
|
+
present: handlePresent,
|
|
430
|
+
// internal
|
|
431
|
+
minimize: handleMinimize,
|
|
432
|
+
restore: handleRestore,
|
|
433
|
+
}));
|
|
434
|
+
//#endregion
|
|
435
|
+
|
|
436
|
+
// render
|
|
437
|
+
return mount ? (
|
|
438
|
+
<Portal
|
|
439
|
+
key={key}
|
|
440
|
+
name={key}
|
|
441
|
+
hostName={hostName}
|
|
442
|
+
handleOnMount={handlePortalRender}
|
|
443
|
+
handleOnUpdate={handlePortalRender}
|
|
444
|
+
handleOnUnmount={handlePortalOnUnmount}
|
|
445
|
+
>
|
|
446
|
+
<ContainerComponent key={key}>
|
|
447
|
+
<BottomSheet
|
|
448
|
+
{...bottomSheetProps}
|
|
449
|
+
ref={bottomSheetRef}
|
|
450
|
+
key={key}
|
|
451
|
+
index={index}
|
|
452
|
+
snapPoints={snapPoints}
|
|
453
|
+
enablePanDownToClose={enablePanDownToClose}
|
|
454
|
+
animateOnMount={animateOnMount}
|
|
455
|
+
containerLayoutState={containerLayoutState}
|
|
456
|
+
onChange={handleBottomSheetOnChange}
|
|
457
|
+
onClose={handleBottomSheetOnClose}
|
|
458
|
+
onAnimate={handleBottomSheetOnAnimate}
|
|
459
|
+
$modal={true}
|
|
460
|
+
>
|
|
461
|
+
{typeof Content === 'function' ? <Content data={data} /> : Content}
|
|
462
|
+
</BottomSheet>
|
|
463
|
+
</ContainerComponent>
|
|
464
|
+
</Portal>
|
|
465
|
+
) : null;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
const BottomSheetModal = memo(forwardRef(BottomSheetModalComponent)) as <
|
|
469
|
+
// biome-ignore lint/suspicious/noExplicitAny: Using 'any' allows users to define their own strict types for 'data' property.
|
|
470
|
+
T = any,
|
|
471
|
+
>(
|
|
472
|
+
props: BottomSheetModalProps<T> & {
|
|
473
|
+
ref?: React.ForwardedRef<BottomSheetModal<T>>;
|
|
474
|
+
}
|
|
475
|
+
) => ReturnType<typeof BottomSheetModalComponent>;
|
|
476
|
+
(
|
|
477
|
+
BottomSheetModal as React.MemoExoticComponent<
|
|
478
|
+
typeof BottomSheetModalComponent
|
|
479
|
+
>
|
|
480
|
+
).displayName = 'BottomSheetModal';
|
|
481
|
+
|
|
482
|
+
export default BottomSheetModal;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
import type { View } from 'react-native';
|
|
3
|
+
import type { MODAL_STACK_BEHAVIOR } from '../../constants';
|
|
4
|
+
import type { BottomSheetProps } from '../bottomSheet';
|
|
5
|
+
|
|
6
|
+
export interface BottomSheetModalPrivateMethods {
|
|
7
|
+
dismiss: (force?: boolean) => void;
|
|
8
|
+
minimize: () => void;
|
|
9
|
+
restore: () => void;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export type BottomSheetModalStackBehavior = keyof typeof MODAL_STACK_BEHAVIOR;
|
|
13
|
+
|
|
14
|
+
// biome-ignore lint/suspicious/noExplicitAny: Using 'any' allows users to define their own strict types for 'data' property.
|
|
15
|
+
export interface BottomSheetModalProps<T = any>
|
|
16
|
+
extends Omit<BottomSheetProps, 'containerHeight' | 'onClose'> {
|
|
17
|
+
/**
|
|
18
|
+
* Modal name to help identify the modal for later on.
|
|
19
|
+
* @type string
|
|
20
|
+
* @default generated unique key.
|
|
21
|
+
*/
|
|
22
|
+
name?: string;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Defines the stack behavior when modal mount.
|
|
26
|
+
* - `push` it will mount the modal on top of the current one.
|
|
27
|
+
* - `switch` it will minimize the current modal then mount the new one.
|
|
28
|
+
* - `replace` it will dismiss the current modal then mount the new one.
|
|
29
|
+
* @type `push` | `switch` | `replace`
|
|
30
|
+
* @default switch
|
|
31
|
+
*/
|
|
32
|
+
stackBehavior?: BottomSheetModalStackBehavior;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Enable dismiss the modal when it is closed.
|
|
36
|
+
* @type boolean
|
|
37
|
+
* @default true
|
|
38
|
+
*/
|
|
39
|
+
enableDismissOnClose?: boolean;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Add a custom container like FullWindowOverlay
|
|
43
|
+
* allow to fix issue like https://github.com/gorhom/react-native-bottom-sheet/issues/832
|
|
44
|
+
* @type React.ComponentType
|
|
45
|
+
* @default undefined
|
|
46
|
+
*/
|
|
47
|
+
containerComponent?: React.ComponentType<React.PropsWithChildren>;
|
|
48
|
+
|
|
49
|
+
// callbacks
|
|
50
|
+
/**
|
|
51
|
+
* Callback when the modal dismissed.
|
|
52
|
+
* @type () => void;
|
|
53
|
+
*/
|
|
54
|
+
onDismiss?: () => void;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* A scrollable node or normal view.
|
|
58
|
+
* @type React.ReactNode[] | React.ReactNode | (({ data: any }?) => React.ReactElement)
|
|
59
|
+
*/
|
|
60
|
+
children: React.FC<{ data?: T }> | React.ReactNode[] | React.ReactNode;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// biome-ignore lint/suspicious/noExplicitAny: Using 'any' allows users to define their own strict types for 'data' property.
|
|
64
|
+
export interface BottomSheetModalState<T = any> {
|
|
65
|
+
mount: boolean;
|
|
66
|
+
data: T | undefined;
|
|
67
|
+
}
|