react-modal-sheet 1.11.1 → 2.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.
@@ -8,44 +8,41 @@ var framerMotion = require('framer-motion');
8
8
  var utils = require('@react-aria/utils');
9
9
 
10
10
  function _extends() {
11
- _extends = Object.assign || function (target) {
11
+ _extends = Object.assign ? Object.assign.bind() : function (target) {
12
12
  for (var i = 1; i < arguments.length; i++) {
13
13
  var source = arguments[i];
14
-
15
14
  for (var key in source) {
16
15
  if (Object.prototype.hasOwnProperty.call(source, key)) {
17
16
  target[key] = source[key];
18
17
  }
19
18
  }
20
19
  }
21
-
22
20
  return target;
23
21
  };
24
-
25
22
  return _extends.apply(this, arguments);
26
23
  }
27
-
28
24
  function _objectWithoutPropertiesLoose(source, excluded) {
29
25
  if (source == null) return {};
30
26
  var target = {};
31
27
  var sourceKeys = Object.keys(source);
32
28
  var key, i;
33
-
34
29
  for (i = 0; i < sourceKeys.length; i++) {
35
30
  key = sourceKeys[i];
36
31
  if (excluded.indexOf(key) >= 0) continue;
37
32
  target[key] = source[key];
38
33
  }
39
-
40
34
  return target;
41
35
  }
42
36
 
43
37
  var MAX_HEIGHT = 'calc(100% - env(safe-area-inset-top) - 34px)';
44
38
  var IS_SSR = typeof window === 'undefined';
45
- var DEFAULT_SPRING_CONFIG = {
46
- stiffness: 300,
47
- damping: 30,
48
- mass: 0.2
39
+ var DEFAULT_TWEEN_CONFIG = {
40
+ ease: 'easeOut',
41
+ duration: 0.2
42
+ };
43
+ var REDUCED_MOTION_TWEEN_CONFIG = {
44
+ ease: 'linear',
45
+ duration: 0.01
49
46
  };
50
47
  var DRAG_CLOSE_THRESHOLD = 0.6;
51
48
  var DRAG_VELOCITY_THRESHOLD = 500;
@@ -53,23 +50,18 @@ var DRAG_VELOCITY_THRESHOLD = 500;
53
50
  function getClosest(nums, goal) {
54
51
  var closest = nums[0];
55
52
  var minDifference = Math.abs(nums[0] - goal);
56
-
57
53
  for (var i = 1; i < nums.length; i++) {
58
54
  var difference = Math.abs(nums[i] - goal);
59
-
60
55
  if (difference < minDifference) {
61
56
  closest = nums[i];
62
57
  minDifference = difference;
63
58
  }
64
59
  }
65
-
66
60
  return closest;
67
61
  }
68
- var highlightId = 'react-modal-sheet-highlight';
69
62
  function applyRootStyles(rootId) {
70
63
  var body = document.querySelector('body');
71
64
  var root = document.querySelector("#" + rootId);
72
-
73
65
  if (root) {
74
66
  var p = 24;
75
67
  var h = window.innerHeight;
@@ -77,52 +69,28 @@ function applyRootStyles(rootId) {
77
69
  body.style.backgroundColor = '#000';
78
70
  root.style.overflow = 'hidden';
79
71
  root.style.willChange = 'transform';
80
- root.style.transition = 'transform 200ms linear';
72
+ root.style.transition = 'transform 200ms ease-in-out, border-radius 200ms linear';
81
73
  root.style.transform = "translateY(calc(env(safe-area-inset-top) + " + p / 2 + "px)) scale(" + s + ")"; // prettier-ignore
82
-
83
74
  root.style.borderTopRightRadius = '10px';
84
- root.style.borderTopLeftRadius = '10px'; // Add highlighed overlay to emphasize the modality effect
85
-
86
- var highlight = document.createElement('div');
87
- highlight.setAttribute('aria-hidden', 'true');
88
- highlight.id = highlightId;
89
- highlight.style.position = 'absolute';
90
- highlight.style.top = '0px';
91
- highlight.style.left = '0px';
92
- highlight.style.bottom = '0px';
93
- highlight.style.right = '0px';
94
- highlight.style.opacity = '0';
95
- highlight.style.transition = 'opacity 200ms linear';
96
- highlight.style.backgroundColor = 'rgba(150, 150, 150, 0.1)';
97
- root.appendChild(highlight);
98
- requestAnimationFrame(function () {
99
- return highlight.style.opacity = '1';
100
- });
75
+ root.style.borderTopLeftRadius = '10px';
101
76
  }
102
77
  }
103
78
  function cleanupRootStyles(rootId) {
104
79
  var body = document.querySelector('body');
105
80
  var root = document.getElementById(rootId);
106
- var highlight = document.getElementById(highlightId);
107
-
108
81
  function onTransitionEnd() {
109
- var _highlight$parentNode;
110
-
111
82
  root.style.removeProperty('overflow');
112
83
  root.style.removeProperty('will-change');
113
84
  root.style.removeProperty('transition');
114
85
  body.style.removeProperty('background-color');
115
86
  root.removeEventListener('transitionend', onTransitionEnd);
116
- (_highlight$parentNode = highlight.parentNode) == null ? void 0 : _highlight$parentNode.removeChild(highlight);
117
87
  }
118
-
119
- if (root && highlight) {
88
+ if (root) {
120
89
  // Start animating back
121
90
  root.style.removeProperty('border-top-right-radius');
122
91
  root.style.removeProperty('border-top-left-radius');
123
92
  root.style.removeProperty('transform');
124
- highlight.style.opacity = '0'; // Remove temp properties after animation is finished
125
-
93
+ // Remove temp properties after animation is finished
126
94
  root.addEventListener('transitionend', onTransitionEnd);
127
95
  }
128
96
  }
@@ -130,17 +98,14 @@ function inDescendingOrder(arr) {
130
98
  for (var i = 0; i < arr.length; i++) {
131
99
  if (arr[i + 1] > arr[i]) return false;
132
100
  }
133
-
134
101
  return true;
135
102
  }
136
103
  function validateSnapTo(_ref) {
137
104
  var snapTo = _ref.snapTo,
138
- sheetHeight = _ref.sheetHeight;
139
-
105
+ sheetHeight = _ref.sheetHeight;
140
106
  if (snapTo < 0) {
141
107
  console.warn("Snap point is out of bounds. Sheet height is " + sheetHeight + " but snap point is " + (sheetHeight + Math.abs(snapTo)) + ".");
142
108
  }
143
-
144
109
  return Math.max(Math.round(snapTo), 0);
145
110
  }
146
111
  function mergeRefs(refs) {
@@ -154,11 +119,15 @@ function mergeRefs(refs) {
154
119
  });
155
120
  };
156
121
  }
122
+ function isTouchDevice() {
123
+ if (typeof window === 'undefined') return false;
124
+ return 'ontouchstart' in window || navigator.maxTouchPoints > 0;
125
+ }
157
126
 
158
127
  var useIsomorphicLayoutEffect = IS_SSR ? React.useEffect : React.useLayoutEffect;
159
- var useModalEffect = function useModalEffect(isOpen, rootId) {
160
- var prevOpen = usePrevious(isOpen); // Automatically apply the iOS modal effect to the body when sheet opens/closes
161
-
128
+ function useModalEffect(isOpen, rootId) {
129
+ var prevOpen = usePrevious(isOpen);
130
+ // Automatically apply the iOS modal effect to the body when sheet opens/closes
162
131
  React.useEffect(function () {
163
132
  if (rootId && !prevOpen && isOpen) {
164
133
  applyRootStyles(rootId);
@@ -167,51 +136,47 @@ var useModalEffect = function useModalEffect(isOpen, rootId) {
167
136
  }
168
137
  }, [isOpen, prevOpen]); // eslint-disable-line
169
138
  // Make sure to cleanup modal styles on unmount
170
-
171
139
  React.useEffect(function () {
172
140
  return function () {
173
141
  if (rootId && isOpen) cleanupRootStyles(rootId);
174
142
  };
175
143
  }, [isOpen]); // eslint-disable-line
176
- };
177
- var useEventCallbacks = function useEventCallbacks(isOpen, callbacks) {
144
+ }
145
+
146
+ function useEventCallbacks(isOpen, callbacks) {
178
147
  var prevOpen = usePrevious(isOpen);
179
- var didOpen = React.useRef(false); // Because of AnimatePrecence we don't have access to latest isOpen value
148
+ var didOpen = React.useRef(false);
149
+ // Because of AnimatePrecence we don't have access to latest isOpen value
180
150
  // so we need to read and write to a ref to determine if we are
181
151
  // opening or closing the sheet
182
-
183
152
  var handleAnimationComplete = React.useCallback(function () {
184
153
  if (!didOpen.current) {
185
- callbacks.current.onOpenEnd == null ? void 0 : callbacks.current.onOpenEnd();
154
+ callbacks.current.onOpenEnd == null || callbacks.current.onOpenEnd();
186
155
  didOpen.current = true;
187
156
  } else {
188
- callbacks.current.onCloseEnd == null ? void 0 : callbacks.current.onCloseEnd();
157
+ callbacks.current.onCloseEnd == null || callbacks.current.onCloseEnd();
189
158
  didOpen.current = false;
190
159
  }
191
160
  }, [isOpen, prevOpen]); // eslint-disable-line
192
-
193
161
  React.useEffect(function () {
194
162
  if (!prevOpen && isOpen) {
195
- callbacks.current.onOpenStart == null ? void 0 : callbacks.current.onOpenStart();
163
+ callbacks.current.onOpenStart == null || callbacks.current.onOpenStart();
196
164
  } else if (!isOpen && prevOpen) {
197
- callbacks.current.onCloseStart == null ? void 0 : callbacks.current.onCloseStart();
165
+ callbacks.current.onCloseStart == null || callbacks.current.onCloseStart();
198
166
  }
199
167
  }, [isOpen, prevOpen]); // eslint-disable-line
200
-
201
168
  return {
202
169
  handleAnimationComplete: handleAnimationComplete
203
170
  };
204
- };
171
+ }
205
172
  function useWindowHeight() {
206
173
  var _React$useState = React.useState(0),
207
- windowHeight = _React$useState[0],
208
- setWindowHeight = _React$useState[1];
209
-
174
+ windowHeight = _React$useState[0],
175
+ setWindowHeight = _React$useState[1];
210
176
  useIsomorphicLayoutEffect(function () {
211
177
  var updateHeight = function updateHeight() {
212
178
  return setWindowHeight(window.innerHeight);
213
179
  };
214
-
215
180
  window.addEventListener('resize', updateHeight);
216
181
  updateHeight();
217
182
  return function () {
@@ -226,9 +191,9 @@ function usePrevious(state) {
226
191
  ref.current = state;
227
192
  });
228
193
  return ref.current;
229
- } // Userland version of the `useEvent` React hook:
194
+ }
195
+ // Userland version of the `useEvent` React hook:
230
196
  // RFC: https://github.com/reactjs/rfcs/blob/useevent/text/0000-useevent.md
231
-
232
197
  function useEvent(handler) {
233
198
  var handlerRef = React.useRef();
234
199
  useIsomorphicLayoutEffect(function () {
@@ -236,11 +201,9 @@ function useEvent(handler) {
236
201
  });
237
202
  return React.useCallback(function () {
238
203
  var fn = handlerRef.current;
239
-
240
204
  for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
241
205
  args[_key] = arguments[_key];
242
206
  }
243
-
244
207
  return fn == null ? void 0 : fn.apply(void 0, args);
245
208
  }, []);
246
209
  }
@@ -251,13 +214,39 @@ var useSheetContext = function useSheetContext() {
251
214
  if (!context) throw Error('Sheet context error');
252
215
  return context;
253
216
  };
217
+ var SheetScrollerContext = /*#__PURE__*/React.createContext(undefined);
218
+ function SheetScrollerContextProvider(_ref) {
219
+ var children = _ref.children;
220
+ var sheetContext = useSheetContext();
221
+ var _React$useState = React.useState(!!sheetContext.disableDrag),
222
+ disableDrag = _React$useState[0],
223
+ setDisableDrag = _React$useState[1];
224
+ function setDragEnabled() {
225
+ if (!sheetContext.disableDrag) setDisableDrag(false);
226
+ }
227
+ function setDragDisabled() {
228
+ if (!disableDrag) setDisableDrag(true);
229
+ }
230
+ return React.createElement(SheetScrollerContext.Provider, {
231
+ value: {
232
+ disableDrag: disableDrag,
233
+ setDragEnabled: setDragEnabled,
234
+ setDragDisabled: setDragDisabled
235
+ }
236
+ }, children);
237
+ }
238
+ var useSheetScrollerContext = function useSheetScrollerContext() {
239
+ var context = React.useContext(SheetScrollerContext);
240
+ if (!context) throw Error('Sheet scroller context error');
241
+ return context;
242
+ };
254
243
 
255
244
  // Source: https://github.com/adobe/react-spectrum/blob/main/packages/@react-aria/overlays/src/usePreventScroll.ts
256
-
257
- var visualViewport = typeof window !== 'undefined' && window.visualViewport; // HTML input types that do not cause the software keyboard to appear.
258
-
259
- var nonTextInputTypes = /*#__PURE__*/new Set(['checkbox', 'radio', 'range', 'color', 'file', 'image', 'button', 'submit', 'reset']); // The number of active usePreventScroll calls. Used to determine whether to revert back to the original page style/scroll position
260
-
245
+ // @ts-ignore
246
+ var visualViewport = typeof window !== 'undefined' && window.visualViewport;
247
+ // HTML input types that do not cause the software keyboard to appear.
248
+ var nonTextInputTypes = /*#__PURE__*/new Set(['checkbox', 'radio', 'range', 'color', 'file', 'image', 'button', 'submit', 'reset']);
249
+ // The number of active usePreventScroll calls. Used to determine whether to revert back to the original page style/scroll position
261
250
  var preventScrollCount = 0;
262
251
  var restore;
263
252
  /**
@@ -265,21 +254,17 @@ var restore;
265
254
  * restores it on unmount. Also ensures that content does not
266
255
  * shift due to the scrollbars disappearing.
267
256
  */
268
-
269
257
  function usePreventScroll(options) {
270
258
  if (options === void 0) {
271
259
  options = {};
272
260
  }
273
-
274
261
  var _options = options,
275
- isDisabled = _options.isDisabled;
262
+ isDisabled = _options.isDisabled;
276
263
  utils.useLayoutEffect(function () {
277
264
  if (isDisabled) {
278
265
  return;
279
266
  }
280
-
281
267
  preventScrollCount++;
282
-
283
268
  if (preventScrollCount === 1) {
284
269
  if (utils.isIOS()) {
285
270
  restore = preventScrollMobileSafari();
@@ -287,21 +272,20 @@ function usePreventScroll(options) {
287
272
  restore = preventScrollStandard();
288
273
  }
289
274
  }
290
-
291
275
  return function () {
292
276
  preventScrollCount--;
293
-
294
277
  if (preventScrollCount === 0) {
295
278
  restore();
296
279
  }
297
280
  };
298
281
  }, [isDisabled]);
299
- } // For most browsers, all we need to do is set `overflow: hidden` on the root element, and
282
+ }
283
+ // For most browsers, all we need to do is set `overflow: hidden` on the root element, and
300
284
  // add some padding to prevent the page from shifting when the scrollbar is hidden.
301
-
302
285
  function preventScrollStandard() {
303
286
  return utils.chain(setStyle(document.documentElement, 'paddingRight', window.innerWidth - document.documentElement.clientWidth + "px"), setStyle(document.documentElement, 'overflow', 'hidden'));
304
- } // Mobile Safari is a whole different beast. Even with overflow: hidden,
287
+ }
288
+ // Mobile Safari is a whole different beast. Even with overflow: hidden,
305
289
  // it still scrolls the page in many situations:
306
290
  //
307
291
  // 1. When the bottom toolbar and address bar are collapsed, page scrolling is always allowed.
@@ -327,57 +311,47 @@ function preventScrollStandard() {
327
311
  // above work or Safari will still try to scroll the page when focusing an input.
328
312
  // 6. As a last resort, handle window scroll events, and scroll back to the top. This can happen when attempting
329
313
  // to navigate to an input with the next/previous buttons that's outside a modal.
330
-
331
-
332
314
  function preventScrollMobileSafari() {
333
315
  var scrollable;
334
316
  var lastY = 0;
335
-
336
317
  var onTouchStart = function onTouchStart(e) {
337
318
  // Store the nearest scrollable parent element from the element that the user touched.
338
319
  scrollable = utils.getScrollParent(e.target);
339
-
340
320
  if (scrollable === document.documentElement && scrollable === document.body) {
341
321
  return;
342
322
  }
343
-
344
323
  lastY = e.changedTouches[0].pageY;
345
324
  };
346
-
347
325
  var onTouchMove = function onTouchMove(e) {
348
326
  // Prevent scrolling the window.
349
327
  if (scrollable === document.documentElement || scrollable === document.body) {
350
328
  e.preventDefault();
351
329
  return;
352
- } // Prevent scrolling up when at the top and scrolling down when at the bottom
330
+ }
331
+ // Prevent scrolling up when at the top and scrolling down when at the bottom
353
332
  // of a nested scrollable area, otherwise mobile Safari will start scrolling
354
333
  // the window instead. Unfortunately, this disables bounce scrolling when at
355
334
  // the top but it's the best we can do.
356
-
357
-
358
335
  var y = e.changedTouches[0].pageY;
359
336
  var scrollTop = scrollable.scrollTop;
360
- var bottom = scrollable.scrollHeight - scrollable.clientHeight; // Fix for: https://github.com/adobe/react-spectrum/pull/3780/files
361
-
337
+ var bottom = scrollable.scrollHeight - scrollable.clientHeight;
338
+ // Fix for: https://github.com/adobe/react-spectrum/pull/3780/files
362
339
  if (bottom === 0) {
363
340
  return;
364
341
  }
365
-
366
342
  if (scrollTop <= 0 && y > lastY || scrollTop >= bottom && y < lastY) {
367
343
  e.preventDefault();
368
344
  }
369
-
370
345
  lastY = y;
371
346
  };
372
-
373
347
  var onTouchEnd = function onTouchEnd(e) {
374
- var target = e.target; // Apply this change if we're not already focused on the target element
375
-
348
+ var target = e.target;
349
+ // Apply this change if we're not already focused on the target element
376
350
  if (willOpenKeyboard(target) && target !== document.activeElement) {
377
- e.preventDefault(); // Apply a transform to trick Safari into thinking the input is at the top of the page
351
+ e.preventDefault();
352
+ // Apply a transform to trick Safari into thinking the input is at the top of the page
378
353
  // so it doesn't try to scroll it into view. When tapping on an input, this needs to
379
354
  // be done before the "focus" event, so we have to focus the element ourselves.
380
-
381
355
  target.style.transform = 'translateY(-2000px)';
382
356
  target.focus();
383
357
  requestAnimationFrame(function () {
@@ -385,10 +359,8 @@ function preventScrollMobileSafari() {
385
359
  });
386
360
  }
387
361
  };
388
-
389
362
  var onFocus = function onFocus(e) {
390
363
  var target = e.target;
391
-
392
364
  if (willOpenKeyboard(target)) {
393
365
  // Transform also needs to be applied in the focus event in cases where focus moves
394
366
  // other than tapping on an input directly, e.g. the next/previous buttons in the
@@ -396,9 +368,9 @@ function preventScrollMobileSafari() {
396
368
  // is good enough, whereas when tapping an input, it must be done before the focus event. 🤷‍♂️
397
369
  target.style.transform = 'translateY(-2000px)';
398
370
  requestAnimationFrame(function () {
399
- target.style.transform = ''; // This will have prevented the browser from scrolling the focused element into view,
371
+ target.style.transform = '';
372
+ // This will have prevented the browser from scrolling the focused element into view,
400
373
  // so we need to do this ourselves in a way that doesn't cause the whole page to scroll.
401
-
402
374
  if (visualViewport) {
403
375
  if (visualViewport.height < window.innerHeight) {
404
376
  // If the keyboard is already visible, do this after one additional frame
@@ -419,20 +391,18 @@ function preventScrollMobileSafari() {
419
391
  });
420
392
  }
421
393
  };
422
-
423
394
  var onWindowScroll = function onWindowScroll() {
424
395
  // Last resort. If the window scrolled, scroll it back to the top.
425
396
  // It should always be at the top because the body will have a negative margin (see below).
426
397
  window.scrollTo(0, 0);
427
- }; // Record the original scroll position so we can restore it.
398
+ };
399
+ // Record the original scroll position so we can restore it.
428
400
  // Then apply a negative margin to the body to offset it by the scroll position. This will
429
401
  // enable us to scroll the window to the top, which is required for the rest of this to work.
430
-
431
-
432
402
  var scrollX = window.pageXOffset;
433
403
  var scrollY = window.pageYOffset;
434
- var restoreStyles = utils.chain(setStyle(document.documentElement, 'paddingRight', window.innerWidth - document.documentElement.clientWidth + "px"), setStyle(document.documentElement, 'overflow', 'hidden'), setStyle(document.body, 'marginTop', "-" + scrollY + "px")); // Scroll to the top. The negative margin on the body will make this appear the same.
435
-
404
+ var restoreStyles = utils.chain(setStyle(document.documentElement, 'paddingRight', window.innerWidth - document.documentElement.clientWidth + "px"), setStyle(document.documentElement, 'overflow', 'hidden'), setStyle(document.body, 'marginTop', "-" + scrollY + "px"));
405
+ // Scroll to the top. The negative margin on the body will make this appear the same.
436
406
  window.scrollTo(0, 0);
437
407
  var removeEvents = utils.chain(addEvent(document, 'touchstart', onTouchStart, {
438
408
  passive: false,
@@ -450,45 +420,37 @@ function preventScrollMobileSafari() {
450
420
  removeEvents();
451
421
  window.scrollTo(scrollX, scrollY);
452
422
  };
453
- } // Sets a CSS property on an element, and returns a function to revert it to the previous value.
454
-
455
-
423
+ }
424
+ // Sets a CSS property on an element, and returns a function to revert it to the previous value.
456
425
  function setStyle(element, style, value) {
457
426
  var cur = element.style[style];
458
427
  element.style[style] = value;
459
428
  return function () {
460
429
  element.style[style] = cur;
461
430
  };
462
- } // Adds an event listener to an element, and returns a function to remove it.
463
-
464
-
431
+ }
432
+ // Adds an event listener to an element, and returns a function to remove it.
465
433
  function addEvent(target, event, handler, options) {
466
434
  target.addEventListener(event, handler, options);
467
435
  return function () {
468
436
  target.removeEventListener(event, handler, options);
469
437
  };
470
438
  }
471
-
472
439
  function scrollIntoView(target) {
473
440
  var root = document.scrollingElement || document.documentElement;
474
-
475
441
  while (target && target !== root) {
476
442
  // Find the parent scrollable element and adjust the scroll position if the target is not already in view.
477
443
  var scrollable = utils.getScrollParent(target);
478
-
479
444
  if (scrollable !== document.documentElement && scrollable !== document.body && scrollable !== target) {
480
445
  var scrollableTop = scrollable.getBoundingClientRect().top;
481
446
  var targetTop = target.getBoundingClientRect().top;
482
-
483
447
  if (targetTop > scrollableTop + target.clientHeight) {
484
448
  scrollable.scrollTop += targetTop - scrollableTop;
485
449
  }
486
450
  }
487
-
488
451
  target = scrollable.parentElement;
489
452
  }
490
453
  }
491
-
492
454
  function willOpenKeyboard(target) {
493
455
  return target instanceof HTMLInputElement && !nonTextInputTypes.has(target.type) || target instanceof HTMLTextAreaElement || target instanceof HTMLElement && target.isContentEditable;
494
456
  }
@@ -510,7 +472,7 @@ var styles = {
510
472
  left: 0,
511
473
  width: '100%',
512
474
  height: '100%',
513
- backgroundColor: 'rgba(51, 51, 51, 0.5)',
475
+ backgroundColor: 'rgba(0, 0, 0, 0.2)',
514
476
  touchAction: 'none',
515
477
  border: 'none'
516
478
  },
@@ -546,60 +508,65 @@ var styles = {
546
508
  backgroundColor: '#ddd'
547
509
  },
548
510
  content: {
549
- flex: 1,
550
- overflow: 'auto',
511
+ flexGrow: 1,
512
+ display: 'flex',
513
+ flexDirection: 'column',
514
+ minHeight: '0px',
551
515
  position: 'relative'
516
+ },
517
+ scroller: {
518
+ height: '100%',
519
+ overflowY: 'auto'
552
520
  }
553
521
  };
554
522
 
523
+ var _excluded = ["onOpenStart", "onOpenEnd", "onClose", "onCloseStart", "onCloseEnd", "onSnap", "children", "disableScrollLocking", "isOpen", "snapPoints", "rootId", "mountPoint", "style", "detent", "initialSnap", "disableDrag", "prefersReducedMotion", "tweenConfig"];
555
524
  var Sheet = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
556
525
  var onOpenStart = _ref.onOpenStart,
557
- onOpenEnd = _ref.onOpenEnd,
558
- onClose = _ref.onClose,
559
- onCloseStart = _ref.onCloseStart,
560
- onCloseEnd = _ref.onCloseEnd,
561
- onSnap = _ref.onSnap,
562
- children = _ref.children,
563
- isOpen = _ref.isOpen,
564
- snapPoints = _ref.snapPoints,
565
- rootId = _ref.rootId,
566
- mountPoint = _ref.mountPoint,
567
- style = _ref.style,
568
- _ref$detent = _ref.detent,
569
- detent = _ref$detent === void 0 ? 'full-height' : _ref$detent,
570
- _ref$initialSnap = _ref.initialSnap,
571
- initialSnap = _ref$initialSnap === void 0 ? 0 : _ref$initialSnap,
572
- _ref$springConfig = _ref.springConfig,
573
- springConfig = _ref$springConfig === void 0 ? DEFAULT_SPRING_CONFIG : _ref$springConfig,
574
- _ref$disableDrag = _ref.disableDrag,
575
- disableDrag = _ref$disableDrag === void 0 ? false : _ref$disableDrag,
576
- _ref$prefersReducedMo = _ref.prefersReducedMotion,
577
- prefersReducedMotion = _ref$prefersReducedMo === void 0 ? false : _ref$prefersReducedMo,
578
- rest = _objectWithoutPropertiesLoose(_ref, ["onOpenStart", "onOpenEnd", "onClose", "onCloseStart", "onCloseEnd", "onSnap", "children", "isOpen", "snapPoints", "rootId", "mountPoint", "style", "detent", "initialSnap", "springConfig", "disableDrag", "prefersReducedMotion"]);
579
-
526
+ onOpenEnd = _ref.onOpenEnd,
527
+ onClose = _ref.onClose,
528
+ onCloseStart = _ref.onCloseStart,
529
+ onCloseEnd = _ref.onCloseEnd,
530
+ onSnap = _ref.onSnap,
531
+ children = _ref.children,
532
+ _ref$disableScrollLoc = _ref.disableScrollLocking,
533
+ disableScrollLocking = _ref$disableScrollLoc === void 0 ? false : _ref$disableScrollLoc,
534
+ isOpen = _ref.isOpen,
535
+ snapPoints = _ref.snapPoints,
536
+ rootId = _ref.rootId,
537
+ mountPoint = _ref.mountPoint,
538
+ style = _ref.style,
539
+ _ref$detent = _ref.detent,
540
+ detent = _ref$detent === void 0 ? 'full-height' : _ref$detent,
541
+ _ref$initialSnap = _ref.initialSnap,
542
+ initialSnap = _ref$initialSnap === void 0 ? 0 : _ref$initialSnap,
543
+ _ref$disableDrag = _ref.disableDrag,
544
+ disableDrag = _ref$disableDrag === void 0 ? false : _ref$disableDrag,
545
+ _ref$prefersReducedMo = _ref.prefersReducedMotion,
546
+ prefersReducedMotion = _ref$prefersReducedMo === void 0 ? false : _ref$prefersReducedMo,
547
+ _ref$tweenConfig = _ref.tweenConfig,
548
+ tweenConfig = _ref$tweenConfig === void 0 ? DEFAULT_TWEEN_CONFIG : _ref$tweenConfig,
549
+ rest = _objectWithoutPropertiesLoose(_ref, _excluded);
580
550
  var sheetRef = React.useRef(null);
581
551
  var indicatorRotation = framerMotion.useMotionValue(0);
582
552
  var windowHeight = useWindowHeight();
583
553
  var shouldReduceMotion = framerMotion.useReducedMotion();
584
554
  var reduceMotion = Boolean(prefersReducedMotion || shouldReduceMotion);
585
- var animationOptions = reduceMotion ? {
586
- type: 'tween',
587
- duration: 0.01
588
- } : _extends({
589
- type: 'spring'
590
- }, springConfig); // NOTE: the inital value for `y` doesn't matter since it is overwritten by
555
+ var animationOptions = _extends({
556
+ type: 'tween'
557
+ }, reduceMotion ? REDUCED_MOTION_TWEEN_CONFIG : tweenConfig);
558
+ // NOTE: the inital value for `y` doesn't matter since it is overwritten by
591
559
  // the value driven by the `AnimatePresence` component when the sheet is opened
592
560
  // and after that it is driven by the gestures and/or snapping
593
-
594
561
  var y = framerMotion.useMotionValue(0);
595
562
  var zIndex = framerMotion.useTransform(y, function (value) {
596
563
  return value >= windowHeight ? -1 : 9999999;
597
564
  });
598
565
  var visibility = framerMotion.useTransform(y, function (value) {
599
566
  return value >= windowHeight ? 'hidden' : 'visible';
600
- }); // Keep the callback fns up-to-date so that they can be accessed inside
567
+ });
568
+ // Keep the callback fns up-to-date so that they can be accessed inside
601
569
  // the effect without including them to the dependencies array
602
-
603
570
  var callbacks = React.useRef({
604
571
  onOpenStart: onOpenStart,
605
572
  onOpenEnd: onOpenEnd,
@@ -614,7 +581,6 @@ var Sheet = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
614
581
  onCloseEnd: onCloseEnd
615
582
  };
616
583
  });
617
-
618
584
  if (snapPoints) {
619
585
  // Convert negative / percentage snap points to absolute values
620
586
  snapPoints = snapPoints.map(function (point) {
@@ -622,21 +588,20 @@ var Sheet = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
622
588
  if (point > 0 && point <= 1) return Math.round(point * windowHeight);
623
589
  return point < 0 ? windowHeight + point : point; // negative values
624
590
  });
591
+
625
592
  console.assert(inDescendingOrder(snapPoints) || windowHeight === 0, "Snap points need to be in descending order got: [" + snapPoints + "]");
626
593
  }
627
-
628
594
  var onDrag = useEvent(function (_, _ref2) {
629
595
  var delta = _ref2.delta;
630
596
  // Update drag indicator rotation based on drag velocity
631
597
  var velocity = y.getVelocity();
632
598
  if (velocity > 0) indicatorRotation.set(10);
633
- if (velocity < 0) indicatorRotation.set(-10); // Make sure user cannot drag beyond the top of the sheet
634
-
599
+ if (velocity < 0) indicatorRotation.set(-10);
600
+ // Make sure user cannot drag beyond the top of the sheet
635
601
  y.set(Math.max(y.get() + delta.y, 0));
636
602
  });
637
603
  var onDragEnd = useEvent(function (_, _ref3) {
638
604
  var velocity = _ref3.velocity;
639
-
640
605
  if (velocity.y > DRAG_VELOCITY_THRESHOLD) {
641
606
  // User flicked the sheet down
642
607
  onClose();
@@ -645,58 +610,49 @@ var Sheet = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
645
610
  var sheetHeight = sheetEl.getBoundingClientRect().height;
646
611
  var currentY = y.get();
647
612
  var snapTo = 0;
648
-
649
613
  if (snapPoints) {
650
614
  var snapToValues = snapPoints.map(function (p) {
651
615
  return sheetHeight - Math.min(p, sheetHeight);
652
- }); // Allow snapping to the top of the sheet if detent is set to `content-height`
653
-
616
+ });
617
+ // Allow snapping to the top of the sheet if detent is set to `content-height`
654
618
  if (detent === 'content-height' && !snapToValues.includes(0)) {
655
619
  snapToValues.unshift(0);
656
- } // Get the closest snap point
657
-
658
-
620
+ }
621
+ // Get the closest snap point
659
622
  snapTo = getClosest(snapToValues, currentY);
660
623
  } else if (currentY / sheetHeight > DRAG_CLOSE_THRESHOLD) {
661
624
  // Close if dragged over enough far
662
625
  snapTo = sheetHeight;
663
626
  }
664
-
665
627
  snapTo = validateSnapTo({
666
628
  snapTo: snapTo,
667
629
  sheetHeight: sheetHeight
668
- }); // Update the spring value so that the sheet is animated to the snap point
669
-
630
+ });
631
+ // Update the spring value so that the sheet is animated to the snap point
670
632
  framerMotion.animate(y, snapTo, animationOptions);
671
-
672
633
  if (snapPoints && onSnap) {
673
634
  var snapValue = Math.abs(Math.round(snapPoints[0] - snapTo));
674
635
  var snapIndex = snapPoints.indexOf(getClosest(snapPoints, snapValue)); // prettier-ignore
675
-
676
636
  onSnap(snapIndex);
677
637
  }
678
-
679
638
  var roundedSheetHeight = Math.round(sheetHeight);
680
639
  var shouldClose = snapTo >= roundedSheetHeight;
681
640
  if (shouldClose) onClose();
682
- } // Reset indicator rotation after dragging
683
-
684
-
641
+ }
642
+ // Reset indicator rotation after dragging
685
643
  indicatorRotation.set(0);
