react-modal-sheet 4.4.0 → 5.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/README.md +244 -191
- package/dist/index.d.mts +39 -31
- package/dist/index.d.ts +39 -31
- package/dist/index.js +856 -670
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +794 -609
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -2
- package/src/SheetBackdrop.tsx +7 -5
- package/src/SheetContainer.tsx +29 -22
- package/src/SheetContent.tsx +76 -14
- package/src/SheetHeader.tsx +15 -12
- package/src/constants.ts +5 -3
- package/src/context.tsx +2 -42
- package/src/hooks/use-dimensions.ts +4 -4
- package/src/hooks/use-drag-constraints.ts +3 -3
- package/src/hooks/use-modal-effect.ts +19 -23
- package/src/hooks/use-resize-observer.ts +31 -0
- package/src/hooks/use-scroll-position.ts +72 -0
- package/src/hooks/use-sheet-state.ts +63 -0
- package/src/hooks/use-virtual-keyboard.ts +153 -0
- package/src/index.tsx +10 -8
- package/src/sheet.tsx +239 -209
- package/src/snap.ts +226 -0
- package/src/styles.ts +3 -3
- package/src/types.tsx +54 -59
- package/src/utils.ts +2 -150
- package/src/SheetScroller.tsx +0 -96
- package/src/hooks/use-event-callbacks.ts +0 -44
package/src/index.tsx
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
import type { MotionValue } from 'motion/react';
|
|
2
|
+
import type { RefObject } from 'react';
|
|
2
3
|
|
|
3
|
-
import
|
|
4
|
-
import { Sheet as SheetBase } from './sheet';
|
|
4
|
+
import { SheetBackdrop } from './SheetBackdrop';
|
|
5
5
|
import { SheetContainer } from './SheetContainer';
|
|
6
6
|
import { SheetContent } from './SheetContent';
|
|
7
7
|
import { SheetHeader } from './SheetHeader';
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
8
|
+
import { Sheet as SheetBase } from './sheet';
|
|
9
|
+
import type { SheetCompound, SheetDraggableProps } from './types';
|
|
10
10
|
|
|
11
11
|
export interface SheetRef {
|
|
12
12
|
y: MotionValue<number>;
|
|
13
|
-
|
|
13
|
+
yInverted: MotionValue<number>;
|
|
14
|
+
height: number;
|
|
15
|
+
snapTo: (index: number) => Promise<void>;
|
|
14
16
|
}
|
|
15
17
|
|
|
16
18
|
export const Sheet: SheetCompound = Object.assign(SheetBase, {
|
|
@@ -18,15 +20,15 @@ export const Sheet: SheetCompound = Object.assign(SheetBase, {
|
|
|
18
20
|
Header: SheetHeader,
|
|
19
21
|
Content: SheetContent,
|
|
20
22
|
Backdrop: SheetBackdrop,
|
|
21
|
-
Scroller: SheetScroller,
|
|
22
23
|
});
|
|
23
24
|
|
|
24
25
|
// Export types
|
|
25
26
|
export type SheetHeaderProps = SheetDraggableProps;
|
|
26
27
|
export type SheetContentProps = SheetDraggableProps;
|
|
27
28
|
export type {
|
|
28
|
-
SheetProps,
|
|
29
29
|
SheetBackdropProps,
|
|
30
|
-
SheetScrollerProps,
|
|
31
30
|
SheetContainerProps,
|
|
31
|
+
SheetProps,
|
|
32
|
+
SheetSnapPoint,
|
|
33
|
+
SheetStateInfo,
|
|
32
34
|
} from './types';
|
package/src/sheet.tsx
CHANGED
|
@@ -1,82 +1,92 @@
|
|
|
1
|
-
import { createPortal } from 'react-dom';
|
|
2
|
-
|
|
3
|
-
import React, {
|
|
4
|
-
Children,
|
|
5
|
-
cloneElement,
|
|
6
|
-
forwardRef,
|
|
7
|
-
useEffect,
|
|
8
|
-
useImperativeHandle,
|
|
9
|
-
useMemo,
|
|
10
|
-
useRef,
|
|
11
|
-
} from 'react';
|
|
12
|
-
|
|
13
1
|
import {
|
|
2
|
+
type DragHandler,
|
|
3
|
+
type Transition,
|
|
14
4
|
animate,
|
|
15
|
-
AnimatePresence,
|
|
16
5
|
motion,
|
|
17
|
-
type PanInfo,
|
|
18
|
-
type Transition,
|
|
19
6
|
useMotionValue,
|
|
20
7
|
useReducedMotion,
|
|
21
8
|
useTransform,
|
|
22
9
|
} from 'motion/react';
|
|
10
|
+
import React, {
|
|
11
|
+
forwardRef,
|
|
12
|
+
useImperativeHandle,
|
|
13
|
+
useRef,
|
|
14
|
+
useState,
|
|
15
|
+
} from 'react';
|
|
16
|
+
import { createPortal } from 'react-dom';
|
|
17
|
+
import useMeasure from 'react-use-measure';
|
|
23
18
|
|
|
24
19
|
import {
|
|
25
|
-
REDUCED_MOTION_TWEEN_CONFIG,
|
|
26
|
-
DEFAULT_TWEEN_CONFIG,
|
|
27
20
|
DEFAULT_DRAG_CLOSE_THRESHOLD,
|
|
28
21
|
DEFAULT_DRAG_VELOCITY_THRESHOLD,
|
|
22
|
+
DEFAULT_TWEEN_CONFIG,
|
|
29
23
|
IS_SSR,
|
|
24
|
+
REDUCED_MOTION_TWEEN_CONFIG,
|
|
30
25
|
} from './constants';
|
|
31
|
-
|
|
32
|
-
import {
|
|
33
|
-
getSheetHeight,
|
|
34
|
-
getSnapY,
|
|
35
|
-
getSnapPoints,
|
|
36
|
-
getClosestSnapPoint,
|
|
37
|
-
} from './utils';
|
|
38
|
-
|
|
39
|
-
import { type SheetContextType, type SheetProps } from './types';
|
|
40
|
-
import { SheetScrollerContextProvider, SheetContext } from './context';
|
|
41
|
-
import { styles } from './styles';
|
|
42
|
-
import { useModalEffect } from './hooks/use-modal-effect';
|
|
26
|
+
import { SheetContext } from './context';
|
|
43
27
|
import { useDimensions } from './hooks/use-dimensions';
|
|
44
|
-
import {
|
|
45
|
-
import { useStableCallback } from './hooks/use-stable-callback';
|
|
28
|
+
import { useModalEffect } from './hooks/use-modal-effect';
|
|
46
29
|
import { usePreventScroll } from './hooks/use-prevent-scroll';
|
|
30
|
+
import { useSheetState } from './hooks/use-sheet-state';
|
|
31
|
+
import { useStableCallback } from './hooks/use-stable-callback';
|
|
32
|
+
import { useVirtualKeyboard } from './hooks/use-virtual-keyboard';
|
|
33
|
+
import {
|
|
34
|
+
computeSnapPoints,
|
|
35
|
+
handleHighVelocityDrag,
|
|
36
|
+
handleLowVelocityDrag,
|
|
37
|
+
} from './snap';
|
|
38
|
+
import { styles } from './styles';
|
|
39
|
+
import { type SheetContextType, type SheetProps } from './types';
|
|
47
40
|
|
|
48
41
|
export const Sheet = forwardRef<any, SheetProps>(
|
|
49
42
|
(
|
|
50
43
|
{
|
|
51
|
-
|
|
52
|
-
onOpenEnd,
|
|
53
|
-
onClose,
|
|
54
|
-
onCloseStart,
|
|
55
|
-
onCloseEnd,
|
|
56
|
-
onSnap,
|
|
44
|
+
avoidKeyboard = true,
|
|
57
45
|
children,
|
|
46
|
+
className = '',
|
|
47
|
+
detent = 'default',
|
|
48
|
+
disableDrag: disableDragProp = false,
|
|
58
49
|
disableScrollLocking = false,
|
|
50
|
+
disableDismiss = false,
|
|
51
|
+
dragCloseThreshold = DEFAULT_DRAG_CLOSE_THRESHOLD,
|
|
52
|
+
dragVelocityThreshold = DEFAULT_DRAG_VELOCITY_THRESHOLD,
|
|
59
53
|
isOpen,
|
|
60
|
-
snapPoints: snapPointsProp,
|
|
61
|
-
rootId,
|
|
62
54
|
modalEffectRootId,
|
|
55
|
+
modalEffectThreshold,
|
|
63
56
|
mountPoint,
|
|
64
|
-
style,
|
|
65
|
-
detent = 'full-height',
|
|
66
|
-
initialSnap = 0,
|
|
67
|
-
disableDrag = false,
|
|
68
57
|
prefersReducedMotion = false,
|
|
58
|
+
snapPoints: snapPointsProp,
|
|
59
|
+
initialSnap,
|
|
60
|
+
style,
|
|
69
61
|
tweenConfig = DEFAULT_TWEEN_CONFIG,
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
62
|
+
onOpenStart,
|
|
63
|
+
onOpenEnd,
|
|
64
|
+
onClose,
|
|
65
|
+
onCloseStart,
|
|
66
|
+
onCloseEnd,
|
|
67
|
+
onSnap,
|
|
68
|
+
onDrag: onDragProp,
|
|
69
|
+
onDragStart: onDragStartProp,
|
|
70
|
+
onDragEnd: onDragEndProp,
|
|
73
71
|
...rest
|
|
74
72
|
},
|
|
75
73
|
ref
|
|
76
74
|
) => {
|
|
75
|
+
const [sheetBoundsRef, sheetBounds] = useMeasure();
|
|
77
76
|
const sheetRef = useRef<HTMLDivElement>(null);
|
|
77
|
+
const sheetHeight = Math.round(sheetBounds.height);
|
|
78
|
+
const [currentSnap, setCurrentSnap] = useState(initialSnap);
|
|
79
|
+
const snapPoints =
|
|
80
|
+
snapPointsProp && sheetHeight > 0
|
|
81
|
+
? computeSnapPoints({ sheetHeight, snapPointsProp })
|
|
82
|
+
: [];
|
|
83
|
+
|
|
84
|
+
const { windowHeight } = useDimensions();
|
|
85
|
+
const closedY = sheetHeight > 0 ? sheetHeight : windowHeight;
|
|
86
|
+
const y = useMotionValue(closedY);
|
|
87
|
+
const yInverted = useTransform(y, (val) => Math.max(sheetHeight - val, 0));
|
|
78
88
|
const indicatorRotation = useMotionValue(0);
|
|
79
|
-
|
|
89
|
+
|
|
80
90
|
const shouldReduceMotion = useReducedMotion();
|
|
81
91
|
const reduceMotion = Boolean(prefersReducedMotion || shouldReduceMotion);
|
|
82
92
|
const animationOptions: Transition = {
|
|
@@ -84,66 +94,67 @@ export const Sheet = forwardRef<any, SheetProps>(
|
|
|
84
94
|
...(reduceMotion ? REDUCED_MOTION_TWEEN_CONFIG : tweenConfig),
|
|
85
95
|
};
|
|
86
96
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
97
|
+
const keyboard = useVirtualKeyboard({
|
|
98
|
+
isEnabled: isOpen && avoidKeyboard,
|
|
99
|
+
containerRef: sheetRef,
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
// Disable drag if the keyboard is open to avoid weird behavior
|
|
103
|
+
const disableDrag = keyboard.isKeyboardOpen || disableDragProp;
|
|
91
104
|
|
|
92
105
|
// +2 for tolerance in case the animated value is slightly off
|
|
93
|
-
const zIndex = useTransform(y, (
|
|
94
|
-
|
|
106
|
+
const zIndex = useTransform(y, (val) =>
|
|
107
|
+
val + 2 >= closedY ? -1 : (style?.zIndex ?? 9999)
|
|
95
108
|
);
|
|
96
|
-
const visibility = useTransform(y, (
|
|
97
|
-
|
|
109
|
+
const visibility = useTransform(y, (val) =>
|
|
110
|
+
val + 2 >= closedY ? 'hidden' : 'visible'
|
|
98
111
|
);
|
|
99
112
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
onOpenStart,
|
|
104
|
-
onOpenEnd,
|
|
105
|
-
onCloseStart,
|
|
106
|
-
onCloseEnd,
|
|
113
|
+
const updateSnap = useStableCallback((snapIndex: number) => {
|
|
114
|
+
setCurrentSnap(snapIndex);
|
|
115
|
+
onSnap?.(snapIndex);
|
|
107
116
|
});
|
|
108
117
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
118
|
+
const getSnapPoint = useStableCallback((snapIndex: number) => {
|
|
119
|
+
if (snapPointsProp && snapPoints) {
|
|
120
|
+
if (snapIndex < 0 || snapIndex >= snapPoints.length) {
|
|
121
|
+
console.warn(
|
|
122
|
+
`Invalid snap index ${snapIndex}. Snap points are: [${snapPointsProp.join(', ')}] and their computed values are: [${snapPoints
|
|
123
|
+
.map((point) => point.snapValue)
|
|
124
|
+
.join(', ')}]`
|
|
125
|
+
);
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
return snapPoints[snapIndex];
|
|
129
|
+
}
|
|
130
|
+
return null;
|
|
116
131
|
});
|
|
117
132
|
|
|
118
|
-
const
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
* Make sure the user cannot drag the sheet beyond the upmost snap point
|
|
123
|
-
* if the `snapPoints` prop is provided.
|
|
124
|
-
*/
|
|
125
|
-
if (snapPointsProp) {
|
|
126
|
-
const sheetHeight = getSheetHeight(sheetRef);
|
|
127
|
-
const snapPoints = getSnapPoints({ snapPointsProp, sheetHeight });
|
|
128
|
-
const topSnapPoint = sheetHeight - snapPoints[0];
|
|
129
|
-
|
|
130
|
-
if (info.delta.y < 0 && currentY <= topSnapPoint) {
|
|
131
|
-
y.set(topSnapPoint);
|
|
132
|
-
return;
|
|
133
|
-
}
|
|
133
|
+
const snapTo = useStableCallback(async (snapIndex: number) => {
|
|
134
|
+
if (!snapPointsProp) {
|
|
135
|
+
console.warn('Snapping is not possible without `snapPoints` prop.');
|
|
136
|
+
return;
|
|
134
137
|
}
|
|
135
138
|
|
|
136
|
-
|
|
137
|
-
const velocity = y.getVelocity();
|
|
139
|
+
const snapPoint = getSnapPoint(snapIndex);
|
|
138
140
|
|
|
139
|
-
if (
|
|
140
|
-
|
|
141
|
+
if (snapPoint === null) {
|
|
142
|
+
console.warn(`Invalid snap index ${snapIndex}.`);
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
141
145
|
|
|
142
|
-
|
|
143
|
-
|
|
146
|
+
if (snapIndex === 0) {
|
|
147
|
+
onClose();
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
await animate(y, snapPoint.snapValueY, {
|
|
152
|
+
...animationOptions,
|
|
153
|
+
onComplete: () => updateSnap(snapIndex),
|
|
154
|
+
});
|
|
144
155
|
});
|
|
145
156
|
|
|
146
|
-
const
|
|
157
|
+
const blurActiveInput = useStableCallback(() => {
|
|
147
158
|
// Find focused input inside the sheet and blur it when dragging starts
|
|
148
159
|
// to prevent a weird ghost caret "bug" on mobile
|
|
149
160
|
const focusedElement = document.activeElement as HTMLElement | null;
|
|
@@ -159,148 +170,174 @@ export const Sheet = forwardRef<any, SheetProps>(
|
|
|
159
170
|
}
|
|
160
171
|
});
|
|
161
172
|
|
|
162
|
-
const
|
|
163
|
-
|
|
164
|
-
// User flicked the sheet down
|
|
165
|
-
onClose();
|
|
166
|
-
} else {
|
|
167
|
-
const sheetHeight = getSheetHeight(sheetRef);
|
|
168
|
-
const currentY = y.get();
|
|
173
|
+
const onDrag = useStableCallback<DragHandler>((event, info) => {
|
|
174
|
+
onDragProp?.(event, info);
|
|
169
175
|
|
|
170
|
-
|
|
176
|
+
const currentY = y.get();
|
|
171
177
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
178
|
+
// Update drag indicator rotation based on drag velocity
|
|
179
|
+
const velocity = y.getVelocity();
|
|
180
|
+
if (velocity > 0) indicatorRotation.set(10);
|
|
181
|
+
if (velocity < 0) indicatorRotation.set(-10);
|
|
175
182
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
currentY,
|
|
180
|
-
sheetHeight,
|
|
181
|
-
detent,
|
|
182
|
-
});
|
|
183
|
+
// Make sure user cannot drag beyond the top of the sheet
|
|
184
|
+
y.set(Math.max(currentY + info.delta.y, 0));
|
|
185
|
+
});
|
|
183
186
|
|
|
184
|
-
|
|
187
|
+
const onDragStart = useStableCallback<DragHandler>((event, info) => {
|
|
188
|
+
blurActiveInput();
|
|
189
|
+
onDragStartProp?.(event, info);
|
|
190
|
+
});
|
|
185
191
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
// Close if dragged over enough far
|
|
190
|
-
yTo = sheetHeight;
|
|
191
|
-
}
|
|
192
|
+
const onDragEnd = useStableCallback<DragHandler>((event, info) => {
|
|
193
|
+
blurActiveInput();
|
|
194
|
+
onDragEndProp?.(event, info);
|
|
192
195
|
|
|
193
|
-
|
|
194
|
-
animate(y, yTo, animationOptions);
|
|
196
|
+
const currentY = y.get();
|
|
195
197
|
|
|
196
|
-
|
|
197
|
-
if (yTo + 1 >= sheetHeight) {
|
|
198
|
-
onClose();
|
|
199
|
-
}
|
|
200
|
-
}
|
|
198
|
+
let yTo = 0;
|
|
201
199
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
});
|
|
200
|
+
const currentSnapPoint =
|
|
201
|
+
currentSnap !== undefined ? getSnapPoint(currentSnap) : null;
|
|
205
202
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
}, [isOpen]);
|
|
203
|
+
if (currentSnapPoint) {
|
|
204
|
+
const dragOffsetDirection = info.offset.y > 0 ? 'down' : 'up';
|
|
205
|
+
const dragVelocityDirection = info.velocity.y > 0 ? 'down' : 'up';
|
|
206
|
+
const isHighVelocity =
|
|
207
|
+
Math.abs(info.velocity.y) > dragVelocityThreshold;
|
|
212
208
|
|
|
213
|
-
|
|
214
|
-
useEffect(() => {
|
|
215
|
-
if (!isOpen) return; // let `AnimatePresence` handle the exit animation
|
|
209
|
+
let result: { yTo: number; snapIndex: number | undefined };
|
|
216
210
|
|
|
217
|
-
|
|
211
|
+
if (isHighVelocity) {
|
|
212
|
+
result = handleHighVelocityDrag({
|
|
213
|
+
snapPoints,
|
|
214
|
+
dragDirection: dragVelocityDirection,
|
|
215
|
+
});
|
|
216
|
+
} else {
|
|
217
|
+
result = handleLowVelocityDrag({
|
|
218
|
+
currentSnapPoint,
|
|
219
|
+
currentY,
|
|
220
|
+
dragDirection: dragOffsetDirection,
|
|
221
|
+
snapPoints,
|
|
222
|
+
velocity: info.velocity.y,
|
|
223
|
+
});
|
|
224
|
+
}
|
|
218
225
|
|
|
219
|
-
|
|
220
|
-
const snapIndex = initialSnap;
|
|
221
|
-
const sheetHeight = getSheetHeight(sheetRef);
|
|
222
|
-
const snapPoints = getSnapPoints({ snapPointsProp, sheetHeight });
|
|
223
|
-
const snapY = getSnapY({ sheetHeight, snapPoints, snapIndex });
|
|
226
|
+
yTo = result.yTo;
|
|
224
227
|
|
|
225
|
-
|
|
226
|
-
|
|
228
|
+
// If disableDismiss is true, prevent closing via gesture
|
|
229
|
+
if (disableDismiss && yTo + 1 >= sheetHeight) {
|
|
230
|
+
// Use the bottom-most open snap point
|
|
231
|
+
const bottomSnapPoint = snapPoints.find((s) => s.snapValue > 0);
|
|
232
|
+
|
|
233
|
+
if (bottomSnapPoint) {
|
|
234
|
+
yTo = bottomSnapPoint.snapValueY;
|
|
235
|
+
updateSnap(bottomSnapPoint.snapIndex);
|
|
236
|
+
} else {
|
|
237
|
+
// If no open snap points available, stay at current position
|
|
238
|
+
yTo = currentY;
|
|
239
|
+
}
|
|
240
|
+
} else if (result.snapIndex !== undefined) {
|
|
241
|
+
updateSnap(result.snapIndex);
|
|
242
|
+
}
|
|
243
|
+
} else if (
|
|
244
|
+
info.velocity.y > dragVelocityThreshold ||
|
|
245
|
+
currentY > sheetHeight * dragCloseThreshold
|
|
246
|
+
) {
|
|
247
|
+
// Close the sheet if dragged past the threshold or if the velocity is high enough
|
|
248
|
+
// But only if disableDismiss is false
|
|
249
|
+
if (disableDismiss) {
|
|
250
|
+
// If disableDismiss, snap back to the open position
|
|
251
|
+
yTo = 0;
|
|
252
|
+
} else {
|
|
253
|
+
yTo = closedY;
|
|
227
254
|
}
|
|
228
255
|
}
|
|
229
256
|
|
|
257
|
+
// Update the spring value so that the sheet is animated to the snap point
|
|
230
258
|
animate(y, yTo, animationOptions);
|
|
231
|
-
}, [isOpen]);
|
|
232
|
-
|
|
233
|
-
useImperativeHandle(ref, () => ({
|
|
234
|
-
y,
|
|
235
|
-
snapTo: (snapIndex: number) => {
|
|
236
|
-
if (!snapPointsProp) {
|
|
237
|
-
console.warn('Snapping is not possible without `snapPoints` prop.');
|
|
238
|
-
return;
|
|
239
|
-
}
|
|
240
259
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
260
|
+
// +1px for imprecision tolerance
|
|
261
|
+
// Only call onClose if disableDismiss is false or if we're actually closing
|
|
262
|
+
if (yTo + 1 >= sheetHeight && !disableDismiss) {
|
|
263
|
+
onClose();
|
|
264
|
+
}
|
|
244
265
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
266
|
+
// Reset indicator rotation after dragging
|
|
267
|
+
indicatorRotation.set(0);
|
|
268
|
+
});
|
|
248
269
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
},
|
|
270
|
+
useImperativeHandle(ref, () => ({
|
|
271
|
+
y,
|
|
272
|
+
yInverted,
|
|
273
|
+
height: sheetHeight,
|
|
274
|
+
snapTo,
|
|
255
275
|
}));
|
|
256
276
|
|
|
257
|
-
if (rootId) {
|
|
258
|
-
console.warn(
|
|
259
|
-
'The `rootId` prop is deprecated and will be removed in the next major version. Use `modalEffectRootId` instead.'
|
|
260
|
-
);
|
|
261
|
-
}
|
|
262
|
-
|
|
263
277
|
useModalEffect({
|
|
264
278
|
y,
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
279
|
+
detent,
|
|
280
|
+
sheetHeight,
|
|
281
|
+
snapPoints,
|
|
282
|
+
rootId: modalEffectRootId,
|
|
268
283
|
startThreshold: modalEffectThreshold,
|
|
269
284
|
});
|
|
270
285
|
|
|
271
|
-
|
|
272
|
-
|
|
286
|
+
/**
|
|
287
|
+
* Motion should handle body scroll locking but it's not working properly on iOS.
|
|
288
|
+
* Scroll locking from React Aria seems to work much better 🤷♂️
|
|
289
|
+
*/
|
|
273
290
|
usePreventScroll({
|
|
274
291
|
isDisabled: disableScrollLocking || !isOpen,
|
|
275
292
|
});
|
|
276
293
|
|
|
277
|
-
const
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
dragMomentum: false,
|
|
282
|
-
dragPropagation: false,
|
|
283
|
-
onDrag,
|
|
284
|
-
onDragStart,
|
|
285
|
-
onDragEnd,
|
|
286
|
-
};
|
|
294
|
+
const state = useSheetState({
|
|
295
|
+
isOpen,
|
|
296
|
+
onOpen: async () => {
|
|
297
|
+
onOpenStart?.();
|
|
287
298
|
|
|
288
|
-
|
|
289
|
-
|
|
299
|
+
const initialSnapPoint =
|
|
300
|
+
initialSnap !== undefined ? getSnapPoint(initialSnap) : null;
|
|
301
|
+
|
|
302
|
+
const yTo = initialSnapPoint?.snapValueY ?? 0;
|
|
303
|
+
|
|
304
|
+
await animate(y, yTo, animationOptions);
|
|
305
|
+
|
|
306
|
+
if (initialSnap !== undefined) {
|
|
307
|
+
updateSnap(initialSnap);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
onOpenEnd?.();
|
|
311
|
+
},
|
|
312
|
+
onClosing: async () => {
|
|
313
|
+
onCloseStart?.();
|
|
314
|
+
|
|
315
|
+
await animate(y, closedY, animationOptions);
|
|
316
|
+
|
|
317
|
+
onCloseEnd?.();
|
|
318
|
+
},
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
const dragProps: SheetContextType['dragProps'] = {
|
|
322
|
+
drag: 'y',
|
|
323
|
+
dragElastic: 0,
|
|
324
|
+
dragMomentum: false,
|
|
325
|
+
dragPropagation: false,
|
|
326
|
+
onDrag,
|
|
327
|
+
onDragStart,
|
|
328
|
+
onDragEnd,
|
|
329
|
+
};
|
|
290
330
|
|
|
291
331
|
const context: SheetContextType = {
|
|
292
|
-
|
|
293
|
-
sheetRef,
|
|
294
|
-
isOpen,
|
|
295
|
-
initialSnap,
|
|
332
|
+
currentSnap,
|
|
296
333
|
detent,
|
|
297
|
-
indicatorRotation,
|
|
298
|
-
callbacks,
|
|
299
|
-
dragProps,
|
|
300
|
-
windowHeight,
|
|
301
|
-
animationOptions,
|
|
302
|
-
reduceMotion,
|
|
303
334
|
disableDrag,
|
|
335
|
+
dragProps,
|
|
336
|
+
indicatorRotation,
|
|
337
|
+
avoidKeyboard,
|
|
338
|
+
sheetBoundsRef,
|
|
339
|
+
sheetRef,
|
|
340
|
+
y,
|
|
304
341
|
};
|
|
305
342
|
|
|
306
343
|
const sheet = (
|
|
@@ -308,18 +345,11 @@ export const Sheet = forwardRef<any, SheetProps>(
|
|
|
308
345
|
<motion.div
|
|
309
346
|
{...rest}
|
|
310
347
|
ref={ref}
|
|
311
|
-
|
|
348
|
+
data-sheet-state={state}
|
|
349
|
+
className={`react-modal-sheet-root ${className}`}
|
|
350
|
+
style={{ ...styles.root, zIndex, visibility, ...style }}
|
|
312
351
|
>
|
|
313
|
-
|
|
314
|
-
{/* NOTE: AnimatePresence requires us to set keys to children */}
|
|
315
|
-
{isOpen ? (
|
|
316
|
-
<SheetScrollerContextProvider>
|
|
317
|
-
{Children.map(children, (child: any, i) =>
|
|
318
|
-
cloneElement(child, { key: `sheet-child-${i}` })
|
|
319
|
-
)}
|
|
320
|
-
</SheetScrollerContextProvider>
|
|
321
|
-
) : null}
|
|
322
|
-
</AnimatePresence>
|
|
352
|
+
{state !== 'closed' ? children : null}
|
|
323
353
|
</motion.div>
|
|
324
354
|
</SheetContext.Provider>
|
|
325
355
|
);
|