react-modal-sheet 3.3.0 → 3.4.1
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/dist/index.js +213 -110
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +209 -106
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -4
- package/src/SheetScroller.tsx +6 -0
- package/src/hooks.tsx +85 -16
- package/src/sheet.tsx +5 -1
- package/src/use-prevent-scroll.ts +80 -21
- package/src/utils.ts +41 -42
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import React2, { createContext, forwardRef, useRef, useEffect, useImperativeHandle, useMemo, Children, cloneElement, useLayoutEffect, useCallback, useState, useContext } from 'react';
|
|
2
2
|
import { createPortal } from 'react-dom';
|
|
3
|
-
import { useMotionValue, useReducedMotion, useTransform, animate, motion, AnimatePresence,
|
|
4
|
-
import { useLayoutEffect as useLayoutEffect$1, isIOS, chain, getScrollParent } from '@react-aria/utils';
|
|
3
|
+
import { useMotionValue, useReducedMotion, useTransform, animate, motion, AnimatePresence, transform } from 'framer-motion';
|
|
5
4
|
|
|
6
5
|
// src/sheet.tsx
|
|
7
6
|
|
|
@@ -19,104 +18,85 @@ var REDUCED_MOTION_TWEEN_CONFIG = {
|
|
|
19
18
|
var DEFAULT_DRAG_CLOSE_THRESHOLD = 0.6;
|
|
20
19
|
var DEFAULT_DRAG_VELOCITY_THRESHOLD = 500;
|
|
21
20
|
|
|
22
|
-
// src/
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
function applyRootStyles(rootId) {
|
|
36
|
-
const body = document.querySelector("body");
|
|
37
|
-
const root = document.querySelector(`#${rootId}`);
|
|
38
|
-
if (root) {
|
|
39
|
-
const p = 24;
|
|
40
|
-
const h = window.innerHeight;
|
|
41
|
-
const s = (h - p) / h;
|
|
21
|
+
// src/hooks.tsx
|
|
22
|
+
var useIsomorphicLayoutEffect = IS_SSR ? useEffect : useLayoutEffect;
|
|
23
|
+
function useModalEffect({
|
|
24
|
+
y,
|
|
25
|
+
rootId,
|
|
26
|
+
sheetRef
|
|
27
|
+
}) {
|
|
28
|
+
const heightRef = useRef(IS_SSR ? 0 : window.innerHeight);
|
|
29
|
+
function setup() {
|
|
30
|
+
const root = document.querySelector(`#${rootId}`);
|
|
31
|
+
const body = document.querySelector("body");
|
|
32
|
+
if (!root)
|
|
33
|
+
return;
|
|
42
34
|
body.style.backgroundColor = "#000";
|
|
43
35
|
root.style.overflow = "hidden";
|
|
44
|
-
root.style.
|
|
45
|
-
root.style.
|
|
46
|
-
root.style.
|
|
47
|
-
root.style.
|
|
48
|
-
root.style.borderTopLeftRadius = "10px";
|
|
36
|
+
root.style.transitionTimingFunction = "cubic-bezier(0.32, 0.72, 0, 1)";
|
|
37
|
+
root.style.transitionProperty = "transform, border-radius";
|
|
38
|
+
root.style.transitionDuration = "0.5s";
|
|
39
|
+
root.style.transformOrigin = "center top";
|
|
49
40
|
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
function inDescendingOrder(arr) {
|
|
69
|
-
for (let i = 0; i < arr.length; i++) {
|
|
70
|
-
if (arr[i + 1] > arr[i])
|
|
71
|
-
return false;
|
|
72
|
-
}
|
|
73
|
-
return true;
|
|
74
|
-
}
|
|
75
|
-
function validateSnapTo({
|
|
76
|
-
snapTo,
|
|
77
|
-
sheetHeight
|
|
78
|
-
}) {
|
|
79
|
-
if (snapTo < 0) {
|
|
80
|
-
console.warn(
|
|
81
|
-
`Snap point is out of bounds. Sheet height is ${sheetHeight} but snap point is ${sheetHeight + Math.abs(snapTo)}.`
|
|
82
|
-
);
|
|
41
|
+
function cleanup() {
|
|
42
|
+
const root = document.querySelector(`#${rootId}`);
|
|
43
|
+
const body = document.querySelector("body");
|
|
44
|
+
if (!root)
|
|
45
|
+
return;
|
|
46
|
+
setTimeout(() => {
|
|
47
|
+
body.style.removeProperty("background-color");
|
|
48
|
+
root.style.removeProperty("overflow");
|
|
49
|
+
root.style.removeProperty("transition-timing-function");
|
|
50
|
+
root.style.removeProperty("transition-property");
|
|
51
|
+
root.style.removeProperty("transition-duration");
|
|
52
|
+
root.style.removeProperty("transform-origin");
|
|
53
|
+
root.style.removeProperty("transform");
|
|
54
|
+
root.style.removeProperty("border-top-right-radius");
|
|
55
|
+
root.style.removeProperty("border-top-left-radius");
|
|
56
|
+
}, 100);
|
|
83
57
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
ref(value);
|
|
91
|
-
} else if (ref) {
|
|
92
|
-
ref.current = value;
|
|
93
|
-
}
|
|
94
|
-
});
|
|
95
|
-
};
|
|
96
|
-
}
|
|
97
|
-
function isTouchDevice() {
|
|
98
|
-
if (typeof window === "undefined")
|
|
99
|
-
return false;
|
|
100
|
-
return "ontouchstart" in window || navigator.maxTouchPoints > 0;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
// src/hooks.tsx
|
|
104
|
-
var useIsomorphicLayoutEffect = IS_SSR ? useEffect : useLayoutEffect;
|
|
105
|
-
function useModalEffect(isOpen, rootId) {
|
|
106
|
-
const prevOpen = usePrevious(isOpen);
|
|
58
|
+
useIsomorphicLayoutEffect(() => {
|
|
59
|
+
return () => {
|
|
60
|
+
if (rootId)
|
|
61
|
+
cleanup();
|
|
62
|
+
};
|
|
63
|
+
}, []);
|
|
107
64
|
useEffect(() => {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
65
|
+
const root = document.querySelector(`#${rootId}`);
|
|
66
|
+
if (!root)
|
|
67
|
+
return;
|
|
68
|
+
function onCompleted() {
|
|
69
|
+
if (y.get() - 10 >= heightRef.current)
|
|
70
|
+
cleanup();
|
|
112
71
|
}
|
|
113
|
-
|
|
114
|
-
|
|
72
|
+
const removeStartListener = y.on("animationStart", () => {
|
|
73
|
+
var _a;
|
|
74
|
+
heightRef.current = ((_a = sheetRef.current) == null ? void 0 : _a.offsetHeight) || window.innerHeight;
|
|
75
|
+
setup();
|
|
76
|
+
});
|
|
77
|
+
const removeChangeListener = y.on("change", (value) => {
|
|
78
|
+
if (root) {
|
|
79
|
+
const progress = Math.max(0, 1 - value / heightRef.current);
|
|
80
|
+
const pageWidth = window.innerWidth;
|
|
81
|
+
const scale = (pageWidth - 16) / pageWidth;
|
|
82
|
+
const ty = transform(progress, [0, 1], [0, 24]);
|
|
83
|
+
const s = transform(progress, [0, 1], [1, scale]);
|
|
84
|
+
const borderRadius = transform(progress, [0, 1], [0, 10]);
|
|
85
|
+
const inset = "env(safe-area-inset-top)";
|
|
86
|
+
root.style.transform = `scale(${s}) translate3d(0, calc(${inset} + ${ty}px), 0)`;
|
|
87
|
+
root.style.borderTopRightRadius = `${borderRadius}px`;
|
|
88
|
+
root.style.borderTopLeftRadius = `${borderRadius}px`;
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
const removeCompleteListener = y.on("animationComplete", onCompleted);
|
|
92
|
+
const removeCancelListener = y.on("animationCancel", onCompleted);
|
|
115
93
|
return () => {
|
|
116
|
-
|
|
117
|
-
|
|
94
|
+
removeStartListener();
|
|
95
|
+
removeChangeListener();
|
|
96
|
+
removeCompleteListener();
|
|
97
|
+
removeCancelListener();
|
|
118
98
|
};
|
|
119
|
-
}, [
|
|
99
|
+
}, [y, rootId]);
|
|
120
100
|
}
|
|
121
101
|
function useEventCallbacks(isOpen, callbacks) {
|
|
122
102
|
const prevOpen = usePrevious(isOpen);
|
|
@@ -143,8 +123,8 @@ function useEventCallbacks(isOpen, callbacks) {
|
|
|
143
123
|
}
|
|
144
124
|
function useDimensions() {
|
|
145
125
|
const [dimensions, setDimensions] = useState({
|
|
146
|
-
height:
|
|
147
|
-
width:
|
|
126
|
+
height: !IS_SSR ? window.innerHeight : 0,
|
|
127
|
+
width: !IS_SSR ? window.innerWidth : 0
|
|
148
128
|
});
|
|
149
129
|
useIsomorphicLayoutEffect(() => {
|
|
150
130
|
function handler() {
|
|
@@ -221,7 +201,120 @@ var useSheetScrollerContext = () => {
|
|
|
221
201
|
throw Error("Sheet scroller context error");
|
|
222
202
|
return context;
|
|
223
203
|
};
|
|
224
|
-
|
|
204
|
+
|
|
205
|
+
// src/utils.ts
|
|
206
|
+
function getClosest(nums, goal) {
|
|
207
|
+
let closest = nums[0];
|
|
208
|
+
let minDifference = Math.abs(nums[0] - goal);
|
|
209
|
+
for (let i = 1; i < nums.length; i++) {
|
|
210
|
+
const difference = Math.abs(nums[i] - goal);
|
|
211
|
+
if (difference < minDifference) {
|
|
212
|
+
closest = nums[i];
|
|
213
|
+
minDifference = difference;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
return closest;
|
|
217
|
+
}
|
|
218
|
+
function inDescendingOrder(arr) {
|
|
219
|
+
for (let i = 0; i < arr.length; i++) {
|
|
220
|
+
if (arr[i + 1] > arr[i])
|
|
221
|
+
return false;
|
|
222
|
+
}
|
|
223
|
+
return true;
|
|
224
|
+
}
|
|
225
|
+
function validateSnapTo({
|
|
226
|
+
snapTo,
|
|
227
|
+
sheetHeight
|
|
228
|
+
}) {
|
|
229
|
+
if (snapTo < 0) {
|
|
230
|
+
console.warn(
|
|
231
|
+
`Snap point is out of bounds. Sheet height is ${sheetHeight} but snap point is ${sheetHeight + Math.abs(snapTo)}.`
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
return Math.max(Math.round(snapTo), 0);
|
|
235
|
+
}
|
|
236
|
+
function mergeRefs(refs) {
|
|
237
|
+
return (value) => {
|
|
238
|
+
refs.forEach((ref) => {
|
|
239
|
+
if (typeof ref === "function") {
|
|
240
|
+
ref(value);
|
|
241
|
+
} else if (ref) {
|
|
242
|
+
ref.current = value;
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
function isTouchDevice() {
|
|
248
|
+
if (typeof window === "undefined")
|
|
249
|
+
return false;
|
|
250
|
+
return "ontouchstart" in window || navigator.maxTouchPoints > 0;
|
|
251
|
+
}
|
|
252
|
+
function testPlatform(re) {
|
|
253
|
+
var _a;
|
|
254
|
+
return typeof window !== "undefined" && window.navigator != null ? re.test(
|
|
255
|
+
// @ts-expect-error
|
|
256
|
+
// eslint-disable-next-line @typescript-eslint/dot-notation
|
|
257
|
+
((_a = window.navigator["userAgentData"]) == null ? void 0 : _a.platform) || window.navigator.platform
|
|
258
|
+
) : false;
|
|
259
|
+
}
|
|
260
|
+
function cached(fn) {
|
|
261
|
+
let res = null;
|
|
262
|
+
return () => {
|
|
263
|
+
if (res == null) {
|
|
264
|
+
res = fn();
|
|
265
|
+
}
|
|
266
|
+
return res;
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
var isMac = cached(function() {
|
|
270
|
+
return testPlatform(/^Mac/i);
|
|
271
|
+
});
|
|
272
|
+
var isIPhone = cached(function() {
|
|
273
|
+
return testPlatform(/^iPhone/i);
|
|
274
|
+
});
|
|
275
|
+
var isIPad = cached(function() {
|
|
276
|
+
return testPlatform(/^iPad/i) || // iPadOS 13 lies and says it's a Mac, but we can distinguish by detecting touch support.
|
|
277
|
+
isMac() && navigator.maxTouchPoints > 1;
|
|
278
|
+
});
|
|
279
|
+
var isIOS = cached(function() {
|
|
280
|
+
return isIPhone() || isIPad();
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
// src/use-prevent-scroll.ts
|
|
284
|
+
var KEYBOARD_BUFFER = 24;
|
|
285
|
+
function chain(...callbacks) {
|
|
286
|
+
return (...args) => {
|
|
287
|
+
for (const callback of callbacks) {
|
|
288
|
+
if (typeof callback === "function") {
|
|
289
|
+
callback(...args);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
var visualViewport = typeof document !== "undefined" && window.visualViewport;
|
|
295
|
+
function isScrollable(node, checkForOverflow) {
|
|
296
|
+
if (!node) {
|
|
297
|
+
return false;
|
|
298
|
+
}
|
|
299
|
+
const style = window.getComputedStyle(node);
|
|
300
|
+
let isScrollable2 = /(auto|scroll)/.test(
|
|
301
|
+
style.overflow + style.overflowX + style.overflowY
|
|
302
|
+
);
|
|
303
|
+
if (isScrollable2 && checkForOverflow) {
|
|
304
|
+
isScrollable2 = node.scrollHeight !== node.clientHeight || node.scrollWidth !== node.clientWidth;
|
|
305
|
+
}
|
|
306
|
+
return isScrollable2;
|
|
307
|
+
}
|
|
308
|
+
function getScrollParent(node, checkForOverflow) {
|
|
309
|
+
let scrollableNode = node;
|
|
310
|
+
if (isScrollable(scrollableNode, checkForOverflow)) {
|
|
311
|
+
scrollableNode = scrollableNode.parentElement;
|
|
312
|
+
}
|
|
313
|
+
while (scrollableNode && !isScrollable(scrollableNode, checkForOverflow)) {
|
|
314
|
+
scrollableNode = scrollableNode.parentElement;
|
|
315
|
+
}
|
|
316
|
+
return scrollableNode || document.scrollingElement || document.documentElement;
|
|
317
|
+
}
|
|
225
318
|
var nonTextInputTypes = /* @__PURE__ */ new Set([
|
|
226
319
|
"checkbox",
|
|
227
320
|
"radio",
|
|
@@ -237,7 +330,7 @@ var preventScrollCount = 0;
|
|
|
237
330
|
var restore;
|
|
238
331
|
function usePreventScroll(options = {}) {
|
|
239
332
|
const { isDisabled } = options;
|
|
240
|
-
|
|
333
|
+
useIsomorphicLayoutEffect(() => {
|
|
241
334
|
if (isDisabled) {
|
|
242
335
|
return;
|
|
243
336
|
}
|
|
@@ -252,7 +345,7 @@ function usePreventScroll(options = {}) {
|
|
|
252
345
|
return () => {
|
|
253
346
|
preventScrollCount--;
|
|
254
347
|
if (preventScrollCount === 0) {
|
|
255
|
-
restore();
|
|
348
|
+
restore == null ? void 0 : restore();
|
|
256
349
|
}
|
|
257
350
|
};
|
|
258
351
|
}, [isDisabled]);
|
|
@@ -271,7 +364,7 @@ function preventScrollMobileSafari() {
|
|
|
271
364
|
let scrollable;
|
|
272
365
|
let lastY = 0;
|
|
273
366
|
const onTouchStart = (e) => {
|
|
274
|
-
scrollable = getScrollParent(e.target);
|
|
367
|
+
scrollable = getScrollParent(e.target, true);
|
|
275
368
|
if (scrollable === document.documentElement && scrollable === document.body) {
|
|
276
369
|
return;
|
|
277
370
|
}
|
|
@@ -281,7 +374,7 @@ function preventScrollMobileSafari() {
|
|
|
281
374
|
if (scrollable === void 0) {
|
|
282
375
|
return;
|
|
283
376
|
}
|
|
284
|
-
if (scrollable === document.documentElement || scrollable === document.body) {
|
|
377
|
+
if (!scrollable || scrollable === document.documentElement || scrollable === document.body) {
|
|
285
378
|
e.preventDefault();
|
|
286
379
|
return;
|
|
287
380
|
}
|
|
@@ -321,9 +414,7 @@ function preventScrollMobileSafari() {
|
|
|
321
414
|
} else {
|
|
322
415
|
visualViewport.addEventListener(
|
|
323
416
|
"resize",
|
|
324
|
-
() =>
|
|
325
|
-
scrollIntoView(target);
|
|
326
|
-
},
|
|
417
|
+
() => scrollIntoView(target),
|
|
327
418
|
{ once: true }
|
|
328
419
|
);
|
|
329
420
|
}
|
|
@@ -388,7 +479,9 @@ function scrollIntoView(target) {
|
|
|
388
479
|
if (scrollable !== document.documentElement && scrollable !== document.body && scrollable !== target) {
|
|
389
480
|
const scrollableTop = scrollable.getBoundingClientRect().top;
|
|
390
481
|
const targetTop = target.getBoundingClientRect().top;
|
|
391
|
-
|
|
482
|
+
const targetBottom = target.getBoundingClientRect().bottom;
|
|
483
|
+
const keyboardHeight = scrollable.getBoundingClientRect().bottom + KEYBOARD_BUFFER;
|
|
484
|
+
if (targetBottom > keyboardHeight) {
|
|
392
485
|
scrollable.scrollTop += targetTop - scrollableTop;
|
|
393
486
|
}
|
|
394
487
|
}
|
|
@@ -612,7 +705,11 @@ var Sheet = forwardRef(
|
|
|
612
705
|
}
|
|
613
706
|
}
|
|
614
707
|
}));
|
|
615
|
-
useModalEffect(
|
|
708
|
+
useModalEffect({
|
|
709
|
+
y,
|
|
710
|
+
rootId,
|
|
711
|
+
sheetRef
|
|
712
|
+
});
|
|
616
713
|
usePreventScroll({ isDisabled: disableScrollLocking || !isOpen });
|
|
617
714
|
const dragProps = useMemo(() => {
|
|
618
715
|
const dragProps2 = {
|
|
@@ -792,8 +889,8 @@ var SheetScroller = forwardRef(
|
|
|
792
889
|
const sheetScrollerContext = useSheetScrollerContext();
|
|
793
890
|
function determineDragState(element) {
|
|
794
891
|
const { scrollTop, scrollHeight, clientHeight } = element;
|
|
795
|
-
const
|
|
796
|
-
if (!
|
|
892
|
+
const isScrollable2 = scrollHeight > clientHeight;
|
|
893
|
+
if (!isScrollable2)
|
|
797
894
|
return;
|
|
798
895
|
const isAtTop = scrollTop <= 0;
|
|
799
896
|
const isAtBottom = scrollHeight - scrollTop === clientHeight;
|
|
@@ -806,9 +903,15 @@ var SheetScroller = forwardRef(
|
|
|
806
903
|
}
|
|
807
904
|
function onScroll(e) {
|
|
808
905
|
determineDragState(e.currentTarget);
|
|
906
|
+
if (rest.onScroll) {
|
|
907
|
+
rest.onScroll(e);
|
|
908
|
+
}
|
|
809
909
|
}
|
|
810
910
|
function onTouchStart(e) {
|
|
811
911
|
determineDragState(e.currentTarget);
|
|
912
|
+
if (rest.onTouchStart) {
|
|
913
|
+
rest.onTouchStart(e);
|
|
914
|
+
}
|
|
812
915
|
}
|
|
813
916
|
const scrollProps = isTouchDevice() ? { onScroll, onTouchStart } : void 0;
|
|
814
917
|
return /* @__PURE__ */ React2.createElement(
|