686
- }); // Trigger onSnap callback when sheet is opened or closed
687
-
644
+ });
645
+ // Trigger onSnap callback when sheet is opened or closed
688
646
  React.useEffect(function () {
689
647
  if (!snapPoints || !onSnap) return;
690
648
  var snapIndex = isOpen ? initialSnap : snapPoints.length - 1;
691
649
  onSnap(snapIndex);
692
650
  }, [isOpen]); // eslint-disable-line
693
-
694
651
  React.useImperativeHandle(ref, function () {
695
652
  return {
696
653
  y: y,
697
654
  snapTo: function snapTo(snapIndex) {
698
655
  var sheetEl = sheetRef.current;
699
-
700
656
  if (snapPoints && snapPoints[snapIndex] !== undefined && sheetEl !== null) {
701
657
  var sheetHeight = sheetEl.getBoundingClientRect().height;
702
658
  var snapPoint = snapPoints[snapIndex];
@@ -711,11 +667,11 @@ var Sheet = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
711
667
  }
712
668
  };
713
669
  });
714
- useModalEffect(isOpen, rootId); // Framer Motion should handle body scroll locking but it's not working
670
+ useModalEffect(isOpen, rootId);
671
+ // Framer Motion should handle body scroll locking but it's not working
715
672
  // properly on iOS. Scroll locking from React Aria seems to work much better.
716
-
717
673
  usePreventScroll({
718
- isDisabled: !isOpen
674
+ isDisabled: disableScrollLocking === true || !isOpen
719
675
  });
720
676
  var dragProps = React.useMemo(function () {
721
677
  var dragProps = {
@@ -732,7 +688,6 @@ var Sheet = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
732
688
  };
733
689
  return disableDrag ? undefined : dragProps;
734
690
  }, [disableDrag]); // eslint-disable-line
735
-
736
691
  var context = {
737
692
  y: y,
738
693
  sheetRef: sheetRef,
@@ -745,7 +700,8 @@ var Sheet = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
745
700
  dragProps: dragProps,
746
701
  windowHeight: windowHeight,
747
702
  animationOptions: animationOptions,
748
- reduceMotion: reduceMotion
703
+ reduceMotion: reduceMotion,
704
+ disableDrag: disableDrag
749
705
  };
750
706
  var sheet = React.createElement(SheetContext.Provider, {
751
707
  value: context
@@ -755,39 +711,37 @@ var Sheet = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
755
711
  zIndex: zIndex,
756
712
  visibility: visibility
757
713
  }, style)
758
- }), React.createElement(framerMotion.AnimatePresence, null, isOpen ? React.Children.map(children, function (child, i) {
714
+ }), React.createElement(framerMotion.AnimatePresence, null, isOpen ? React.createElement(SheetScrollerContextProvider, null, React.Children.map(children, function (child, i) {
759
715
  return React.cloneElement(child, {
760
716
  key: "sheet-child-" + i
761
717
  });
762
- }) : null)));
718
+ })) : null)));
763
719
  if (IS_SSR) return sheet;
