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 CHANGED
@@ -3,7 +3,6 @@
3
3
  var React2 = require('react');
4
4
  var reactDom = require('react-dom');
5
5
  var framerMotion = require('framer-motion');
6
- var utils = require('@react-aria/utils');
7
6
 
8
7
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
9
8
 
@@ -25,104 +24,85 @@ var REDUCED_MOTION_TWEEN_CONFIG = {
25
24
  var DEFAULT_DRAG_CLOSE_THRESHOLD = 0.6;
26
25
  var DEFAULT_DRAG_VELOCITY_THRESHOLD = 500;
27
26
 
28
- // src/utils.ts
29
- function getClosest(nums, goal) {
30
- let closest = nums[0];
31
- let minDifference = Math.abs(nums[0] - goal);
32
- for (let i = 1; i < nums.length; i++) {
33
- const difference = Math.abs(nums[i] - goal);
34
- if (difference < minDifference) {
35
- closest = nums[i];
36
- minDifference = difference;
37
- }
38
- }
39
- return closest;
40
- }
41
- function applyRootStyles(rootId) {
42
- const body = document.querySelector("body");
43
- const root = document.querySelector(`#${rootId}`);
44
- if (root) {
45
- const p = 24;
46
- const h = window.innerHeight;
47
- const s = (h - p) / h;
27
+ // src/hooks.tsx
28
+ var useIsomorphicLayoutEffect = IS_SSR ? React2.useEffect : React2.useLayoutEffect;
29
+ function useModalEffect({
30
+ y,
31
+ rootId,
32
+ sheetRef
33
+ }) {
34
+ const heightRef = React2.useRef(IS_SSR ? 0 : window.innerHeight);
35
+ function setup() {
36
+ const root = document.querySelector(`#${rootId}`);
37
+ const body = document.querySelector("body");
38
+ if (!root)
39
+ return;
48
40
  body.style.backgroundColor = "#000";
49
41
  root.style.overflow = "hidden";
50
- root.style.willChange = "transform";
51
- root.style.transition = "transform 200ms ease-in-out, border-radius 200ms linear";
52
- root.style.transform = `translateY(calc(env(safe-area-inset-top) + ${p / 2}px)) scale(${s})`;
53
- root.style.borderTopRightRadius = "10px";
54
- root.style.borderTopLeftRadius = "10px";
42
+ root.style.transitionTimingFunction = "cubic-bezier(0.32, 0.72, 0, 1)";
43
+ root.style.transitionProperty = "transform, border-radius";
44
+ root.style.transitionDuration = "0.5s";
45
+ root.style.transformOrigin = "center top";
55
46
  }
56
- }
57
- function cleanupRootStyles(rootId) {
58
- const body = document.querySelector("body");
59
- const root = document.getElementById(rootId);
60
- function onTransitionEnd() {
61
- root.style.removeProperty("overflow");
62
- root.style.removeProperty("will-change");
63
- root.style.removeProperty("transition");
64
- body.style.removeProperty("background-color");
65
- root.removeEventListener("transitionend", onTransitionEnd);
66
- }
67
- if (root) {
68
- root.style.removeProperty("border-top-right-radius");
69
- root.style.removeProperty("border-top-left-radius");
70
- root.style.removeProperty("transform");
71
- root.addEventListener("transitionend", onTransitionEnd);
72
- }
73
- }
74
- function inDescendingOrder(arr) {
75
- for (let i = 0; i < arr.length; i++) {
76
- if (arr[i + 1] > arr[i])
77
- return false;
78
- }
79
- return true;
80
- }
81
- function validateSnapTo({
82
- snapTo,
83
- sheetHeight
84
- }) {
85
- if (snapTo < 0) {
86
- console.warn(
87
- `Snap point is out of bounds. Sheet height is ${sheetHeight} but snap point is ${sheetHeight + Math.abs(snapTo)}.`
88
- );
47
+ function cleanup() {
48
+ const root = document.querySelector(`#${rootId}`);
49
+ const body = document.querySelector("body");
50
+ if (!root)
51
+ return;
52
+ setTimeout(() => {
53
+ body.style.removeProperty("background-color");
54
+ root.style.removeProperty("overflow");
55
+ root.style.removeProperty("transition-timing-function");
56
+ root.style.removeProperty("transition-property");
57
+ root.style.removeProperty("transition-duration");
58
+ root.style.removeProperty("transform-origin");
59
+ root.style.removeProperty("transform");
60
+ root.style.removeProperty("border-top-right-radius");
61
+ root.style.removeProperty("border-top-left-radius");
62
+ }, 100);
89
63
  }
90
- return Math.max(Math.round(snapTo), 0);
91
- }
92
- function mergeRefs(refs) {
93
- return (value) => {
94
- refs.forEach((ref) => {
95
- if (typeof ref === "function") {
96
- ref(value);
97
- } else if (ref) {
98
- ref.current = value;
99
- }
100
- });
101
- };
102
- }
103
- function isTouchDevice() {
104
- if (typeof window === "undefined")
105
- return false;
106
- return "ontouchstart" in window || navigator.maxTouchPoints > 0;
107
- }
108
-
109
- // src/hooks.tsx
110
- var useIsomorphicLayoutEffect = IS_SSR ? React2.useEffect : React2.useLayoutEffect;
111
- function useModalEffect(isOpen, rootId) {
112
- const prevOpen = usePrevious(isOpen);
64
+ useIsomorphicLayoutEffect(() => {
65
+ return () => {
66
+ if (rootId)
67
+ cleanup();
68
+ };
69
+ }, []);
113
70
  React2.useEffect(() => {
114
- if (rootId && !prevOpen && isOpen) {
115
- applyRootStyles(rootId);
116
- } else if (rootId && !isOpen && prevOpen) {
117
- cleanupRootStyles(rootId);
71
+ const root = document.querySelector(`#${rootId}`);
72
+ if (!root)
73
+ return;
74
+ function onCompleted() {
75
+ if (y.get() - 10 >= heightRef.current)
76
+ cleanup();
118
77
  }
119
- }, [isOpen, prevOpen]);
120
- React2.useEffect(() => {
78
+ const removeStartListener = y.on("animationStart", () => {
79
+ var _a;
80
+ heightRef.current = ((_a = sheetRef.current) == null ? void 0 : _a.offsetHeight) || window.innerHeight;
81
+ setup();
82
+ });
83
+ const removeChangeListener = y.on("change", (value) => {
84
+ if (root) {
85
+ const progress = Math.max(0, 1 - value / heightRef.current);
86
+ const pageWidth = window.innerWidth;
87
+ const scale = (pageWidth - 16) / pageWidth;
88
+ const ty = framerMotion.transform(progress, [0, 1], [0, 24]);
89
+ const s = framerMotion.transform(progress, [0, 1], [1, scale]);
90
+ const borderRadius = framerMotion.transform(progress, [0, 1], [0, 10]);
91
+ const inset = "env(safe-area-inset-top)";
92
+ root.style.transform = `scale(${s}) translate3d(0, calc(${inset} + ${ty}px), 0)`;
93
+ root.style.borderTopRightRadius = `${borderRadius}px`;
94
+ root.style.borderTopLeftRadius = `${borderRadius}px`;
95
+ }
96
+ });
97
+ const removeCompleteListener = y.on("animationComplete", onCompleted);
98
+ const removeCancelListener = y.on("animationCancel", onCompleted);
121
99
  return () => {
122
- if (rootId && isOpen)
123
- cleanupRootStyles(rootId);
100
+ removeStartListener();
101
+ removeChangeListener();
102
+ removeCompleteListener();
103
+ removeCancelListener();
124
104
  };
125
- }, [isOpen]);
105
+ }, [y, rootId]);
126
106
  }
127
107
  function useEventCallbacks(isOpen, callbacks) {
128
108
  const prevOpen = usePrevious(isOpen);
@@ -149,8 +129,8 @@ function useEventCallbacks(isOpen, callbacks) {
149
129
  }
150
130
  function useDimensions() {
151
131
  const [dimensions, setDimensions] = React2.useState({
152
- height: framerMotion.isBrowser ? window.innerHeight : 0,
153
- width: framerMotion.isBrowser ? window.innerWidth : 0
132
+ height: !IS_SSR ? window.innerHeight : 0,
133
+ width: !IS_SSR ? window.innerWidth : 0
154
134
  });
155
135
  useIsomorphicLayoutEffect(() => {
156
136
  function handler() {
@@ -227,7 +207,120 @@ var useSheetScrollerContext = () => {
227
207
  throw Error("Sheet scroller context error");
228
208
  return context;
229
209
  };
230
- var visualViewport = typeof window !== "undefined" ? window.visualViewport : void 0;
210
+
211
+ // src/utils.ts
212
+ function getClosest(nums, goal) {
213
+ let closest = nums[0];
214
+ let minDifference = Math.abs(nums[0] - goal);
215
+ for (let i = 1; i < nums.length; i++) {
216
+ const difference = Math.abs(nums[i] - goal);
217
+ if (difference < minDifference) {
218
+ closest = nums[i];
219
+ minDifference = difference;
220
+ }
221
+ }
222
+ return closest;
223
+ }
224
+ function inDescendingOrder(arr) {
225
+ for (let i = 0; i < arr.length; i++) {
226
+ if (arr[i + 1] > arr[i])
227
+ return false;
228
+ }
229
+ return true;
230
+ }
231
+ function validateSnapTo({
232
+ snapTo,
233
+ sheetHeight
234
+ }) {
235
+ if (snapTo < 0) {
236
+ console.warn(
237
+ `Snap point is out of bounds. Sheet height is ${sheetHeight} but snap point is ${sheetHeight + Math.abs(snapTo)}.`
238
+ );
239
+ }
240
+ return Math.max(Math.round(snapTo), 0);
241
+ }
242
+ function mergeRefs(refs) {
243
+ return (value) => {
244
+ refs.forEach((ref) => {
245
+ if (typeof ref === "function") {
246
+ ref(value);
247
+ } else if (ref) {
248
+ ref.current = value;
249
+ }
250
+ });
251
+ };
252
+ }
253
+ function isTouchDevice() {
254
+ if (typeof window === "undefined")
255
+ return false;
256
+ return "ontouchstart" in window || navigator.maxTouchPoints > 0;
257
+ }
258
+ function testPlatform(re) {
259
+ var _a;
260
+ return typeof window !== "undefined" && window.navigator != null ? re.test(
261
+ // @ts-expect-error
262
+ // eslint-disable-next-line @typescript-eslint/dot-notation
263
+ ((_a = window.navigator["userAgentData"]) == null ? void 0 : _a.platform) || window.navigator.platform
264
+ ) : false;
265
+ }
266
+ function cached(fn) {
267
+ let res = null;
268
+ return () => {
269
+ if (res == null) {
270
+ res = fn();
271
+ }
272
+ return res;
273
+ };
274
+ }
275
+ var isMac = cached(function() {
276
+ return testPlatform(/^Mac/i);
277
+ });
278
+ var isIPhone = cached(function() {
279
+ return testPlatform(/^iPhone/i);
280
+ });
281
+ var isIPad = cached(function() {
282
+ return testPlatform(/^iPad/i) || // iPadOS 13 lies and says it's a Mac, but we can distinguish by detecting touch support.
283
+ isMac() && navigator.maxTouchPoints > 1;
284
+ });
285
+ var isIOS = cached(function() {
286
+ return isIPhone() || isIPad();
287
+ });
288
+
289
+ // src/use-prevent-scroll.ts
290
+ var KEYBOARD_BUFFER = 24;
291
+ function chain(...callbacks) {
292
+ return (...args) => {
293
+ for (const callback of callbacks) {
294
+ if (typeof callback === "function") {
295
+ callback(...args);
296
+ }
297
+ }
298
+ };
299
+ }
300
+ var visualViewport = typeof document !== "undefined" && window.visualViewport;
301
+ function isScrollable(node, checkForOverflow) {
302
+ if (!node) {
303
+ return false;
304
+ }
305
+ const style = window.getComputedStyle(node);
306
+ let isScrollable2 = /(auto|scroll)/.test(
307
+ style.overflow + style.overflowX + style.overflowY
308
+ );
309
+ if (isScrollable2 && checkForOverflow) {
310
+ isScrollable2 = node.scrollHeight !== node.clientHeight || node.scrollWidth !== node.clientWidth;
311
+ }
312
+ return isScrollable2;
313
+ }
314
+ function getScrollParent(node, checkForOverflow) {
315
+ let scrollableNode = node;
316
+ if (isScrollable(scrollableNode, checkForOverflow)) {
317
+ scrollableNode = scrollableNode.parentElement;
318
+ }
319
+ while (scrollableNode && !isScrollable(scrollableNode, checkForOverflow)) {
320
+ scrollableNode = scrollableNode.parentElement;
321
+ }
322
+ return scrollableNode || document.scrollingElement || document.documentElement;
323
+ }
231
324
  var nonTextInputTypes = /* @__PURE__ */ new Set([
232
325
  "checkbox",
233
326
  "radio",
@@ -243,13 +336,13 @@ var preventScrollCount = 0;
243
336
  var restore;
244
337
  function usePreventScroll(options = {}) {
245
338
  const { isDisabled } = options;
246
- utils.useLayoutEffect(() => {
339
+ useIsomorphicLayoutEffect(() => {
247
340
  if (isDisabled) {
248
341
  return;
249
342
  }
250
343
  preventScrollCount++;
251
344
  if (preventScrollCount === 1) {
252
- if (utils.isIOS()) {
345
+ if (isIOS()) {
253
346
  restore = preventScrollMobileSafari();
254
347
  } else {
255
348
  restore = preventScrollStandard();
@@ -258,13 +351,13 @@ function usePreventScroll(options = {}) {
258
351
  return () => {
259
352
  preventScrollCount--;
260
353
  if (preventScrollCount === 0) {
261
- restore();
354
+ restore == null ? void 0 : restore();
262
355
  }
263
356
  };
264
357
  }, [isDisabled]);
265
358
  }
266
359
  function preventScrollStandard() {
267
- return utils.chain(
360
+ return chain(
268
361
  setStyle(
269
362
  document.documentElement,
270
363
  "paddingRight",
@@ -277,7 +370,7 @@ function preventScrollMobileSafari() {
277
370
  let scrollable;
278
371
  let lastY = 0;
279
372
  const onTouchStart = (e) => {
280
- scrollable = utils.getScrollParent(e.target);
373
+ scrollable = getScrollParent(e.target, true);
281
374
  if (scrollable === document.documentElement && scrollable === document.body) {
282
375
  return;
283
376
  }
@@ -287,7 +380,7 @@ function preventScrollMobileSafari() {
287
380
  if (scrollable === void 0) {
288
381
  return;
289
382
  }
290
- if (scrollable === document.documentElement || scrollable === document.body) {
383
+ if (!scrollable || scrollable === document.documentElement || scrollable === document.body) {
291
384
  e.preventDefault();
292
385
  return;
293
386
  }
@@ -327,9 +420,7 @@ function preventScrollMobileSafari() {
327
420
  } else {
328
421
  visualViewport.addEventListener(
329
422
  "resize",
330
- () => {
331
- scrollIntoView(target);
332
- },
423
+ () => scrollIntoView(target),
333
424
  { once: true }
334
425
  );
335
426
  }
@@ -342,7 +433,7 @@ function preventScrollMobileSafari() {
342
433
  };
343
434
  const scrollX = window.pageXOffset;
344
435
  const scrollY = window.pageYOffset;
345
- const restoreStyles = utils.chain(
436
+ const restoreStyles = chain(
346
437
  setStyle(
347
438
  document.documentElement,
348
439
  "paddingRight",
@@ -352,7 +443,7 @@ function preventScrollMobileSafari() {
352
443
  setStyle(document.body, "marginTop", `-${scrollY}px`)
353
444
  );
354
445
  window.scrollTo(0, 0);
355
- const removeEvents = utils.chain(
446
+ const removeEvents = chain(
356
447
  addEvent(document, "touchstart", onTouchStart, {
357
448
  passive: false,
358
449
  capture: true
@@ -390,11 +481,13 @@ function addEvent(target, event, handler, options) {
390
481
  function scrollIntoView(target) {
391
482
  const root = document.scrollingElement || document.documentElement;
392
483
  while (target && target !== root) {
393
- const scrollable = utils.getScrollParent(target);
484
+ const scrollable = getScrollParent(target);
394
485
  if (scrollable !== document.documentElement && scrollable !== document.body && scrollable !== target) {
395
486
  const scrollableTop = scrollable.getBoundingClientRect().top;
396
487
  const targetTop = target.getBoundingClientRect().top;
397
- if (targetTop > scrollableTop + target.clientHeight) {
488
+ const targetBottom = target.getBoundingClientRect().bottom;
489
+ const keyboardHeight = scrollable.getBoundingClientRect().bottom + KEYBOARD_BUFFER;
490
+ if (targetBottom > keyboardHeight) {
398
491
  scrollable.scrollTop += targetTop - scrollableTop;
399
492
  }
400
493
  }
@@ -618,7 +711,11 @@ var Sheet = React2.forwardRef(
618
711
  }
619
712
  }
620
713
  }));
621
- useModalEffect(isOpen, rootId);
714
+ useModalEffect({
715
+ y,
716
+ rootId,
717
+ sheetRef
718
+ });
622
719
  usePreventScroll({ isDisabled: disableScrollLocking || !isOpen });
623
720
  const dragProps = React2.useMemo(() => {
624
721
  const dragProps2 = {
@@ -798,8 +895,8 @@ var SheetScroller = React2.forwardRef(
798
895
  const sheetScrollerContext = useSheetScrollerContext();
799
896
  function determineDragState(element) {
800
897
  const { scrollTop, scrollHeight, clientHeight } = element;
801
- const isScrollable = scrollHeight > clientHeight;
802
- if (!isScrollable)
898
+ const isScrollable2 = scrollHeight > clientHeight;
899
+ if (!isScrollable2)
803
900
  return;
804
901
  const isAtTop = scrollTop <= 0;
805
902
  const isAtBottom = scrollHeight - scrollTop === clientHeight;
@@ -812,9 +909,15 @@ var SheetScroller = React2.forwardRef(
812
909
  }
813
910
  function onScroll(e) {
814
911
  determineDragState(e.currentTarget);
912
+ if (rest.onScroll) {
913
+ rest.onScroll(e);
914
+ }
815
915
  }
816
916
  function onTouchStart(e) {
817
917
  determineDragState(e.currentTarget);
918
+ if (rest.onTouchStart) {
919
+ rest.onTouchStart(e);
920
+ }
818
921
  }
819
922
  const scrollProps = isTouchDevice() ? { onScroll, onTouchStart } : void 0;
820
923
  return /* @__PURE__ */ React2__default.default.createElement(