react-modal-sheet 4.4.0 → 5.1.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 +332 -188
- package/dist/index.d.mts +51 -39
- package/dist/index.d.ts +51 -39
- package/dist/index.js +854 -585
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +847 -579
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -3
- package/src/SheetBackdrop.tsx +19 -6
- package/src/SheetContainer.tsx +34 -25
- package/src/SheetContent.tsx +77 -14
- package/src/SheetDragIndicator.tsx +52 -0
- package/src/SheetHeader.tsx +30 -30
- 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 +14 -10
- package/src/sheet.tsx +247 -209
- package/src/snap.ts +226 -0
- package/src/styles.ts +94 -53
- package/src/types.tsx +83 -79
- package/src/utils.ts +8 -149
- package/src/SheetScroller.tsx +0 -96
- package/src/hooks/use-event-callbacks.ts +0 -44
package/dist/index.mjs
CHANGED
|
@@ -1,12 +1,125 @@
|
|
|
1
|
+
import { motion, useMotionValue, useTransform, useReducedMotion, animate } from 'motion/react';
|
|
2
|
+
import React4, { createContext, forwardRef, useRef, useState, useImperativeHandle, useContext, useCallback, useEffect, useLayoutEffect } from 'react';
|
|
1
3
|
import { createPortal } from 'react-dom';
|
|
2
|
-
import
|
|
3
|
-
import { useMotionValue, useReducedMotion, useTransform, animate, motion, AnimatePresence } from 'motion/react';
|
|
4
|
+
import useMeasure from 'react-use-measure';
|
|
4
5
|
import { transform } from 'motion';
|
|
5
6
|
|
|
6
|
-
// src/
|
|
7
|
+
// src/SheetBackdrop.tsx
|
|
8
|
+
var SheetContext = createContext(
|
|
9
|
+
void 0
|
|
10
|
+
);
|
|
11
|
+
function useSheetContext() {
|
|
12
|
+
const context = useContext(SheetContext);
|
|
13
|
+
if (!context) throw new Error("Sheet context error");
|
|
14
|
+
return context;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// src/styles.ts
|
|
18
|
+
var styles = {
|
|
19
|
+
root: {
|
|
20
|
+
base: {
|
|
21
|
+
position: "fixed",
|
|
22
|
+
top: 0,
|
|
23
|
+
bottom: 0,
|
|
24
|
+
left: 0,
|
|
25
|
+
right: 0,
|
|
26
|
+
overflow: "hidden",
|
|
27
|
+
pointerEvents: "none"
|
|
28
|
+
},
|
|
29
|
+
decorative: {}
|
|
30
|
+
},
|
|
31
|
+
backdrop: {
|
|
32
|
+
base: {
|
|
33
|
+
zIndex: 1,
|
|
34
|
+
position: "fixed",
|
|
35
|
+
top: 0,
|
|
36
|
+
left: 0,
|
|
37
|
+
width: "100%",
|
|
38
|
+
height: "100%",
|
|
39
|
+
touchAction: "none",
|
|
40
|
+
userSelect: "none"
|
|
41
|
+
},
|
|
42
|
+
decorative: {
|
|
43
|
+
backgroundColor: "rgba(0, 0, 0, 0.2)",
|
|
44
|
+
border: "none",
|
|
45
|
+
WebkitTapHighlightColor: "transparent"
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
container: {
|
|
49
|
+
base: {
|
|
50
|
+
zIndex: 2,
|
|
51
|
+
position: "absolute",
|
|
52
|
+
left: 0,
|
|
53
|
+
bottom: 0,
|
|
54
|
+
width: "100%",
|
|
55
|
+
pointerEvents: "auto",
|
|
56
|
+
display: "flex",
|
|
57
|
+
flexDirection: "column"
|
|
58
|
+
},
|
|
59
|
+
decorative: {
|
|
60
|
+
backgroundColor: "#fff",
|
|
61
|
+
borderTopRightRadius: "8px",
|
|
62
|
+
borderTopLeftRadius: "8px",
|
|
63
|
+
boxShadow: "0px -2px 16px rgba(0, 0, 0, 0.3)"
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
headerWrapper: {
|
|
67
|
+
base: {
|
|
68
|
+
width: "100%"
|
|
69
|
+
},
|
|
70
|
+
decorative: {}
|
|
71
|
+
},
|
|
72
|
+
header: {
|
|
73
|
+
base: {
|
|
74
|
+
width: "100%",
|
|
75
|
+
position: "relative"
|
|
76
|
+
},
|
|
77
|
+
decorative: {
|
|
78
|
+
height: "40px",
|
|
79
|
+
display: "flex",
|
|
80
|
+
alignItems: "center",
|
|
81
|
+
justifyContent: "center"
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
indicatorWrapper: {
|
|
85
|
+
base: {
|
|
86
|
+
display: "flex"
|
|
87
|
+
},
|
|
88
|
+
decorative: {}
|
|
89
|
+
},
|
|
90
|
+
indicator: {
|
|
91
|
+
base: {
|
|
92
|
+
display: "inline-block"
|
|
93
|
+
},
|
|
94
|
+
decorative: {
|
|
95
|
+
width: "18px",
|
|
96
|
+
height: "4px",
|
|
97
|
+
borderRadius: "99px",
|
|
98
|
+
backgroundColor: "#ddd"
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
content: {
|
|
102
|
+
base: {
|
|
103
|
+
minHeight: "0px",
|
|
104
|
+
position: "relative",
|
|
105
|
+
flexGrow: 1,
|
|
106
|
+
display: "flex",
|
|
107
|
+
flexDirection: "column"
|
|
108
|
+
},
|
|
109
|
+
decorative: {}
|
|
110
|
+
},
|
|
111
|
+
scroller: {
|
|
112
|
+
base: {
|
|
113
|
+
height: "100%",
|
|
114
|
+
overflowY: "auto",
|
|
115
|
+
overscrollBehaviorY: "none"
|
|
116
|
+
},
|
|
117
|
+
decorative: {}
|
|
118
|
+
}
|
|
119
|
+
};
|
|
7
120
|
|
|
8
121
|
// src/constants.ts
|
|
9
|
-
var
|
|
122
|
+
var DEFAULT_HEIGHT = "calc(100% - env(safe-area-inset-top) - 34px)";
|
|
10
123
|
var IS_SSR = typeof window === "undefined";
|
|
11
124
|
var DEFAULT_TWEEN_CONFIG = {
|
|
12
125
|
ease: "easeOut",
|
|
@@ -20,96 +133,15 @@ var DEFAULT_DRAG_CLOSE_THRESHOLD = 0.6;
|
|
|
20
133
|
var DEFAULT_DRAG_VELOCITY_THRESHOLD = 500;
|
|
21
134
|
|
|
22
135
|
// src/utils.ts
|
|
23
|
-
function
|
|
24
|
-
|
|
25
|
-
if (!sheetEl) {
|
|
26
|
-
console.warn(
|
|
27
|
-
"Sheet height is not available because the sheet element is not mounted yet."
|
|
28
|
-
);
|
|
29
|
-
return 0;
|
|
30
|
-
}
|
|
31
|
-
return Math.round(sheetEl.getBoundingClientRect().height);
|
|
32
|
-
}
|
|
33
|
-
function getSnapPoints({
|
|
34
|
-
snapPointsProp,
|
|
35
|
-
sheetHeight
|
|
36
|
-
}) {
|
|
37
|
-
const snapPointValues = snapPointsProp.map((point) => {
|
|
38
|
-
if (point > 0 && point <= 1) {
|
|
39
|
-
return Math.round(point * sheetHeight);
|
|
40
|
-
}
|
|
41
|
-
return point < 0 ? sheetHeight + point : point;
|
|
42
|
-
});
|
|
43
|
-
console.assert(
|
|
44
|
-
inDescendingOrder(snapPointValues) || sheetHeight === 0,
|
|
45
|
-
`Snap points need to be in descending order got: [${snapPointsProp.join(", ")}]`
|
|
46
|
-
);
|
|
47
|
-
return snapPointValues;
|
|
48
|
-
}
|
|
49
|
-
function getClosestSnapPoint({
|
|
50
|
-
snapPoints,
|
|
51
|
-
currentY,
|
|
52
|
-
sheetHeight,
|
|
53
|
-
detent
|
|
54
|
-
}) {
|
|
55
|
-
const snapInverse = snapPoints.map(
|
|
56
|
-
(p) => sheetHeight - Math.min(p, sheetHeight)
|
|
57
|
-
);
|
|
58
|
-
if (detent === "content-height" && !snapInverse.includes(0)) {
|
|
59
|
-
snapInverse.unshift(0);
|
|
60
|
-
}
|
|
61
|
-
const snapTo = getClosest(snapInverse, currentY);
|
|
62
|
-
const snapIndex = snapInverse.indexOf(snapTo);
|
|
63
|
-
const snapY = validateSnapTo({
|
|
64
|
-
snapTo: getClosest(snapInverse, currentY),
|
|
65
|
-
sheetHeight
|
|
66
|
-
});
|
|
67
|
-
return { snapY, snapIndex };
|
|
68
|
-
}
|
|
69
|
-
function getClosest(nums, goal) {
|
|
70
|
-
let closest = nums[0];
|
|
71
|
-
let minDifference = Math.abs(nums[0] - goal);
|
|
72
|
-
for (let i = 1; i < nums.length; i++) {
|
|
73
|
-
const difference = Math.abs(nums[i] - goal);
|
|
74
|
-
if (difference < minDifference) {
|
|
75
|
-
closest = nums[i];
|
|
76
|
-
minDifference = difference;
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
return closest;
|
|
136
|
+
function applyStyles(styles2, unstyled) {
|
|
137
|
+
return unstyled ? styles2.base : { ...styles2.base, ...styles2.decorative };
|
|
80
138
|
}
|
|
81
|
-
function
|
|
139
|
+
function isAscendingOrder(arr) {
|
|
82
140
|
for (let i = 0; i < arr.length; i++) {
|
|
83
|
-
if (arr[i + 1]
|
|
141
|
+
if (arr[i + 1] < arr[i]) return false;
|
|
84
142
|
}
|
|
85
143
|
return true;
|
|
86
144
|
}
|
|
87
|
-
function getSnapY({
|
|
88
|
-
sheetHeight,
|
|
89
|
-
snapPoints,
|
|
90
|
-
snapIndex
|
|
91
|
-
}) {
|
|
92
|
-
const snapPoint = snapPoints[snapIndex];
|
|
93
|
-
if (snapPoint === void 0) {
|
|
94
|
-
console.warn(
|
|
95
|
-
`Invalid snap index ${snapIndex}. Snap points are: [${snapPoints.join(", ")}]`
|
|
96
|
-
);
|
|
97
|
-
return null;
|
|
98
|
-
}
|
|
99
|
-
const y = validateSnapTo({ snapTo: sheetHeight - snapPoint, sheetHeight });
|
|
100
|
-
return y;
|
|
101
|
-
}
|
|
102
|
-
function validateSnapTo({
|
|
103
|
-
snapTo,
|
|
104
|
-
sheetHeight
|
|
105
|
-
}) {
|
|
106
|
-
if (snapTo < 0) {
|
|
107
|
-
console.warn(
|
|
108
|
-
`Snap point is out of bounds. Sheet height is ${sheetHeight} but snap point is ${sheetHeight + Math.abs(snapTo)}.`
|
|
109
|
-
);
|
|
110
|
-
}
|
|
111
|
-
return Math.max(Math.round(snapTo), 0);
|
|
112
|
-
}
|
|
113
145
|
function mergeRefs(refs) {
|
|
114
146
|
return (value) => {
|
|
115
147
|
refs.forEach((ref) => {
|
|
@@ -121,10 +153,6 @@ function mergeRefs(refs) {
|
|
|
121
153
|
});
|
|
122
154
|
};
|
|
123
155
|
}
|
|
124
|
-
function isTouchDevice() {
|
|
125
|
-
if (IS_SSR) return false;
|
|
126
|
-
return "ontouchstart" in window || navigator.maxTouchPoints > 0;
|
|
127
|
-
}
|
|
128
156
|
function testPlatform(re) {
|
|
129
157
|
var _a;
|
|
130
158
|
return typeof window !== "undefined" && window.navigator != null ? re.test(
|
|
@@ -153,109 +181,287 @@ var isIPad = cached(function() {
|
|
|
153
181
|
var isIOS = cached(function() {
|
|
154
182
|
return isIPhone() || isIPad();
|
|
155
183
|
});
|
|
156
|
-
|
|
157
|
-
|
|
184
|
+
|
|
185
|
+
// src/SheetBackdrop.tsx
|
|
186
|
+
var isClickable = (props) => !!props.onClick || !!props.onTap;
|
|
187
|
+
var SheetBackdrop = forwardRef(
|
|
188
|
+
({ style, className = "", unstyled, ...rest }, ref) => {
|
|
189
|
+
const sheetContext = useSheetContext();
|
|
190
|
+
const clickable = isClickable(rest);
|
|
191
|
+
const Comp = clickable ? motion.button : motion.div;
|
|
192
|
+
const pointerEvents = clickable ? "auto" : "none";
|
|
193
|
+
const isUnstyled = unstyled ?? sheetContext.unstyled;
|
|
194
|
+
const backdropStyle = {
|
|
195
|
+
...applyStyles(styles.backdrop, isUnstyled),
|
|
196
|
+
...style,
|
|
197
|
+
pointerEvents
|
|
198
|
+
};
|
|
199
|
+
return /* @__PURE__ */ React4.createElement(
|
|
200
|
+
Comp,
|
|
201
|
+
{
|
|
202
|
+
...rest,
|
|
203
|
+
ref,
|
|
204
|
+
className: `react-modal-sheet-backdrop ${className}`,
|
|
205
|
+
style: backdropStyle,
|
|
206
|
+
initial: { opacity: 0 },
|
|
207
|
+
animate: { opacity: 1 },
|
|
208
|
+
exit: { opacity: 0 },
|
|
209
|
+
transition: { duration: 1 }
|
|
210
|
+
}
|
|
211
|
+
);
|
|
212
|
+
}
|
|
158
213
|
);
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
214
|
+
SheetBackdrop.displayName = "SheetBackdrop";
|
|
215
|
+
var SheetContainer = forwardRef(
|
|
216
|
+
({ children, style, className = "", unstyled, ...rest }, ref) => {
|
|
217
|
+
const sheetContext = useSheetContext();
|
|
218
|
+
const isUnstyled = unstyled ?? sheetContext.unstyled;
|
|
219
|
+
const containerStyle = {
|
|
220
|
+
...applyStyles(styles.container, isUnstyled),
|
|
221
|
+
...style,
|
|
222
|
+
y: sheetContext.y
|
|
223
|
+
};
|
|
224
|
+
if (sheetContext.detent === "default") {
|
|
225
|
+
containerStyle.height = DEFAULT_HEIGHT;
|
|
226
|
+
}
|
|
227
|
+
if (sheetContext.detent === "full") {
|
|
228
|
+
containerStyle.height = "100%";
|
|
229
|
+
containerStyle.maxHeight = "100%";
|
|
230
|
+
}
|
|
231
|
+
if (sheetContext.detent === "content") {
|
|
232
|
+
containerStyle.height = "auto";
|
|
233
|
+
containerStyle.maxHeight = DEFAULT_HEIGHT;
|
|
234
|
+
}
|
|
235
|
+
return /* @__PURE__ */ React4.createElement(
|
|
236
|
+
motion.div,
|
|
237
|
+
{
|
|
238
|
+
...rest,
|
|
239
|
+
ref: mergeRefs([
|
|
240
|
+
ref,
|
|
241
|
+
sheetContext.sheetRef,
|
|
242
|
+
sheetContext.sheetBoundsRef
|
|
243
|
+
]),
|
|
244
|
+
className: `react-modal-sheet-container ${className}`,
|
|
245
|
+
style: containerStyle
|
|
246
|
+
},
|
|
247
|
+
children
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
);
|
|
251
|
+
SheetContainer.displayName = "SheetContainer";
|
|
252
|
+
var constraints = { bottom: 0, top: 0, left: 0, right: 0 };
|
|
253
|
+
function useDragConstraints() {
|
|
254
|
+
const ref = useRef(null);
|
|
255
|
+
const onMeasure = useCallback(() => constraints, []);
|
|
256
|
+
return { ref, onMeasure };
|
|
257
|
+
}
|
|
258
|
+
function useScrollPosition() {
|
|
259
|
+
const ref = useRef(null);
|
|
260
|
+
const [scrollPosition, setScrollPosition] = useState(void 0);
|
|
261
|
+
useEffect(() => {
|
|
262
|
+
const element = ref.current;
|
|
263
|
+
if (!element) return;
|
|
264
|
+
let scrollTimeout = null;
|
|
265
|
+
function determineScrollPosition(element2) {
|
|
266
|
+
const { scrollTop, scrollHeight, clientHeight } = element2;
|
|
267
|
+
const isScrollable2 = scrollHeight > clientHeight;
|
|
268
|
+
if (!isScrollable2) {
|
|
269
|
+
if (scrollPosition) setScrollPosition(void 0);
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
const isAtTop = scrollTop <= 0;
|
|
273
|
+
const isAtBottom = Math.ceil(scrollHeight) - Math.ceil(scrollTop) === Math.ceil(clientHeight);
|
|
274
|
+
let position;
|
|
275
|
+
if (isAtTop) {
|
|
276
|
+
position = "top";
|
|
277
|
+
} else if (isAtBottom) {
|
|
278
|
+
position = "bottom";
|
|
279
|
+
} else {
|
|
280
|
+
position = "middle";
|
|
281
|
+
}
|
|
282
|
+
if (position === scrollPosition) return;
|
|
283
|
+
setScrollPosition(position);
|
|
284
|
+
}
|
|
285
|
+
function onScroll(event) {
|
|
286
|
+
if (event.currentTarget instanceof HTMLDivElement) {
|
|
287
|
+
const el = event.currentTarget;
|
|
288
|
+
if (scrollTimeout) clearTimeout(scrollTimeout);
|
|
289
|
+
scrollTimeout = setTimeout(() => determineScrollPosition(el), 32);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
function onTouchStart(event) {
|
|
293
|
+
if (event.currentTarget instanceof HTMLDivElement) {
|
|
294
|
+
determineScrollPosition(event.currentTarget);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
determineScrollPosition(element);
|
|
298
|
+
element.addEventListener("scroll", onScroll);
|
|
299
|
+
element.addEventListener("touchstart", onTouchStart);
|
|
300
|
+
return () => {
|
|
301
|
+
if (scrollTimeout) clearTimeout(scrollTimeout);
|
|
302
|
+
element.removeEventListener("scroll", onScroll);
|
|
303
|
+
element.removeEventListener("touchstart", onTouchStart);
|
|
304
|
+
};
|
|
305
|
+
}, []);
|
|
306
|
+
return { ref, scrollPosition };
|
|
163
307
|
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
308
|
+
|
|
309
|
+
// src/SheetContent.tsx
|
|
310
|
+
var SheetContent = forwardRef(
|
|
311
|
+
({
|
|
312
|
+
disableScroll: disableScrollProp,
|
|
313
|
+
disableDrag: disableDragProp,
|
|
314
|
+
children,
|
|
315
|
+
style: styleProp,
|
|
316
|
+
className = "",
|
|
317
|
+
scrollRef: scrollRefProp = null,
|
|
318
|
+
unstyled,
|
|
319
|
+
...rest
|
|
320
|
+
}, ref) => {
|
|
321
|
+
const sheetContext = useSheetContext();
|
|
322
|
+
const dragConstraints = useDragConstraints();
|
|
323
|
+
const scroll = useScrollPosition();
|
|
324
|
+
const disableScroll = typeof disableScrollProp === "function" ? disableScrollProp({
|
|
325
|
+
scrollPosition: scroll.scrollPosition,
|
|
326
|
+
currentSnap: sheetContext.currentSnap
|
|
327
|
+
}) : Boolean(disableScrollProp);
|
|
328
|
+
const disableDragDueToScroll = !disableScroll && scroll.scrollPosition && scroll.scrollPosition !== "top";
|
|
329
|
+
const disableDragDueToProp = typeof disableDragProp === "function" ? disableDragProp({
|
|
330
|
+
scrollPosition: scroll.scrollPosition,
|
|
331
|
+
currentSnap: sheetContext.currentSnap
|
|
332
|
+
}) : Boolean(disableDragProp);
|
|
333
|
+
const disableDrag = disableDragDueToProp || disableDragDueToScroll || sheetContext.disableDrag;
|
|
334
|
+
const dragProps = disableDrag || sheetContext.disableDrag ? void 0 : sheetContext.dragProps;
|
|
335
|
+
const isUnstyled = unstyled ?? sheetContext.unstyled;
|
|
336
|
+
const contentStyle = {
|
|
337
|
+
...applyStyles(styles.content, isUnstyled),
|
|
338
|
+
...styleProp
|
|
339
|
+
};
|
|
340
|
+
const scrollStyle = applyStyles(styles.scroller, isUnstyled);
|
|
341
|
+
if (sheetContext.avoidKeyboard) {
|
|
342
|
+
scrollStyle.paddingBottom = "env(keyboard-inset-height, var(--keyboard-inset-height, 0px))";
|
|
343
|
+
}
|
|
344
|
+
if (disableScroll) {
|
|
345
|
+
scrollStyle.overflowY = "hidden";
|
|
346
|
+
}
|
|
347
|
+
return /* @__PURE__ */ React4.createElement(
|
|
348
|
+
motion.div,
|
|
349
|
+
{
|
|
350
|
+
...rest,
|
|
351
|
+
ref: mergeRefs([ref, dragConstraints.ref]),
|
|
352
|
+
className: `react-modal-sheet-content ${className}`,
|
|
353
|
+
style: contentStyle,
|
|
354
|
+
...dragProps,
|
|
355
|
+
dragConstraints: dragConstraints.ref,
|
|
356
|
+
onMeasureDragConstraints: dragConstraints.onMeasure
|
|
357
|
+
},
|
|
358
|
+
/* @__PURE__ */ React4.createElement(
|
|
359
|
+
motion.div,
|
|
360
|
+
{
|
|
361
|
+
ref: mergeRefs([scroll.ref, scrollRefProp]),
|
|
362
|
+
style: scrollStyle,
|
|
363
|
+
className: "react-modal-sheet-content-scroller"
|
|
364
|
+
},
|
|
365
|
+
children
|
|
366
|
+
)
|
|
367
|
+
);
|
|
368
|
+
}
|
|
369
|
+
);
|
|
370
|
+
SheetContent.displayName = "SheetContent";
|
|
371
|
+
function SheetDragIndicator({
|
|
372
|
+
style,
|
|
373
|
+
className = "",
|
|
374
|
+
unstyled,
|
|
375
|
+
...rest
|
|
167
376
|
}) {
|
|
168
377
|
const sheetContext = useSheetContext();
|
|
169
|
-
const
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
378
|
+
const indicator1Transform = useTransform(
|
|
379
|
+
sheetContext.indicatorRotation,
|
|
380
|
+
(r) => `translateX(2px) rotate(${r}deg)`
|
|
381
|
+
);
|
|
382
|
+
const indicator2Transform = useTransform(
|
|
383
|
+
sheetContext.indicatorRotation,
|
|
384
|
+
(r) => `translateX(-2px) rotate(${ -1 * r}deg)`
|
|
385
|
+
);
|
|
386
|
+
const isUnstyled = unstyled ?? sheetContext.unstyled;
|
|
387
|
+
const indicatorWrapperStyle = {
|
|
388
|
+
...applyStyles(styles.indicatorWrapper, isUnstyled),
|
|
389
|
+
...style
|
|
390
|
+
};
|
|
391
|
+
const indicatorStyle = applyStyles(styles.indicator, isUnstyled);
|
|
392
|
+
return /* @__PURE__ */ React4.createElement(
|
|
393
|
+
"div",
|
|
178
394
|
{
|
|
179
|
-
|
|
395
|
+
className: `react-modal-sheet-drag-indicator-container ${className}`,
|
|
396
|
+
style: indicatorWrapperStyle,
|
|
397
|
+
...rest
|
|
180
398
|
},
|
|
181
|
-
|
|
399
|
+
/* @__PURE__ */ React4.createElement(
|
|
400
|
+
motion.span,
|
|
401
|
+
{
|
|
402
|
+
className: "react-modal-sheet-drag-indicator",
|
|
403
|
+
style: { ...indicatorStyle, transform: indicator1Transform }
|
|
404
|
+
}
|
|
405
|
+
),
|
|
406
|
+
/* @__PURE__ */ React4.createElement(
|
|
407
|
+
motion.span,
|
|
408
|
+
{
|
|
409
|
+
className: "react-modal-sheet-drag-indicator",
|
|
410
|
+
style: { ...indicatorStyle, transform: indicator2Transform }
|
|
411
|
+
}
|
|
412
|
+
)
|
|
182
413
|
);
|
|
183
414
|
}
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
backgroundColor: "rgba(0, 0, 0, 0.2)",
|
|
209
|
-
touchAction: "none",
|
|
210
|
-
border: "none",
|
|
211
|
-
userSelect: "none",
|
|
212
|
-
WebkitTapHighlightColor: "transparent"
|
|
213
|
-
},
|
|
214
|
-
container: {
|
|
215
|
-
zIndex: 2,
|
|
216
|
-
position: "absolute",
|
|
217
|
-
left: 0,
|
|
218
|
-
bottom: 0,
|
|
219
|
-
width: "100%",
|
|
220
|
-
backgroundColor: "#fff",
|
|
221
|
-
borderTopRightRadius: "8px",
|
|
222
|
-
borderTopLeftRadius: "8px",
|
|
223
|
-
boxShadow: "0px -2px 16px rgba(0, 0, 0, 0.3)",
|
|
224
|
-
display: "flex",
|
|
225
|
-
flexDirection: "column",
|
|
226
|
-
pointerEvents: "auto"
|
|
227
|
-
},
|
|
228
|
-
headerWrapper: {
|
|
229
|
-
width: "100%"
|
|
230
|
-
},
|
|
231
|
-
header: {
|
|
232
|
-
height: "40px",
|
|
233
|
-
width: "100%",
|
|
234
|
-
position: "relative",
|
|
235
|
-
display: "flex",
|
|
236
|
-
alignItems: "center",
|
|
237
|
-
justifyContent: "center"
|
|
238
|
-
},
|
|
239
|
-
indicator: {
|
|
240
|
-
width: "18px",
|
|
241
|
-
height: "4px",
|
|
242
|
-
borderRadius: "99px",
|
|
243
|
-
backgroundColor: "#ddd"
|
|
244
|
-
},
|
|
245
|
-
content: {
|
|
246
|
-
flexGrow: 1,
|
|
247
|
-
display: "flex",
|
|
248
|
-
flexDirection: "column",
|
|
249
|
-
minHeight: "0px",
|
|
250
|
-
position: "relative"
|
|
251
|
-
},
|
|
252
|
-
scroller: {
|
|
253
|
-
height: "100%",
|
|
254
|
-
overflowY: "auto",
|
|
255
|
-
overscrollBehaviorY: "none"
|
|
415
|
+
var SheetHeader = forwardRef(
|
|
416
|
+
({ children, style, disableDrag, unstyled, className = "", ...rest }, ref) => {
|
|
417
|
+
const sheetContext = useSheetContext();
|
|
418
|
+
const dragConstraints = useDragConstraints();
|
|
419
|
+
const dragProps = disableDrag || sheetContext.disableDrag ? void 0 : sheetContext.dragProps;
|
|
420
|
+
const isUnstyled = unstyled ?? sheetContext.unstyled;
|
|
421
|
+
const headerWrapperStyle = {
|
|
422
|
+
...applyStyles(styles.headerWrapper, isUnstyled),
|
|
423
|
+
...style
|
|
424
|
+
};
|
|
425
|
+
const headerStyle = applyStyles(styles.header, isUnstyled);
|
|
426
|
+
return /* @__PURE__ */ React4.createElement(
|
|
427
|
+
motion.div,
|
|
428
|
+
{
|
|
429
|
+
...rest,
|
|
430
|
+
ref: mergeRefs([ref, dragConstraints.ref]),
|
|
431
|
+
style: headerWrapperStyle,
|
|
432
|
+
className: `react-modal-sheet-header-container ${className}`,
|
|
433
|
+
...dragProps,
|
|
434
|
+
dragConstraints: dragConstraints.ref,
|
|
435
|
+
onMeasureDragConstraints: dragConstraints.onMeasure
|
|
436
|
+
},
|
|
437
|
+
children || /* @__PURE__ */ React4.createElement("div", { className: "react-modal-sheet-header", style: headerStyle }, /* @__PURE__ */ React4.createElement(SheetDragIndicator, null))
|
|
438
|
+
);
|
|
256
439
|
}
|
|
257
|
-
|
|
440
|
+
);
|
|
441
|
+
SheetHeader.displayName = "SheetHeader";
|
|
258
442
|
var useIsomorphicLayoutEffect = IS_SSR ? useEffect : useLayoutEffect;
|
|
443
|
+
|
|
444
|
+
// src/hooks/use-dimensions.ts
|
|
445
|
+
function useDimensions() {
|
|
446
|
+
const [dimensions, setDimensions] = useState(() => ({
|
|
447
|
+
windowHeight: !IS_SSR ? window.innerHeight : 0,
|
|
448
|
+
windowWidth: !IS_SSR ? window.innerWidth : 0
|
|
449
|
+
}));
|
|
450
|
+
useIsomorphicLayoutEffect(() => {
|
|
451
|
+
function handler() {
|
|
452
|
+
setDimensions({
|
|
453
|
+
windowHeight: window.innerHeight,
|
|
454
|
+
windowWidth: window.innerWidth
|
|
455
|
+
});
|
|
456
|
+
}
|
|
457
|
+
handler();
|
|
458
|
+
window.addEventListener("resize", handler);
|
|
459
|
+
return () => {
|
|
460
|
+
window.removeEventListener("resize", handler);
|
|
461
|
+
};
|
|
462
|
+
}, []);
|
|
463
|
+
return dimensions;
|
|
464
|
+
}
|
|
259
465
|
function useSafeAreaInsets() {
|
|
260
466
|
const [insets] = useState(() => {
|
|
261
467
|
const fallback = { top: 0, left: 0, right: 0, bottom: 0 };
|
|
@@ -287,12 +493,18 @@ function getComputedValue(computed, property) {
|
|
|
287
493
|
// src/hooks/use-modal-effect.ts
|
|
288
494
|
function useModalEffect({
|
|
289
495
|
y,
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
496
|
+
detent,
|
|
497
|
+
rootId: _rootId,
|
|
498
|
+
sheetHeight,
|
|
499
|
+
snapPoints,
|
|
293
500
|
startThreshold
|
|
294
501
|
}) {
|
|
295
502
|
const insetTop = useSafeAreaInsets().top;
|
|
503
|
+
let rootId = _rootId;
|
|
504
|
+
if (rootId && detent === "full") {
|
|
505
|
+
console.warn('Using "full" detent with modal effect is not supported.');
|
|
506
|
+
rootId = void 0;
|
|
507
|
+
}
|
|
296
508
|
useIsomorphicLayoutEffect(() => {
|
|
297
509
|
return () => {
|
|
298
510
|
if (rootId) cleanupModalEffect(rootId);
|
|
@@ -302,18 +514,15 @@ function useModalEffect({
|
|
|
302
514
|
if (!rootId) return;
|
|
303
515
|
const root = document.querySelector(`#${rootId}`);
|
|
304
516
|
if (!root) return;
|
|
305
|
-
let sheetHeight = 0;
|
|
306
517
|
const removeStartListener = y.on("animationStart", () => {
|
|
307
|
-
sheetHeight = getSheetHeight(sheetRef);
|
|
308
518
|
setupModalEffect(rootId);
|
|
309
519
|
});
|
|
310
520
|
const removeChangeListener = y.on("change", (yValue) => {
|
|
311
521
|
if (!root) return;
|
|
312
522
|
let progress = Math.max(0, 1 - yValue / sheetHeight);
|
|
313
|
-
const
|
|
314
|
-
const snapThresholdPoint = snapPoints && snapPoints.length > 1 ? snapPoints[1] : void 0;
|
|
523
|
+
const snapThresholdPoint = snapPoints.length > 1 ? snapPoints[snapPoints.length - 2] : void 0;
|
|
315
524
|
if (snapThresholdPoint !== void 0) {
|
|
316
|
-
const snapThresholdValue =
|
|
525
|
+
const snapThresholdValue = snapThresholdPoint.snapValueY;
|
|
317
526
|
if (yValue <= snapThresholdValue) {
|
|
318
527
|
progress = (snapThresholdValue - yValue) / snapThresholdValue;
|
|
319
528
|
} else {
|
|
@@ -350,7 +559,7 @@ function useModalEffect({
|
|
|
350
559
|
removeCompleteListener();
|
|
351
560
|
removeCancelListener();
|
|
352
561
|
};
|
|
353
|
-
}, [y, rootId, insetTop, startThreshold,
|
|
562
|
+
}, [y, rootId, insetTop, startThreshold, sheetHeight]);
|
|
354
563
|
}
|
|
355
564
|
function setupModalEffect(rootId) {
|
|
356
565
|
const root = document.querySelector(`#${rootId}`);
|
|
@@ -377,36 +586,6 @@ function cleanupModalEffect(rootId) {
|
|
|
377
586
|
root.style.removeProperty("border-top-right-radius");
|
|
378
587
|
root.style.removeProperty("border-top-left-radius");
|
|
379
588
|
}
|
|
380
|
-
function useDimensions() {
|
|
381
|
-
const [dimensions, setDimensions] = useState(() => ({
|
|
382
|
-
height: !IS_SSR ? window.innerHeight : 0,
|
|
383
|
-
width: !IS_SSR ? window.innerWidth : 0
|
|
384
|
-
}));
|
|
385
|
-
useIsomorphicLayoutEffect(() => {
|
|
386
|
-
function handler() {
|
|
387
|
-
setDimensions({
|
|
388
|
-
height: window.innerHeight,
|
|
389
|
-
width: window.innerWidth
|
|
390
|
-
});
|
|
391
|
-
}
|
|
392
|
-
handler();
|
|
393
|
-
window.addEventListener("resize", handler);
|
|
394
|
-
return () => {
|
|
395
|
-
window.removeEventListener("resize", handler);
|
|
396
|
-
};
|
|
397
|
-
}, []);
|
|
398
|
-
return dimensions;
|
|
399
|
-
}
|
|
400
|
-
function useStableCallback(handler) {
|
|
401
|
-
const handlerRef = useRef(void 0);
|
|
402
|
-
useIsomorphicLayoutEffect(() => {
|
|
403
|
-
handlerRef.current = handler;
|
|
404
|
-
});
|
|
405
|
-
return useCallback((...args) => {
|
|
406
|
-
const fn = handlerRef.current;
|
|
407
|
-
return fn == null ? void 0 : fn(...args);
|
|
408
|
-
}, []);
|
|
409
|
-
}
|
|
410
589
|
|
|
411
590
|
// src/hooks/use-prevent-scroll.ts
|
|
412
591
|
var KEYBOARD_BUFFER = 24;
|
|
@@ -623,83 +802,370 @@ function scrollIntoView(target) {
|
|
|
623
802
|
function willOpenKeyboard(target) {
|
|
624
803
|
return target instanceof HTMLInputElement && !nonTextInputTypes.has(target.type) || target instanceof HTMLTextAreaElement || target instanceof HTMLElement && target.isContentEditable;
|
|
625
804
|
}
|
|
626
|
-
|
|
627
|
-
|
|
805
|
+
function useStableCallback(handler) {
|
|
806
|
+
const handlerRef = useRef(void 0);
|
|
807
|
+
useIsomorphicLayoutEffect(() => {
|
|
808
|
+
handlerRef.current = handler;
|
|
809
|
+
});
|
|
810
|
+
return useCallback((...args) => {
|
|
811
|
+
const fn = handlerRef.current;
|
|
812
|
+
return fn == null ? void 0 : fn(...args);
|
|
813
|
+
}, []);
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
// src/hooks/use-sheet-state.ts
|
|
817
|
+
function useSheetState({
|
|
818
|
+
isOpen,
|
|
819
|
+
onClosed: _onClosed,
|
|
820
|
+
onOpening: _onOpening,
|
|
821
|
+
onOpen: _onOpen,
|
|
822
|
+
onClosing: _onClosing
|
|
823
|
+
}) {
|
|
824
|
+
const [state, setState] = useState(isOpen ? "opening" : "closed");
|
|
825
|
+
const onClosed = useStableCallback(() => _onClosed == null ? void 0 : _onClosed());
|
|
826
|
+
const onOpening = useStableCallback(() => _onOpening == null ? void 0 : _onOpening());
|
|
827
|
+
const onOpen = useStableCallback(() => _onOpen == null ? void 0 : _onOpen());
|
|
828
|
+
const onClosing = useStableCallback(() => _onClosing == null ? void 0 : _onClosing());
|
|
829
|
+
useEffect(() => {
|
|
830
|
+
if (isOpen && state === "closed") {
|
|
831
|
+
setState("opening");
|
|
832
|
+
} else if (!isOpen && (state === "open" || state === "opening")) {
|
|
833
|
+
setState("closing");
|
|
834
|
+
}
|
|
835
|
+
}, [isOpen, state]);
|
|
836
|
+
useEffect(() => {
|
|
837
|
+
async function handle() {
|
|
838
|
+
switch (state) {
|
|
839
|
+
case "closed":
|
|
840
|
+
await (onClosed == null ? void 0 : onClosed());
|
|
841
|
+
break;
|
|
842
|
+
case "opening":
|
|
843
|
+
await (onOpening == null ? void 0 : onOpening());
|
|
844
|
+
setState("open");
|
|
845
|
+
break;
|
|
846
|
+
case "open":
|
|
847
|
+
await (onOpen == null ? void 0 : onOpen());
|
|
848
|
+
break;
|
|
849
|
+
case "closing":
|
|
850
|
+
await (onClosing == null ? void 0 : onClosing());
|
|
851
|
+
setState("closed");
|
|
852
|
+
break;
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
handle().catch((error) => {
|
|
856
|
+
console.error("Internal sheet state error:", error);
|
|
857
|
+
});
|
|
858
|
+
}, [state]);
|
|
859
|
+
return state;
|
|
860
|
+
}
|
|
861
|
+
function useVirtualKeyboard({
|
|
862
|
+
containerRef,
|
|
863
|
+
isEnabled = true,
|
|
864
|
+
debounceDelay = 100,
|
|
865
|
+
includeContentEditable = true,
|
|
866
|
+
visualViewportThreshold = 100
|
|
867
|
+
}) {
|
|
868
|
+
const [state, setState] = useState({
|
|
869
|
+
isVisible: false,
|
|
870
|
+
height: 0
|
|
871
|
+
});
|
|
872
|
+
const focusedElementRef = useRef(null);
|
|
873
|
+
const debounceTimer = useRef(null);
|
|
874
|
+
const isTextInput = useStableCallback((el) => {
|
|
875
|
+
return (el == null ? void 0 : el.tagName) === "INPUT" || (el == null ? void 0 : el.tagName) === "TEXTAREA" || includeContentEditable && el instanceof HTMLElement && el.isContentEditable;
|
|
876
|
+
});
|
|
877
|
+
useEffect(() => {
|
|
878
|
+
if (!isEnabled) return;
|
|
879
|
+
const vv = window.visualViewport;
|
|
880
|
+
const vk = navigator.virtualKeyboard;
|
|
881
|
+
function setKeyboardInsetHeightEnv(height) {
|
|
882
|
+
var _a;
|
|
883
|
+
(_a = containerRef.current) == null ? void 0 : _a.style.setProperty(
|
|
884
|
+
"--keyboard-inset-height",
|
|
885
|
+
`${height}px`
|
|
886
|
+
);
|
|
887
|
+
}
|
|
888
|
+
function handleFocusIn(e) {
|
|
889
|
+
if (e.target instanceof HTMLElement && isTextInput(e.target)) {
|
|
890
|
+
focusedElementRef.current = e.target;
|
|
891
|
+
updateKeyboardState();
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
function handleFocusOut() {
|
|
895
|
+
focusedElementRef.current = null;
|
|
896
|
+
updateKeyboardState();
|
|
897
|
+
}
|
|
898
|
+
function updateKeyboardState() {
|
|
899
|
+
if (debounceTimer.current) {
|
|
900
|
+
clearTimeout(debounceTimer.current);
|
|
901
|
+
}
|
|
902
|
+
debounceTimer.current = setTimeout(() => {
|
|
903
|
+
const active = focusedElementRef.current;
|
|
904
|
+
const inputIsFocused = isTextInput(active);
|
|
905
|
+
if (!inputIsFocused) {
|
|
906
|
+
setKeyboardInsetHeightEnv(0);
|
|
907
|
+
setState({ isVisible: false, height: 0 });
|
|
908
|
+
return;
|
|
909
|
+
}
|
|
910
|
+
if (vv) {
|
|
911
|
+
const heightDiff = window.innerHeight - vv.height;
|
|
912
|
+
if (heightDiff > visualViewportThreshold) {
|
|
913
|
+
setKeyboardInsetHeightEnv(heightDiff);
|
|
914
|
+
setState({ isVisible: true, height: heightDiff });
|
|
915
|
+
} else {
|
|
916
|
+
setKeyboardInsetHeightEnv(0);
|
|
917
|
+
setState({ isVisible: false, height: 0 });
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
}, debounceDelay);
|
|
921
|
+
}
|
|
922
|
+
window.addEventListener("focusin", handleFocusIn);
|
|
923
|
+
window.addEventListener("focusout", handleFocusOut);
|
|
924
|
+
if (vv) {
|
|
925
|
+
vv.addEventListener("resize", updateKeyboardState);
|
|
926
|
+
vv.addEventListener("scroll", updateKeyboardState);
|
|
927
|
+
}
|
|
928
|
+
let currentOverlaysContent = false;
|
|
929
|
+
if (vk) {
|
|
930
|
+
currentOverlaysContent = vk.overlaysContent;
|
|
931
|
+
vk.overlaysContent = true;
|
|
932
|
+
}
|
|
933
|
+
return () => {
|
|
934
|
+
window.removeEventListener("focusin", handleFocusIn);
|
|
935
|
+
window.removeEventListener("focusout", handleFocusOut);
|
|
936
|
+
if (vv) {
|
|
937
|
+
vv.removeEventListener("resize", updateKeyboardState);
|
|
938
|
+
vv.removeEventListener("scroll", updateKeyboardState);
|
|
939
|
+
}
|
|
940
|
+
if (vk) {
|
|
941
|
+
vk.overlaysContent = currentOverlaysContent;
|
|
942
|
+
}
|
|
943
|
+
if (debounceTimer.current) {
|
|
944
|
+
clearTimeout(debounceTimer.current);
|
|
945
|
+
}
|
|
946
|
+
};
|
|
947
|
+
}, [
|
|
948
|
+
debounceDelay,
|
|
949
|
+
includeContentEditable,
|
|
950
|
+
isEnabled,
|
|
951
|
+
visualViewportThreshold
|
|
952
|
+
]);
|
|
953
|
+
return {
|
|
954
|
+
keyboardHeight: state.height,
|
|
955
|
+
isKeyboardOpen: state.isVisible
|
|
956
|
+
};
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
// src/snap.ts
|
|
960
|
+
function computeSnapPoints({
|
|
961
|
+
snapPointsProp,
|
|
962
|
+
sheetHeight
|
|
963
|
+
}) {
|
|
964
|
+
if (sheetHeight <= 0) {
|
|
965
|
+
console.warn(
|
|
966
|
+
`Sheet height is ${sheetHeight}, cannot compute snap points. Make sure the sheet is mounted and has a valid height.`
|
|
967
|
+
);
|
|
968
|
+
return [];
|
|
969
|
+
}
|
|
970
|
+
const snapPointValues = snapPointsProp.map((point) => {
|
|
971
|
+
if (point > 0 && point <= 1) {
|
|
972
|
+
return Math.round(point * sheetHeight);
|
|
973
|
+
}
|
|
974
|
+
return point < 0 ? sheetHeight + point : point;
|
|
975
|
+
});
|
|
976
|
+
console.assert(
|
|
977
|
+
isAscendingOrder(snapPointValues),
|
|
978
|
+
`Snap points need to be in ascending order got: [${snapPointsProp.join(", ")}]`
|
|
979
|
+
);
|
|
980
|
+
snapPointValues.forEach((snap) => {
|
|
981
|
+
if (snap < 0 || snap > sheetHeight) {
|
|
982
|
+
console.warn(
|
|
983
|
+
`Snap point ${snap} is outside of the sheet height ${sheetHeight}. This can cause unexpected behavior. Consider adjusting your snap points.`
|
|
984
|
+
);
|
|
985
|
+
}
|
|
986
|
+
});
|
|
987
|
+
if (!snapPointValues.includes(sheetHeight)) {
|
|
988
|
+
console.warn(
|
|
989
|
+
"Snap points do not include the sheet height.Please include `1` as the last snap point or it will be included automatically.This is to ensure the sheet can be fully opened."
|
|
990
|
+
);
|
|
991
|
+
snapPointValues.push(sheetHeight);
|
|
992
|
+
}
|
|
993
|
+
return snapPointValues.map((snap, index) => ({
|
|
994
|
+
snapIndex: index,
|
|
995
|
+
snapValue: snap,
|
|
996
|
+
// Absolute value from the bottom of the sheet
|
|
997
|
+
snapValueY: sheetHeight - snap
|
|
998
|
+
// Y value is inverted as `y = 0` means sheet is at the top
|
|
999
|
+
}));
|
|
1000
|
+
}
|
|
1001
|
+
function findClosestSnapPoint({
|
|
1002
|
+
snapPoints,
|
|
1003
|
+
currentY
|
|
1004
|
+
}) {
|
|
1005
|
+
return snapPoints.reduce(
|
|
1006
|
+
(closest, snap) => Math.abs(snap.snapValueY - currentY) < Math.abs(closest.snapValueY - currentY) ? snap : closest
|
|
1007
|
+
);
|
|
1008
|
+
}
|
|
1009
|
+
function findNextSnapPointInDirection({
|
|
1010
|
+
y,
|
|
1011
|
+
snapPoints,
|
|
1012
|
+
dragDirection
|
|
1013
|
+
}) {
|
|
1014
|
+
if (dragDirection === "down") {
|
|
1015
|
+
return snapPoints.slice().reverse().find((s) => s.snapValueY > y);
|
|
1016
|
+
} else {
|
|
1017
|
+
return snapPoints.find((s) => s.snapValueY < y);
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
function handleHighVelocityDrag({
|
|
1021
|
+
dragDirection,
|
|
1022
|
+
snapPoints
|
|
1023
|
+
}) {
|
|
1024
|
+
const bottomSnapPoint = snapPoints[0];
|
|
1025
|
+
const topSnapPoint = snapPoints[snapPoints.length - 1];
|
|
1026
|
+
if (dragDirection === "down") {
|
|
1027
|
+
return {
|
|
1028
|
+
yTo: bottomSnapPoint.snapValueY,
|
|
1029
|
+
snapIndex: bottomSnapPoint.snapIndex
|
|
1030
|
+
};
|
|
1031
|
+
}
|
|
1032
|
+
return {
|
|
1033
|
+
yTo: topSnapPoint.snapValueY,
|
|
1034
|
+
snapIndex: topSnapPoint.snapIndex
|
|
1035
|
+
};
|
|
1036
|
+
}
|
|
1037
|
+
function handleLowVelocityDrag({
|
|
1038
|
+
currentSnapPoint,
|
|
1039
|
+
currentY,
|
|
1040
|
+
dragDirection,
|
|
1041
|
+
snapPoints,
|
|
1042
|
+
velocity
|
|
1043
|
+
}) {
|
|
1044
|
+
const closestSnapRelativeToCurrentY = findClosestSnapPoint({
|
|
1045
|
+
snapPoints,
|
|
1046
|
+
currentY
|
|
1047
|
+
});
|
|
1048
|
+
if (Math.abs(velocity) < 20) {
|
|
1049
|
+
return {
|
|
1050
|
+
yTo: closestSnapRelativeToCurrentY.snapValueY,
|
|
1051
|
+
snapIndex: closestSnapRelativeToCurrentY.snapIndex
|
|
1052
|
+
};
|
|
1053
|
+
}
|
|
1054
|
+
const nextSnapInDirectionRelativeToCurrentY = findNextSnapPointInDirection({
|
|
1055
|
+
y: currentY,
|
|
1056
|
+
snapPoints,
|
|
1057
|
+
dragDirection
|
|
1058
|
+
});
|
|
1059
|
+
if (nextSnapInDirectionRelativeToCurrentY) {
|
|
1060
|
+
return {
|
|
1061
|
+
yTo: nextSnapInDirectionRelativeToCurrentY.snapValueY,
|
|
1062
|
+
snapIndex: nextSnapInDirectionRelativeToCurrentY.snapIndex
|
|
1063
|
+
};
|
|
1064
|
+
}
|
|
1065
|
+
return {
|
|
1066
|
+
yTo: currentSnapPoint.snapValueY,
|
|
1067
|
+
snapIndex: currentSnapPoint.snapIndex
|
|
1068
|
+
};
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
// src/sheet.tsx
|
|
628
1072
|
var Sheet = forwardRef(
|
|
629
1073
|
({
|
|
630
|
-
|
|
631
|
-
onOpenEnd,
|
|
632
|
-
onClose,
|
|
633
|
-
onCloseStart,
|
|
634
|
-
onCloseEnd,
|
|
635
|
-
onSnap,
|
|
1074
|
+
avoidKeyboard = true,
|
|
636
1075
|
children,
|
|
1076
|
+
className = "",
|
|
1077
|
+
detent = "default",
|
|
1078
|
+
disableDismiss = false,
|
|
1079
|
+
disableDrag: disableDragProp = false,
|
|
637
1080
|
disableScrollLocking = false,
|
|
1081
|
+
dragCloseThreshold = DEFAULT_DRAG_CLOSE_THRESHOLD,
|
|
1082
|
+
dragVelocityThreshold = DEFAULT_DRAG_VELOCITY_THRESHOLD,
|
|
1083
|
+
initialSnap,
|
|
638
1084
|
isOpen,
|
|
639
|
-
snapPoints: snapPointsProp,
|
|
640
|
-
rootId,
|
|
641
1085
|
modalEffectRootId,
|
|
1086
|
+
modalEffectThreshold,
|
|
642
1087
|
mountPoint,
|
|
643
|
-
style,
|
|
644
|
-
detent = "full-height",
|
|
645
|
-
initialSnap = 0,
|
|
646
|
-
disableDrag = false,
|
|
647
1088
|
prefersReducedMotion = false,
|
|
1089
|
+
snapPoints: snapPointsProp,
|
|
1090
|
+
style,
|
|
648
1091
|
tweenConfig = DEFAULT_TWEEN_CONFIG,
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
1092
|
+
unstyled = false,
|
|
1093
|
+
onOpenStart,
|
|
1094
|
+
onOpenEnd,
|
|
1095
|
+
onClose,
|
|
1096
|
+
onCloseStart,
|
|
1097
|
+
onCloseEnd,
|
|
1098
|
+
onSnap,
|
|
1099
|
+
onDrag: onDragProp,
|
|
1100
|
+
onDragStart: onDragStartProp,
|
|
1101
|
+
onDragEnd: onDragEndProp,
|
|
652
1102
|
...rest
|
|
653
1103
|
}, ref) => {
|
|
1104
|
+
const [sheetBoundsRef, sheetBounds] = useMeasure();
|
|
654
1105
|
const sheetRef = useRef(null);
|
|
1106
|
+
const sheetHeight = Math.round(sheetBounds.height);
|
|
1107
|
+
const [currentSnap, setCurrentSnap] = useState(initialSnap);
|
|
1108
|
+
const snapPoints = snapPointsProp && sheetHeight > 0 ? computeSnapPoints({ sheetHeight, snapPointsProp }) : [];
|
|
1109
|
+
const { windowHeight } = useDimensions();
|
|
1110
|
+
const closedY = sheetHeight > 0 ? sheetHeight : windowHeight;
|
|
1111
|
+
const y = useMotionValue(closedY);
|
|
1112
|
+
const yInverted = useTransform(y, (val) => Math.max(sheetHeight - val, 0));
|
|
655
1113
|
const indicatorRotation = useMotionValue(0);
|
|
656
|
-
const { height: windowHeight } = useDimensions();
|
|
657
1114
|
const shouldReduceMotion = useReducedMotion();
|
|
658
1115
|
const reduceMotion = Boolean(prefersReducedMotion || shouldReduceMotion);
|
|
659
1116
|
const animationOptions = {
|
|
660
1117
|
type: "tween",
|
|
661
1118
|
...reduceMotion ? REDUCED_MOTION_TWEEN_CONFIG : tweenConfig
|
|
662
1119
|
};
|
|
663
|
-
const
|
|
1120
|
+
const keyboard = useVirtualKeyboard({
|
|
1121
|
+
isEnabled: isOpen && avoidKeyboard,
|
|
1122
|
+
containerRef: sheetRef
|
|
1123
|
+
});
|
|
1124
|
+
const disableDrag = keyboard.isKeyboardOpen || disableDragProp;
|
|
664
1125
|
const zIndex = useTransform(
|
|
665
1126
|
y,
|
|
666
|
-
(
|
|
1127
|
+
(val) => val + 2 >= closedY ? -1 : (style == null ? void 0 : style.zIndex) ?? 9999
|
|
667
1128
|
);
|
|
668
1129
|
const visibility = useTransform(
|
|
669
1130
|
y,
|
|
670
|
-
(
|
|
1131
|
+
(val) => val + 2 >= closedY ? "hidden" : "visible"
|
|
671
1132
|
);
|
|
672
|
-
const
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
onCloseStart,
|
|
676
|
-
onCloseEnd
|
|
677
|
-
});
|
|
678
|
-
useIsomorphicLayoutEffect(() => {
|
|
679
|
-
callbacks.current = {
|
|
680
|
-
onOpenStart,
|
|
681
|
-
onOpenEnd,
|
|
682
|
-
onCloseStart,
|
|
683
|
-
onCloseEnd
|
|
684
|
-
};
|
|
1133
|
+
const updateSnap = useStableCallback((snapIndex) => {
|
|
1134
|
+
setCurrentSnap(snapIndex);
|
|
1135
|
+
onSnap == null ? void 0 : onSnap(snapIndex);
|
|
685
1136
|
});
|
|
686
|
-
const
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
y.set(topSnapPoint);
|
|
694
|
-
return;
|
|
1137
|
+
const getSnapPoint = useStableCallback((snapIndex) => {
|
|
1138
|
+
if (snapPointsProp && snapPoints) {
|
|
1139
|
+
if (snapIndex < 0 || snapIndex >= snapPoints.length) {
|
|
1140
|
+
console.warn(
|
|
1141
|
+
`Invalid snap index ${snapIndex}. Snap points are: [${snapPointsProp.join(", ")}] and their computed values are: [${snapPoints.map((point) => point.snapValue).join(", ")}]`
|
|
1142
|
+
);
|
|
1143
|
+
return null;
|
|
695
1144
|
}
|
|
1145
|
+
return snapPoints[snapIndex];
|
|
696
1146
|
}
|
|
697
|
-
|
|
698
|
-
if (velocity > 0) indicatorRotation.set(10);
|
|
699
|
-
if (velocity < 0) indicatorRotation.set(-10);
|
|
700
|
-
y.set(Math.max(currentY + info.delta.y, 0));
|
|
1147
|
+
return null;
|
|
701
1148
|
});
|
|
702
|
-
const
|
|
1149
|
+
const snapTo = useStableCallback(async (snapIndex) => {
|
|
1150
|
+
if (!snapPointsProp) {
|
|
1151
|
+
console.warn("Snapping is not possible without `snapPoints` prop.");
|
|
1152
|
+
return;
|
|
1153
|
+
}
|
|
1154
|
+
const snapPoint = getSnapPoint(snapIndex);
|
|
1155
|
+
if (snapPoint === null) {
|
|
1156
|
+
console.warn(`Invalid snap index ${snapIndex}.`);
|
|
1157
|
+
return;
|
|
1158
|
+
}
|
|
1159
|
+
if (snapIndex === 0) {
|
|
1160
|
+
onClose();
|
|
1161
|
+
return;
|
|
1162
|
+
}
|
|
1163
|
+
await animate(y, snapPoint.snapValueY, {
|
|
1164
|
+
...animationOptions,
|
|
1165
|
+
onComplete: () => updateSnap(snapIndex)
|
|
1166
|
+
});
|
|
1167
|
+
});
|
|
1168
|
+
const blurActiveInput = useStableCallback(() => {
|
|
703
1169
|
const focusedElement = document.activeElement;
|
|
704
1170
|
if (!focusedElement || !sheetRef.current) return;
|
|
705
1171
|
const isInput = focusedElement.tagName === "INPUT" || focusedElement.tagName === "TEXTAREA";
|
|
@@ -707,351 +1173,153 @@ var Sheet = forwardRef(
|
|
|
707
1173
|
focusedElement.blur();
|
|
708
1174
|
}
|
|
709
1175
|
});
|
|
710
|
-
const
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
1176
|
+
const onDrag = useStableCallback((event, info) => {
|
|
1177
|
+
onDragProp == null ? void 0 : onDragProp(event, info);
|
|
1178
|
+
const currentY = y.get();
|
|
1179
|
+
const velocity = y.getVelocity();
|
|
1180
|
+
if (velocity > 0) indicatorRotation.set(10);
|
|
1181
|
+
if (velocity < 0) indicatorRotation.set(-10);
|
|
1182
|
+
y.set(Math.max(currentY + info.delta.y, 0));
|
|
1183
|
+
});
|
|
1184
|
+
const onDragStart = useStableCallback((event, info) => {
|
|
1185
|
+
blurActiveInput();
|
|
1186
|
+
onDragStartProp == null ? void 0 : onDragStartProp(event, info);
|
|
1187
|
+
});
|
|
1188
|
+
const onDragEnd = useStableCallback((event, info) => {
|
|
1189
|
+
blurActiveInput();
|
|
1190
|
+
onDragEndProp == null ? void 0 : onDragEndProp(event, info);
|
|
1191
|
+
const currentY = y.get();
|
|
1192
|
+
let yTo = 0;
|
|
1193
|
+
const currentSnapPoint = currentSnap !== void 0 ? getSnapPoint(currentSnap) : null;
|
|
1194
|
+
if (currentSnapPoint) {
|
|
1195
|
+
const dragOffsetDirection = info.offset.y > 0 ? "down" : "up";
|
|
1196
|
+
const dragVelocityDirection = info.velocity.y > 0 ? "down" : "up";
|
|
1197
|
+
const isHighVelocity = Math.abs(info.velocity.y) > dragVelocityThreshold;
|
|
1198
|
+
let result;
|
|
1199
|
+
if (isHighVelocity) {
|
|
1200
|
+
result = handleHighVelocityDrag({
|
|
720
1201
|
snapPoints,
|
|
1202
|
+
dragDirection: dragVelocityDirection
|
|
1203
|
+
});
|
|
1204
|
+
} else {
|
|
1205
|
+
result = handleLowVelocityDrag({
|
|
1206
|
+
currentSnapPoint,
|
|
721
1207
|
currentY,
|
|
722
|
-
|
|
723
|
-
|
|
1208
|
+
dragDirection: dragOffsetDirection,
|
|
1209
|
+
snapPoints,
|
|
1210
|
+
velocity: info.velocity.y
|
|
724
1211
|
});
|
|
725
|
-
yTo = snapY;
|
|
726
|
-
onSnap == null ? void 0 : onSnap(snapIndex);
|
|
727
|
-
} else if (currentY / sheetHeight > dragCloseThreshold) {
|
|
728
|
-
yTo = sheetHeight;
|
|
729
1212
|
}
|
|
730
|
-
|
|
731
|
-
if (yTo + 1 >= sheetHeight) {
|
|
732
|
-
|
|
1213
|
+
yTo = result.yTo;
|
|
1214
|
+
if (disableDismiss && yTo + 1 >= sheetHeight) {
|
|
1215
|
+
const bottomSnapPoint = snapPoints.find((s) => s.snapValue > 0);
|
|
1216
|
+
if (bottomSnapPoint) {
|
|
1217
|
+
yTo = bottomSnapPoint.snapValueY;
|
|
1218
|
+
updateSnap(bottomSnapPoint.snapIndex);
|
|
1219
|
+
} else {
|
|
1220
|
+
yTo = currentY;
|
|
1221
|
+
}
|
|
1222
|
+
} else if (result.snapIndex !== void 0) {
|
|
1223
|
+
updateSnap(result.snapIndex);
|
|
733
1224
|
}
|
|
734
|
-
}
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
onSnap(isOpen ? initialSnap : snapPointsProp.length - 1);
|
|
740
|
-
}
|
|
741
|
-
}, [isOpen]);
|
|
742
|
-
useEffect(() => {
|
|
743
|
-
if (!isOpen) return;
|
|
744
|
-
let yTo = 0;
|
|
745
|
-
if (snapPointsProp) {
|
|
746
|
-
const snapIndex = initialSnap;
|
|
747
|
-
const sheetHeight = getSheetHeight(sheetRef);
|
|
748
|
-
const snapPoints = getSnapPoints({ snapPointsProp, sheetHeight });
|
|
749
|
-
const snapY = getSnapY({ sheetHeight, snapPoints, snapIndex });
|
|
750
|
-
if (snapY !== null) {
|
|
751
|
-
yTo = snapY;
|
|
1225
|
+
} else if (info.velocity.y > dragVelocityThreshold || currentY > sheetHeight * dragCloseThreshold) {
|
|
1226
|
+
if (disableDismiss) {
|
|
1227
|
+
yTo = 0;
|
|
1228
|
+
} else {
|
|
1229
|
+
yTo = closedY;
|
|
752
1230
|
}
|
|
753
1231
|
}
|
|
754
1232
|
animate(y, yTo, animationOptions);
|
|
755
|
-
|
|
1233
|
+
if (yTo + 1 >= sheetHeight && !disableDismiss) {
|
|
1234
|
+
onClose();
|
|
1235
|
+
}
|
|
1236
|
+
indicatorRotation.set(0);
|
|
1237
|
+
});
|
|
756
1238
|
useImperativeHandle(ref, () => ({
|
|
757
1239
|
y,
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
return;
|
|
762
|
-
}
|
|
763
|
-
const sheetHeight = getSheetHeight(sheetRef);
|
|
764
|
-
const snapPoints = getSnapPoints({ snapPointsProp, sheetHeight });
|
|
765
|
-
const snapY = getSnapY({ sheetHeight, snapPoints, snapIndex });
|
|
766
|
-
if (snapY !== null) {
|
|
767
|
-
animate(y, snapY, animationOptions);
|
|
768
|
-
onSnap == null ? void 0 : onSnap(snapIndex);
|
|
769
|
-
if (snapY + 1 >= sheetHeight) {
|
|
770
|
-
onClose();
|
|
771
|
-
}
|
|
772
|
-
}
|
|
773
|
-
}
|
|
1240
|
+
yInverted,
|
|
1241
|
+
height: sheetHeight,
|
|
1242
|
+
snapTo
|
|
774
1243
|
}));
|
|
775
|
-
if (rootId) {
|
|
776
|
-
console.warn(
|
|
777
|
-
"The `rootId` prop is deprecated and will be removed in the next major version. Use `modalEffectRootId` instead."
|
|
778
|
-
);
|
|
779
|
-
}
|
|
780
1244
|
useModalEffect({
|
|
781
1245
|
y,
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
1246
|
+
detent,
|
|
1247
|
+
sheetHeight,
|
|
1248
|
+
snapPoints,
|
|
1249
|
+
rootId: modalEffectRootId,
|
|
785
1250
|
startThreshold: modalEffectThreshold
|
|
786
1251
|
});
|
|
787
1252
|
usePreventScroll({
|
|
788
1253
|
isDisabled: disableScrollLocking || !isOpen
|
|
789
1254
|
});
|
|
790
|
-
const
|
|
791
|
-
const dragProps2 = {
|
|
792
|
-
drag: "y",
|
|
793
|
-
dragElastic: 0,
|
|
794
|
-
dragMomentum: false,
|
|
795
|
-
dragPropagation: false,
|
|
796
|
-
onDrag,
|
|
797
|
-
onDragStart,
|
|
798
|
-
onDragEnd
|
|
799
|
-
};
|
|
800
|
-
return disableDrag ? void 0 : dragProps2;
|
|
801
|
-
}, [disableDrag, windowHeight]);
|
|
802
|
-
const context = {
|
|
803
|
-
y,
|
|
804
|
-
sheetRef,
|
|
1255
|
+
const state = useSheetState({
|
|
805
1256
|
isOpen,
|
|
806
|
-
|
|
1257
|
+
onOpen: async () => {
|
|
1258
|
+
onOpenStart == null ? void 0 : onOpenStart();
|
|
1259
|
+
const initialSnapPoint = initialSnap !== void 0 ? getSnapPoint(initialSnap) : null;
|
|
1260
|
+
const yTo = (initialSnapPoint == null ? void 0 : initialSnapPoint.snapValueY) ?? 0;
|
|
1261
|
+
await animate(y, yTo, animationOptions);
|
|
1262
|
+
if (initialSnap !== void 0) {
|
|
1263
|
+
updateSnap(initialSnap);
|
|
1264
|
+
}
|
|
1265
|
+
onOpenEnd == null ? void 0 : onOpenEnd();
|
|
1266
|
+
},
|
|
1267
|
+
onClosing: async () => {
|
|
1268
|
+
onCloseStart == null ? void 0 : onCloseStart();
|
|
1269
|
+
await animate(y, closedY, animationOptions);
|
|
1270
|
+
onCloseEnd == null ? void 0 : onCloseEnd();
|
|
1271
|
+
}
|
|
1272
|
+
});
|
|
1273
|
+
const dragProps = {
|
|
1274
|
+
drag: "y",
|
|
1275
|
+
dragElastic: 0,
|
|
1276
|
+
dragMomentum: false,
|
|
1277
|
+
dragPropagation: false,
|
|
1278
|
+
onDrag,
|
|
1279
|
+
onDragStart,
|
|
1280
|
+
onDragEnd
|
|
1281
|
+
};
|
|
1282
|
+
const context = {
|
|
1283
|
+
currentSnap,
|
|
807
1284
|
detent,
|
|
808
|
-
|
|
809
|
-
callbacks,
|
|
1285
|
+
disableDrag,
|
|
810
1286
|
dragProps,
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
1287
|
+
indicatorRotation,
|
|
1288
|
+
avoidKeyboard,
|
|
1289
|
+
sheetBoundsRef,
|
|
1290
|
+
sheetRef,
|
|
1291
|
+
unstyled,
|
|
1292
|
+
y
|
|
815
1293
|
};
|
|
816
|
-
const sheet = /* @__PURE__ */
|
|
1294
|
+
const sheet = /* @__PURE__ */ React4.createElement(SheetContext.Provider, { value: context }, /* @__PURE__ */ React4.createElement(
|
|
817
1295
|
motion.div,
|
|
818
1296
|
{
|
|
819
1297
|
...rest,
|
|
820
1298
|
ref,
|
|
821
|
-
|
|
1299
|
+
"data-sheet-state": state,
|
|
1300
|
+
className: `react-modal-sheet-root ${className}`,
|
|
1301
|
+
style: {
|
|
1302
|
+
...applyStyles(styles.root, unstyled),
|
|
1303
|
+
zIndex,
|
|
1304
|
+
visibility,
|
|
1305
|
+
...style
|
|
1306
|
+
}
|
|
822
1307
|
},
|
|
823
|
-
|
|
824
|
-
children,
|
|
825
|
-
(child, i) => cloneElement(child, { key: `sheet-child-${i}` })
|
|
826
|
-
)) : null)
|
|
1308
|
+
state !== "closed" ? children : null
|
|
827
1309
|
));
|
|
828
1310
|
if (IS_SSR) return sheet;
|
|
829
1311
|
return createPortal(sheet, mountPoint ?? document.body);
|
|
830
1312
|
}
|
|
831
1313
|
);
|
|
832
1314
|
Sheet.displayName = "Sheet";
|
|
833
|
-
function useEventCallbacks(isOpen, callbacks) {
|
|
834
|
-
const prevOpen = usePrevious(isOpen);
|
|
835
|
-
const didOpen = useRef(false);
|
|
836
|
-
const handleAnimationComplete = useCallback(() => {
|
|
837
|
-
var _a, _b, _c, _d;
|
|
838
|
-
if (!didOpen.current) {
|
|
839
|
-
(_b = (_a = callbacks.current).onOpenEnd) == null ? void 0 : _b.call(_a);
|
|
840
|
-
didOpen.current = true;
|
|
841
|
-
} else {
|
|
842
|
-
(_d = (_c = callbacks.current).onCloseEnd) == null ? void 0 : _d.call(_c);
|
|
843
|
-
didOpen.current = false;
|
|
844
|
-
}
|
|
845
|
-
}, [isOpen, prevOpen]);
|
|
846
|
-
useEffect(() => {
|
|
847
|
-
var _a, _b, _c, _d;
|
|
848
|
-
if (!prevOpen && isOpen) {
|
|
849
|
-
(_b = (_a = callbacks.current).onOpenStart) == null ? void 0 : _b.call(_a);
|
|
850
|
-
} else if (!isOpen && prevOpen) {
|
|
851
|
-
(_d = (_c = callbacks.current).onCloseStart) == null ? void 0 : _d.call(_c);
|
|
852
|
-
}
|
|
853
|
-
}, [isOpen, prevOpen]);
|
|
854
|
-
return { handleAnimationComplete };
|
|
855
|
-
}
|
|
856
|
-
function usePrevious(state) {
|
|
857
|
-
const ref = useRef(void 0);
|
|
858
|
-
useEffect(() => {
|
|
859
|
-
ref.current = state;
|
|
860
|
-
});
|
|
861
|
-
return ref.current;
|
|
862
|
-
}
|
|
863
|
-
|
|
864
|
-
// src/SheetContainer.tsx
|
|
865
|
-
var SheetContainer = forwardRef(
|
|
866
|
-
({ children, style, className = "", ...rest }, ref) => {
|
|
867
|
-
const {
|
|
868
|
-
y,
|
|
869
|
-
isOpen,
|
|
870
|
-
callbacks,
|
|
871
|
-
sheetRef,
|
|
872
|
-
windowHeight,
|
|
873
|
-
detent,
|
|
874
|
-
animationOptions
|
|
875
|
-
} = useSheetContext();
|
|
876
|
-
const { handleAnimationComplete } = useEventCallbacks(isOpen, callbacks);
|
|
877
|
-
const height = detent === "full-height" ? MAX_HEIGHT : void 0;
|
|
878
|
-
const maxHeight = detent === "content-height" ? MAX_HEIGHT : void 0;
|
|
879
|
-
return /* @__PURE__ */ React2.createElement(
|
|
880
|
-
motion.div,
|
|
881
|
-
{
|
|
882
|
-
...rest,
|
|
883
|
-
ref: mergeRefs([sheetRef, ref]),
|
|
884
|
-
className: `react-modal-sheet-container ${className}`,
|
|
885
|
-
style: { ...styles.container, height, maxHeight, ...style, y },
|
|
886
|
-
initial: { y: windowHeight },
|
|
887
|
-
exit: { y: windowHeight, transition: animationOptions },
|
|
888
|
-
onAnimationComplete: handleAnimationComplete
|
|
889
|
-
},
|
|
890
|
-
children
|
|
891
|
-
);
|
|
892
|
-
}
|
|
893
|
-
);
|
|
894
|
-
SheetContainer.displayName = "SheetContainer";
|
|
895
|
-
var constraints = { bottom: 0, top: 0, left: 0, right: 0 };
|
|
896
|
-
function useDragConstraints() {
|
|
897
|
-
const constraintsRef = useRef(null);
|
|
898
|
-
const onMeasureDragConstraints = useCallback(() => constraints, []);
|
|
899
|
-
return { constraintsRef, onMeasureDragConstraints };
|
|
900
|
-
}
|
|
901
|
-
|
|
902
|
-
// src/SheetContent.tsx
|
|
903
|
-
var SheetContent = forwardRef(
|
|
904
|
-
({ children, style, disableDrag, className = "", ...rest }, ref) => {
|
|
905
|
-
const sheetContext = useSheetContext();
|
|
906
|
-
const sheetScrollerContext = useSheetScrollerContext();
|
|
907
|
-
const { constraintsRef, onMeasureDragConstraints } = useDragConstraints();
|
|
908
|
-
const dragProps = disableDrag || sheetScrollerContext.disableDrag ? void 0 : sheetContext.dragProps;
|
|
909
|
-
return /* @__PURE__ */ React2.createElement(
|
|
910
|
-
motion.div,
|
|
911
|
-
{
|
|
912
|
-
...rest,
|
|
913
|
-
ref: mergeRefs([ref, constraintsRef]),
|
|
914
|
-
className: `react-modal-sheet-content ${className}`,
|
|
915
|
-
style: { ...styles.content, ...style },
|
|
916
|
-
...dragProps,
|
|
917
|
-
dragConstraints: constraintsRef,
|
|
918
|
-
onMeasureDragConstraints
|
|
919
|
-
},
|
|
920
|
-
children
|
|
921
|
-
);
|
|
922
|
-
}
|
|
923
|
-
);
|
|
924
|
-
SheetContent.displayName = "SheetContent";
|
|
925
|
-
var SheetHeader = forwardRef(
|
|
926
|
-
({ children, style, disableDrag, ...rest }, ref) => {
|
|
927
|
-
const { indicatorRotation, dragProps } = useSheetContext();
|
|
928
|
-
const { constraintsRef, onMeasureDragConstraints } = useDragConstraints();
|
|
929
|
-
const _dragProps = disableDrag ? void 0 : dragProps;
|
|
930
|
-
const indicator1Transform = useTransform(
|
|
931
|
-
indicatorRotation,
|
|
932
|
-
(r) => `translateX(2px) rotate(${r}deg)`
|
|
933
|
-
);
|
|
934
|
-
const indicator2Transform = useTransform(
|
|
935
|
-
indicatorRotation,
|
|
936
|
-
(r) => `translateX(-2px) rotate(${ -1 * r}deg)`
|
|
937
|
-
);
|
|
938
|
-
return /* @__PURE__ */ React2.createElement(
|
|
939
|
-
motion.div,
|
|
940
|
-
{
|
|
941
|
-
...rest,
|
|
942
|
-
ref: mergeRefs([ref, constraintsRef]),
|
|
943
|
-
style: { ...styles.headerWrapper, ...style },
|
|
944
|
-
..._dragProps,
|
|
945
|
-
dragConstraints: constraintsRef,
|
|
946
|
-
onMeasureDragConstraints
|
|
947
|
-
},
|
|
948
|
-
children || /* @__PURE__ */ React2.createElement("div", { className: "react-modal-sheet-header", style: styles.header }, /* @__PURE__ */ React2.createElement(
|
|
949
|
-
motion.span,
|
|
950
|
-
{
|
|
951
|
-
className: "react-modal-sheet-drag-indicator",
|
|
952
|
-
style: { ...styles.indicator, transform: indicator1Transform }
|
|
953
|
-
}
|
|
954
|
-
), /* @__PURE__ */ React2.createElement(
|
|
955
|
-
motion.span,
|
|
956
|
-
{
|
|
957
|
-
className: "react-modal-sheet-drag-indicator",
|
|
958
|
-
style: { ...styles.indicator, transform: indicator2Transform }
|
|
959
|
-
}
|
|
960
|
-
))
|
|
961
|
-
);
|
|
962
|
-
}
|
|
963
|
-
);
|
|
964
|
-
SheetHeader.displayName = "SheetHeader";
|
|
965
|
-
var isClickable = (props) => !!props.onClick || !!props.onTap;
|
|
966
|
-
var SheetBackdrop = forwardRef(
|
|
967
|
-
({ style = {}, className = "", ...rest }, ref) => {
|
|
968
|
-
const Comp = isClickable(rest) ? motion.button : motion.div;
|
|
969
|
-
const pointerEvents = isClickable(rest) ? "auto" : "none";
|
|
970
|
-
return /* @__PURE__ */ React2.createElement(
|
|
971
|
-
Comp,
|
|
972
|
-
{
|
|
973
|
-
...rest,
|
|
974
|
-
ref,
|
|
975
|
-
className: `react-modal-sheet-backdrop ${className}`,
|
|
976
|
-
style: { ...styles.backdrop, ...style, pointerEvents },
|
|
977
|
-
initial: { opacity: 0 },
|
|
978
|
-
animate: { opacity: 1 },
|
|
979
|
-
exit: { opacity: 0 }
|
|
980
|
-
}
|
|
981
|
-
);
|
|
982
|
-
}
|
|
983
|
-
);
|
|
984
|
-
SheetBackdrop.displayName = "SheetBackdrop";
|
|
985
|
-
var SheetScroller = forwardRef(
|
|
986
|
-
({
|
|
987
|
-
autoPadding = true,
|
|
988
|
-
disableScroll = false,
|
|
989
|
-
draggableAt = "top",
|
|
990
|
-
children,
|
|
991
|
-
style: styleProp,
|
|
992
|
-
className = "",
|
|
993
|
-
...rest
|
|
994
|
-
}, ref) => {
|
|
995
|
-
const { y } = useSheetContext();
|
|
996
|
-
const { setDragEnabled, setDragDisabled } = useSheetScrollerContext();
|
|
997
|
-
function determineDragState(element) {
|
|
998
|
-
if (disableScroll) {
|
|
999
|
-
setDragEnabled();
|
|
1000
|
-
return;
|
|
1001
|
-
}
|
|
1002
|
-
const { scrollTop, scrollHeight, clientHeight } = element;
|
|
1003
|
-
const isScrollable2 = scrollHeight > clientHeight;
|
|
1004
|
-
if (!isScrollable2) return;
|
|
1005
|
-
const isAtTop = scrollTop <= 0;
|
|
1006
|
-
const isAtBottom = scrollHeight - scrollTop === clientHeight;
|
|
1007
|
-
const shouldEnable = draggableAt === "top" && isAtTop || draggableAt === "bottom" && isAtBottom || draggableAt === "both" && (isAtTop || isAtBottom);
|
|
1008
|
-
if (shouldEnable) {
|
|
1009
|
-
setDragEnabled();
|
|
1010
|
-
} else {
|
|
1011
|
-
setDragDisabled();
|
|
1012
|
-
}
|
|
1013
|
-
}
|
|
1014
|
-
function onScroll(event) {
|
|
1015
|
-
determineDragState(event.currentTarget);
|
|
1016
|
-
if (rest.onScroll) rest.onScroll(event);
|
|
1017
|
-
}
|
|
1018
|
-
function onTouchStart(event) {
|
|
1019
|
-
determineDragState(event.currentTarget);
|
|
1020
|
-
if (rest.onTouchStart) rest.onTouchStart(event);
|
|
1021
|
-
}
|
|
1022
|
-
const scrollProps = isTouchDevice() ? { onScroll, onTouchStart } : void 0;
|
|
1023
|
-
const style = {
|
|
1024
|
-
...styles.scroller,
|
|
1025
|
-
...styleProp
|
|
1026
|
-
};
|
|
1027
|
-
if (autoPadding) {
|
|
1028
|
-
style.paddingBottom = y;
|
|
1029
|
-
}
|
|
1030
|
-
if (disableScroll) {
|
|
1031
|
-
style.overflowY = "hidden";
|
|
1032
|
-
}
|
|
1033
|
-
return /* @__PURE__ */ React2.createElement(
|
|
1034
|
-
motion.div,
|
|
1035
|
-
{
|
|
1036
|
-
...rest,
|
|
1037
|
-
ref,
|
|
1038
|
-
className: `react-modal-sheet-scroller ${className}`,
|
|
1039
|
-
style,
|
|
1040
|
-
...scrollProps
|
|
1041
|
-
},
|
|
1042
|
-
children
|
|
1043
|
-
);
|
|
1044
|
-
}
|
|
1045
|
-
);
|
|
1046
|
-
SheetScroller.displayName = "SheetScroller";
|
|
1047
1315
|
|
|
1048
1316
|
// src/index.tsx
|
|
1049
1317
|
var Sheet2 = Object.assign(Sheet, {
|
|
1050
1318
|
Container: SheetContainer,
|
|
1051
1319
|
Header: SheetHeader,
|
|
1320
|
+
DragIndicator: SheetDragIndicator,
|
|
1052
1321
|
Content: SheetContent,
|
|
1053
|
-
Backdrop: SheetBackdrop
|
|
1054
|
-
Scroller: SheetScroller
|
|
1322
|
+
Backdrop: SheetBackdrop
|
|
1055
1323
|
});
|
|
1056
1324
|
|
|
1057
1325
|
export { Sheet2 as Sheet };
|