764
720
  return ReactDOM.createPortal(sheet, mountPoint != null ? mountPoint : document.body);
765
721
  });
766
722
 
723
+ var _excluded$1 = ["children", "style", "className"];
767
724
  var SheetContainer = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
768
725
  var children = _ref.children,
769
- _ref$style = _ref.style,
770
- style = _ref$style === void 0 ? {} : _ref$style,
771
- _ref$className = _ref.className,
772
- className = _ref$className === void 0 ? '' : _ref$className,
773
- rest = _objectWithoutPropertiesLoose(_ref, ["children", "style", "className"]);
774
-
726
+ _ref$style = _ref.style,
727
+ style = _ref$style === void 0 ? {} : _ref$style,
728
+ _ref$className = _ref.className,
729
+ className = _ref$className === void 0 ? '' : _ref$className,
730
+ rest = _objectWithoutPropertiesLoose(_ref, _excluded$1);
775
731
  var _useSheetContext = useSheetContext(),
776
- y = _useSheetContext.y,
777
- isOpen = _useSheetContext.isOpen,
778
- callbacks = _useSheetContext.callbacks,
779
- snapPoints = _useSheetContext.snapPoints,
780
- _useSheetContext$init = _useSheetContext.initialSnap,
781
- initialSnap = _useSheetContext$init === void 0 ? 0 : _useSheetContext$init,
782
- sheetRef = _useSheetContext.sheetRef,
783
- windowHeight = _useSheetContext.windowHeight,
784
- detent = _useSheetContext.detent,
785
- animationOptions = _useSheetContext.animationOptions,
786
- reduceMotion = _useSheetContext.reduceMotion;
787
-
732
+ y = _useSheetContext.y,
733
+ isOpen = _useSheetContext.isOpen,
734
+ callbacks = _useSheetContext.callbacks,
735
+ snapPoints = _useSheetContext.snapPoints,
736
+ _useSheetContext$init = _useSheetContext.initialSnap,
737
+ initialSnap = _useSheetContext$init === void 0 ? 0 : _useSheetContext$init,
738
+ sheetRef = _useSheetContext.sheetRef,
739
+ windowHeight = _useSheetContext.windowHeight,
740
+ detent = _useSheetContext.detent,
741
+ animationOptions = _useSheetContext.animationOptions,
742
+ reduceMotion = _useSheetContext.reduceMotion;
788
743
  var _useEventCallbacks = useEventCallbacks(isOpen, callbacks),
