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.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, isBrowser } from 'framer-motion';
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/utils.ts
23
- function getClosest(nums, goal) {
24
- let closest = nums[0];
25
- let minDifference = Math.abs(nums[0] - goal);
26
- for (let i = 1; i < nums.length; i++) {
27
- const difference = Math.abs(nums[i] - goal);
28
- if (difference < minDifference) {
29
- closest = nums[i];
30
- minDifference = difference;
31
- }
32
- }
33
- return closest;
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.willChange = "transform";
45
- root.style.transition = "transform 200ms ease-in-out, border-radius 200ms linear";
46
- root.style.transform = `translateY(calc(env(safe-area-inset-top) + ${p / 2}px)) scale(${s})`;
47
- root.style.borderTopRightRadius = "10px";
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
- function cleanupRootStyles(rootId) {
52
- const body = document.querySelector("body");
53
- const root = document.getElementById(rootId);
54
- function onTransitionEnd() {
55
- root.style.removeProperty("overflow");
56
- root.style.removeProperty("will-change");
57
- root.style.removeProperty("transition");
58
- body.style.removeProperty("background-color");
59
- root.removeEventListener("transitionend", onTransitionEnd);
60
- }
61
- if (root) {
62
- root.style.removeProperty("border-top-right-radius");
63
- root.style.removeProperty("border-top-left-radius");
64
- root.style.removeProperty("transform");
65
- root.addEventListener("transitionend", onTransitionEnd);
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
- return Math.max(Math.round(snapTo), 0);
85
- }
86
- function mergeRefs(refs) {
87
- return (value) => {
88
- refs.forEach((ref) => {
89
- if (typeof ref === "function") {
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
- if (rootId && !prevOpen && isOpen) {
109
- applyRootStyles(rootId);
110
- } else if (rootId && !isOpen && prevOpen) {
111
- cleanupRootStyles(rootId);
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
- }, [isOpen, prevOpen]);
114
- useEffect(() => {
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
- if (rootId && isOpen)
117
- cleanupRootStyles(rootId);
94
+ removeStartListener();
95
+ removeChangeListener();
96
+ removeCompleteListener();
97
+ removeCancelListener();
118
98
  };
119
- }, [isOpen]);
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: isBrowser ? window.innerHeight : 0,
147
- width: isBrowser ? window.innerWidth : 0
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
- var visualViewport = typeof window !== "undefined" ? window.visualViewport : void 0;
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
- useLayoutEffect$1(() => {
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
- if (targetTop > scrollableTop + target.clientHeight) {
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(isOpen, rootId);
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 isScrollable = scrollHeight > clientHeight;
796
- if (!isScrollable)
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(