789
- handleAnimationComplete = _useEventCallbacks.handleAnimationComplete;
790
-
744
+ handleAnimationComplete = _useEventCallbacks.handleAnimationComplete;
791
745
  var initialY = snapPoints ? snapPoints[0] - snapPoints[initialSnap] : 0;
792
746
  var maxSnapHeight = snapPoints ? snapPoints[0] : null;
793
747
  var height = maxSnapHeight !== null ? "min(" + maxSnapHeight + "px, " + MAX_HEIGHT + ")" : MAX_HEIGHT;
@@ -816,38 +770,34 @@ var SheetContainer = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
816
770
  }), children);
817
771
  });
818
772
 
773
+ var _excluded$2 = ["children", "style", "disableDrag", "className"];
819
774
  var SheetContent = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
820
775
  var children = _ref.children,
821
- style = _ref.style,
822
- disableDrag = _ref.disableDrag,
823
- _ref$className = _ref.className,
824
- className = _ref$className === void 0 ? '' : _ref$className,
825
- rest = _objectWithoutPropertiesLoose(_ref, ["children", "style", "disableDrag", "className"]);
826
-
827
- var _useSheetContext = useSheetContext(),
828
- dragProps = _useSheetContext.dragProps;
829
-
830
- var _dragProps = disableDrag ? undefined : dragProps;
831
-
776
+ style = _ref.style,
777
+ disableDrag = _ref.disableDrag,
778
+ _ref$className = _ref.className,
779
+ className = _ref$className === void 0 ? '' : _ref$className,
780
+ rest = _objectWithoutPropertiesLoose(_ref, _excluded$2);
781
+ var sheetContext = useSheetContext();
782
+ var sheetScrollerContext = useSheetScrollerContext();
783
+ var dragProps = disableDrag || sheetScrollerContext.disableDrag ? undefined : sheetContext.dragProps;
832
784
  return React.createElement(framerMotion.motion.div, Object.assign({}, rest, {
833
785
  ref: ref,
834
786
  className: "react-modal-sheet-content " + className,
835
787
  style: _extends({}, styles.content, style)
836
- }, _dragProps), children);
788
+ }, dragProps), children);
837
789
  });
838
790
 
791
+ var _excluded$3 = ["children", "style", "disableDrag"];
839
792
  var SheetHeader = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
840
793
  var children = _ref.children,
841
- style = _ref.style,
842
- disableDrag = _ref.disableDrag,
843
- rest = _objectWithoutPropertiesLoose(_ref, ["children", "style", "disableDrag"]);
844
-
794
+ style = _ref.style,
795
+ disableDrag = _ref.disableDrag,
796
+ rest = _objectWithoutPropertiesLoose(_ref, _excluded$3);
845
797
  var _useSheetContext = useSheetContext(),
846
- indicatorRotation = _useSheetContext.indicatorRotation,
847
- dragProps = _useSheetContext.dragProps;
848
-
798
+ indicatorRotation = _useSheetContext.indicatorRotation,
799
+ dragProps = _useSheetContext.dragProps;
849
800
  var _dragProps = disableDrag ? undefined : dragProps;
850
-
851
801
  var indicator1Transform = framerMotion.useTransform(indicatorRotation, function (r) {
852
802
  return "translateX(2px) rotate(" + r + "deg)";
853
803
  });
@@ -873,17 +823,16 @@ var SheetHeader = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
873
823
  })));
874
824
  });
875
825
 
826
+ var _excluded$4 = ["style", "className"];
876
827
  var isClickable = function isClickable(props) {
877
828
  return !!props.onClick || !!props.onTap;
878
829
  };
879
-
880
830
  var SheetBackdrop = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
881
831
  var _ref$style = _ref.style,
882
- style = _ref$style === void 0 ? {} : _ref$style,
883
- _ref$className = _ref.className,
884
- className = _ref$className === void 0 ? '' : _ref$className,
885
- rest = _objectWithoutPropertiesLoose(_ref, ["style", "className"]);
886
-
832
+ style = _ref$style === void 0 ? {} : _ref$style,
833
+ _ref$className = _ref.className,
834
+ className = _ref$className === void 0 ? '' : _ref$className,
835
+ rest = _objectWithoutPropertiesLoose(_ref, _excluded$4);
887
836
  var Comp = isClickable(rest) ? framerMotion.motion.button : framerMotion.motion.div;
888
837
  var pointerEvents = isClickable(rest) ? 'auto' : 'none';
889
838
  return React.createElement(Comp, Object.assign({}, rest, {
@@ -904,11 +853,55 @@ var SheetBackdrop = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
904
853
  }));
905
854
  });
906
855
 
856
+ var _excluded$5 = ["draggableAt", "children", "style", "className"];
857
+ var SheetScroller = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
858
+ var _ref$draggableAt = _ref.draggableAt,
859
+ draggableAt = _ref$draggableAt === void 0 ? 'top' : _ref$draggableAt,
860
+ children = _ref.children,
861
+ style = _ref.style,
862
+ _ref$className = _ref.className,
863
+ className = _ref$className === void 0 ? '' : _ref$className,
864
+ rest = _objectWithoutPropertiesLoose(_ref, _excluded$5);
865
+ var sheetScrollerContext = useSheetScrollerContext();
866
+ function determineDragState(element) {
867
+ var scrollTop = element.scrollTop,
868
+ scrollHeight = element.scrollHeight,
869
+ clientHeight = element.clientHeight;
870
+ var isScrollable = scrollHeight > clientHeight;
871
+ if (!isScrollable) return;
872
+ var isAtTop = scrollTop <= 0;
873
+ var isAtBottom = scrollHeight - scrollTop === clientHeight;
874
+ var shouldEnable = draggableAt === 'top' && isAtTop || draggableAt === 'bottom' && isAtBottom || draggableAt === 'both' && (isAtTop || isAtBottom);
875
+ if (shouldEnable) {
876
+ sheetScrollerContext.setDragEnabled();
877
+ } else {
878
+ sheetScrollerContext.setDragDisabled();
879
+ }
880
+ }
881
+ function onScroll(e) {
882
+ determineDragState(e.currentTarget);
883
+ }
884
+ function onTouchStart(e) {
885
+ determineDragState(e.currentTarget);
886
+ }
887
+ var scrollProps = isTouchDevice() ? {
888
+ onScroll: onScroll,
889
+ onTouchStart: onTouchStart
890
+ } : undefined;
891
+ return React.createElement("div", Object.assign({}, rest, {
892
+ ref: ref,
893
+ className: "react-modal-sheet-scroller " + className,
894
+ style: _extends({}, styles.scroller, style)
895
+ }, scrollProps), children);
896
+ });
897
+
898
+ // HACK: this is needed to get the typing to work properly...
907
899
  var _SheetCompound = Sheet;
908
900
  _SheetCompound.Container = SheetContainer;
909
901
  _SheetCompound.Header = SheetHeader;
910
902
  _SheetCompound.Content = SheetContent;
911
903
  _SheetCompound.Backdrop = SheetBackdrop;
904
+ _SheetCompound.Scroller = SheetScroller;
912
905
 
913
906
  exports.default = _SheetCompound;
914
907
  //# sourceMappingURL=react-modal-sheet.cjs.development.js.map