react-modal-sheet 1.11.0 → 2.0.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.
@@ -42,20 +42,32 @@ function _objectWithoutPropertiesLoose(source, excluded) {
42
42
 
43
43
  var MAX_HEIGHT = 'calc(100% - env(safe-area-inset-top) - 34px)';
44
44
  var IS_SSR = typeof window === 'undefined';
45
- var DEFAULT_SPRING_CONFIG = {
46
- stiffness: 300,
47
- damping: 30,
48
- mass: 0.2
45
+ var DEFAULT_TWEEN_CONFIG = {
46
+ ease: 'easeOut',
47
+ duration: 0.2
48
+ };
49
+ var REDUCED_MOTION_TWEEN_CONFIG = {
50
+ ease: 'linear',
51
+ duration: 0.01
49
52
  };
50
53
  var DRAG_CLOSE_THRESHOLD = 0.6;
51
54
  var DRAG_VELOCITY_THRESHOLD = 500;
52
55
 
53
56
  function getClosest(nums, goal) {
54
- return nums.reduce(function (prev, curr) {
55
- return Math.abs(curr - goal) < Math.abs(prev - goal) ? curr : prev;
56
- });
57
+ var closest = nums[0];
58
+ var minDifference = Math.abs(nums[0] - goal);
59
+
60
+ for (var i = 1; i < nums.length; i++) {
61
+ var difference = Math.abs(nums[i] - goal);
62
+
63
+ if (difference < minDifference) {
64
+ closest = nums[i];
65
+ minDifference = difference;
66
+ }
67
+ }
68
+
69
+ return closest;
57
70
  }
58
- var highlightId = 'react-modal-sheet-highlight';
59
71
  function applyRootStyles(rootId) {
60
72
  var body = document.querySelector('body');
61
73
  var root = document.querySelector("#" + rootId);
@@ -67,51 +79,30 @@ function applyRootStyles(rootId) {
67
79
  body.style.backgroundColor = '#000';
68
80
  root.style.overflow = 'hidden';
69
81
  root.style.willChange = 'transform';
70
- root.style.transition = 'transform 200ms linear';
82
+ root.style.transition = 'transform 200ms ease-in-out, border-radius 200ms linear';
71
83
  root.style.transform = "translateY(calc(env(safe-area-inset-top) + " + p / 2 + "px)) scale(" + s + ")"; // prettier-ignore
72
84
 
73
85
  root.style.borderTopRightRadius = '10px';
74
- root.style.borderTopLeftRadius = '10px'; // Add highlighed overlay to emphasize the modality effect
75
-
76
- var highlight = document.createElement('div');
77
- highlight.setAttribute('aria-hidden', 'true');
78
- highlight.id = highlightId;
79
- highlight.style.position = 'absolute';
80
- highlight.style.top = '0px';
81
- highlight.style.left = '0px';
82
- highlight.style.bottom = '0px';
83
- highlight.style.right = '0px';
84
- highlight.style.opacity = '0';
85
- highlight.style.transition = 'opacity 200ms linear';
86
- highlight.style.backgroundColor = 'rgba(150, 150, 150, 0.1)';
87
- root.appendChild(highlight);
88
- requestAnimationFrame(function () {
89
- return highlight.style.opacity = '1';
90
- });
86
+ root.style.borderTopLeftRadius = '10px';
91
87
  }
92
88
  }
93
89
  function cleanupRootStyles(rootId) {
94
90
  var body = document.querySelector('body');
95
91
  var root = document.getElementById(rootId);
96
- var highlight = document.getElementById(highlightId);
97
92
 
98
93
  function onTransitionEnd() {
99
- var _highlight$parentNode;
100
-
101
94
  root.style.removeProperty('overflow');
102
95
  root.style.removeProperty('will-change');
103
96
  root.style.removeProperty('transition');
104
97
  body.style.removeProperty('background-color');
105
98
  root.removeEventListener('transitionend', onTransitionEnd);
106
- (_highlight$parentNode = highlight.parentNode) == null ? void 0 : _highlight$parentNode.removeChild(highlight);
107
99
  }
108
100
 
109
- if (root && highlight) {
101
+ if (root) {
110
102
  // Start animating back
111
103
  root.style.removeProperty('border-top-right-radius');
112
104
  root.style.removeProperty('border-top-left-radius');
113
- root.style.removeProperty('transform');
114
- highlight.style.opacity = '0'; // Remove temp properties after animation is finished
105
+ root.style.removeProperty('transform'); // Remove temp properties after animation is finished
115
106
 
116
107
  root.addEventListener('transitionend', onTransitionEnd);
117
108
  }
@@ -144,9 +135,13 @@ function mergeRefs(refs) {
144
135
  });
145
136
  };
146
137
  }
138
+ function isTouchDevice() {
139
+ if (typeof window === 'undefined') return false;
140
+ return 'ontouchstart' in window || navigator.maxTouchPoints > 0;
141
+ }
147
142
 
148
143
  var useIsomorphicLayoutEffect = IS_SSR ? React.useEffect : React.useLayoutEffect;
149
- var useModalEffect = function useModalEffect(isOpen, rootId) {
144
+ function useModalEffect(isOpen, rootId) {
150
145
  var prevOpen = usePrevious(isOpen); // Automatically apply the iOS modal effect to the body when sheet opens/closes
151
146
 
152
147
  React.useEffect(function () {
@@ -163,8 +158,8 @@ var useModalEffect = function useModalEffect(isOpen, rootId) {
163
158
  if (rootId && isOpen) cleanupRootStyles(rootId);
164
159
  };
165
160
  }, [isOpen]); // eslint-disable-line
166
- };
167
- var useEventCallbacks = function useEventCallbacks(isOpen, callbacks) {
161
+ }
162
+ function useEventCallbacks(isOpen, callbacks) {
168
163
  var prevOpen = usePrevious(isOpen);
169
164
  var didOpen = React.useRef(false); // Because of AnimatePrecence we don't have access to latest isOpen value
170
165
  // so we need to read and write to a ref to determine if we are
@@ -191,7 +186,7 @@ var useEventCallbacks = function useEventCallbacks(isOpen, callbacks) {
191
186
  return {
192
187
  handleAnimationComplete: handleAnimationComplete
193
188
  };
194
- };
189
+ }
195
190
  function useWindowHeight() {
196
191
  var _React$useState = React.useState(0),
197
192
  windowHeight = _React$useState[0],
@@ -241,6 +236,36 @@ var useSheetContext = function useSheetContext() {
241
236
  if (!context) throw Error('Sheet context error');
242
237
  return context;
243
238
  };
239
+ var SheetScrollerContext = /*#__PURE__*/React.createContext(undefined);
240
+ function SheetScrollerContextProvider(_ref) {
241
+ var children = _ref.children;
242
+ var sheetContext = useSheetContext();
243
+
244
+ var _React$useState = React.useState(!!sheetContext.disableDrag),
245
+ disableDrag = _React$useState[0],
246
+ setDisableDrag = _React$useState[1];
247
+
248
+ function setDragEnabled() {
249
+ if (!sheetContext.disableDrag) setDisableDrag(false);
250
+ }
251
+
252
+ function setDragDisabled() {
253
+ if (!disableDrag) setDisableDrag(true);
254
+ }
255
+
256
+ return React.createElement(SheetScrollerContext.Provider, {
257
+ value: {
258
+ disableDrag: disableDrag,
259
+ setDragEnabled: setDragEnabled,
260
+ setDragDisabled: setDragDisabled
261
+ }
262
+ }, children);
263
+ }
264
+ var useSheetScrollerContext = function useSheetScrollerContext() {
265
+ var context = React.useContext(SheetScrollerContext);
266
+ if (!context) throw Error('Sheet scroller context error');
267
+ return context;
268
+ };
244
269
 
245
270
  // Source: https://github.com/adobe/react-spectrum/blob/main/packages/@react-aria/overlays/src/usePreventScroll.ts
246
271
 
@@ -500,7 +525,7 @@ var styles = {
500
525
  left: 0,
501
526
  width: '100%',
502
527
  height: '100%',
503
- backgroundColor: 'rgba(51, 51, 51, 0.5)',
528
+ backgroundColor: 'rgba(0, 0, 0, 0.2)',
504
529
  touchAction: 'none',
505
530
  border: 'none'
506
531
  },
@@ -536,9 +561,15 @@ var styles = {
536
561
  backgroundColor: '#ddd'
537
562
  },
538
563
  content: {
539
- flex: 1,
540
- overflow: 'auto',
564
+ flexGrow: 1,
565
+ display: 'flex',
566
+ flexDirection: 'column',
567
+ minHeight: '0px',
541
568
  position: 'relative'
569
+ },
570
+ scroller: {
571
+ height: '100%',
572
+ overflowY: 'auto'
542
573
  }
543
574
  };
544
575
 
@@ -559,28 +590,27 @@ var Sheet = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
559
590
  detent = _ref$detent === void 0 ? 'full-height' : _ref$detent,
560
591
  _ref$initialSnap = _ref.initialSnap,
561
592
  initialSnap = _ref$initialSnap === void 0 ? 0 : _ref$initialSnap,
562
- _ref$springConfig = _ref.springConfig,
563
- springConfig = _ref$springConfig === void 0 ? DEFAULT_SPRING_CONFIG : _ref$springConfig,
564
593
  _ref$disableDrag = _ref.disableDrag,
565
594
  disableDrag = _ref$disableDrag === void 0 ? false : _ref$disableDrag,
566
595
  _ref$prefersReducedMo = _ref.prefersReducedMotion,
567
596
  prefersReducedMotion = _ref$prefersReducedMo === void 0 ? false : _ref$prefersReducedMo,
568
- rest = _objectWithoutPropertiesLoose(_ref, ["onOpenStart", "onOpenEnd", "onClose", "onCloseStart", "onCloseEnd", "onSnap", "children", "isOpen", "snapPoints", "rootId", "mountPoint", "style", "detent", "initialSnap", "springConfig", "disableDrag", "prefersReducedMotion"]);
597
+ _ref$tweenConfig = _ref.tweenConfig,
598
+ tweenConfig = _ref$tweenConfig === void 0 ? DEFAULT_TWEEN_CONFIG : _ref$tweenConfig,
599
+ rest = _objectWithoutPropertiesLoose(_ref, ["onOpenStart", "onOpenEnd", "onClose", "onCloseStart", "onCloseEnd", "onSnap", "children", "isOpen", "snapPoints", "rootId", "mountPoint", "style", "detent", "initialSnap", "disableDrag", "prefersReducedMotion", "tweenConfig"]);
569
600
 
570
601
  var sheetRef = React.useRef(null);
571
602
  var indicatorRotation = framerMotion.useMotionValue(0);
572
603
  var windowHeight = useWindowHeight();
573
604
  var shouldReduceMotion = framerMotion.useReducedMotion();
574
605
  var reduceMotion = Boolean(prefersReducedMotion || shouldReduceMotion);
575
- var animationOptions = reduceMotion ? {
576
- type: 'tween',
577
- duration: 0.01
578
- } : _extends({
579
- type: 'spring'
580
- }, springConfig); // NOTE: the inital value for `y` doesn't matter since it is overwritten by
606
+
607
+ var animationOptions = _extends({
608
+ type: 'tween'
609
+ }, reduceMotion ? REDUCED_MOTION_TWEEN_CONFIG : tweenConfig); // NOTE: the inital value for `y` doesn't matter since it is overwritten by
581
610
  // the value driven by the `AnimatePresence` component when the sheet is opened
582
611
  // and after that it is driven by the gestures and/or snapping
583
612
 
613
+
584
614
  var y = framerMotion.useMotionValue(0);
585
615
  var zIndex = framerMotion.useTransform(y, function (value) {
586
616
  return value >= windowHeight ? -1 : 9999999;
@@ -638,11 +668,8 @@ var Sheet = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
638
668
 
639
669
  if (snapPoints) {
640
670
  var snapToValues = snapPoints.map(function (p) {
641
- return sheetHeight - p;
642
- }).filter(function (p) {
643
- return p >= 0;
644
- }); // negative values can occur with `content-height` detent
645
- // Allow snapping to the top of the sheet if detent is set to `content-height`
671
+ return sheetHeight - Math.min(p, sheetHeight);
672
+ }); // Allow snapping to the top of the sheet if detent is set to `content-height`
646
673
 
647
674
  if (detent === 'content-height' && !snapToValues.includes(0)) {
648
675
  snapToValues.unshift(0);
@@ -738,7 +765,8 @@ var Sheet = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
738
765
  dragProps: dragProps,
739
766
  windowHeight: windowHeight,
740
767
  animationOptions: animationOptions,
741
- reduceMotion: reduceMotion
768
+ reduceMotion: reduceMotion,
769
+ disableDrag: disableDrag
742
770
  };
743
771
  var sheet = React.createElement(SheetContext.Provider, {
744
772
  value: context
@@ -748,11 +776,11 @@ var Sheet = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
748
776
  zIndex: zIndex,
749
777
  visibility: visibility
750
778
  }, style)
751
- }), React.createElement(framerMotion.AnimatePresence, null, isOpen ? React.Children.map(children, function (child, i) {
779
+ }), React.createElement(framerMotion.AnimatePresence, null, isOpen ? React.createElement(SheetScrollerContextProvider, null, React.Children.map(children, function (child, i) {
752
780
  return React.cloneElement(child, {
753
781
  key: "sheet-child-" + i
754
782
  });
755
- }) : null)));
783
+ })) : null)));
756
784
  if (IS_SSR) return sheet;
757
785
  return ReactDOM.createPortal(sheet, mountPoint != null ? mountPoint : document.body);
758
786
  });
@@ -817,16 +845,14 @@ var SheetContent = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
817
845
  className = _ref$className === void 0 ? '' : _ref$className,
818
846
  rest = _objectWithoutPropertiesLoose(_ref, ["children", "style", "disableDrag", "className"]);
819
847
 
820
- var _useSheetContext = useSheetContext(),
821
- dragProps = _useSheetContext.dragProps;
822
-
823
- var _dragProps = disableDrag ? undefined : dragProps;
824
-
848
+ var sheetContext = useSheetContext();
849
+ var sheetScrollerContext = useSheetScrollerContext();
850
+ var dragProps = disableDrag || sheetScrollerContext.disableDrag ? undefined : sheetContext.dragProps;
825
851
  return React.createElement(framerMotion.motion.div, Object.assign({}, rest, {
826
852
  ref: ref,
827
853
  className: "react-modal-sheet-content " + className,
828
854
  style: _extends({}, styles.content, style)
829
- }, _dragProps), children);
855
+ }, dragProps), children);
830
856
  });
831
857
 
832
858
  var SheetHeader = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
@@ -897,11 +923,59 @@ var SheetBackdrop = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
897
923
  }));
898
924
  });
899
925
 
926
+ var SheetScroller = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
927
+ var _ref$draggableAt = _ref.draggableAt,
928
+ draggableAt = _ref$draggableAt === void 0 ? 'top' : _ref$draggableAt,
929
+ children = _ref.children,
930
+ style = _ref.style,
931
+ _ref$className = _ref.className,
932
+ className = _ref$className === void 0 ? '' : _ref$className,
933
+ rest = _objectWithoutPropertiesLoose(_ref, ["draggableAt", "children", "style", "className"]);
934
+
935
+ var sheetScrollerContext = useSheetScrollerContext();
936
+
937
+ function determineDragState(element) {
938
+ var scrollTop = element.scrollTop,
939
+ scrollHeight = element.scrollHeight,
940
+ clientHeight = element.clientHeight;
941
+ var isScrollable = scrollHeight > clientHeight;
942
+ if (!isScrollable) return;
943
+ var isAtTop = scrollTop <= 0;
944
+ var isAtBottom = scrollHeight - scrollTop === clientHeight;
945
+ var shouldEnable = draggableAt === 'top' && isAtTop || draggableAt === 'bottom' && isAtBottom || draggableAt === 'both' && (isAtTop || isAtBottom);
946
+
947
+ if (shouldEnable) {
948
+ sheetScrollerContext.setDragEnabled();
949
+ } else {
950
+ sheetScrollerContext.setDragDisabled();
951
+ }
952
+ }
953
+
954
+ function onScroll(e) {
955
+ determineDragState(e.currentTarget);
956
+ }
957
+
958
+ function onTouchStart(e) {
959
+ determineDragState(e.currentTarget);
960
+ }
961
+
962
+ var scrollProps = isTouchDevice() ? {
963
+ onScroll: onScroll,
964
+ onTouchStart: onTouchStart
965
+ } : undefined;
966
+ return React.createElement("div", Object.assign({}, rest, {
967
+ ref: ref,
968
+ className: "react-modal-sheet-scroller " + className,
969
+ style: _extends({}, styles.scroller, style)
970
+ }, scrollProps), children);
971
+ });
972
+
900
973
  var _SheetCompound = Sheet;
901
974
  _SheetCompound.Container = SheetContainer;
902
975
  _SheetCompound.Header = SheetHeader;
903
976
  _SheetCompound.Content = SheetContent;
904
977
  _SheetCompound.Backdrop = SheetBackdrop;
978
+ _SheetCompound.Scroller = SheetScroller;
905
979
 
906
980
  exports.default = _SheetCompound;
907
981
  //# sourceMappingURL=react-modal-sheet.cjs.development.js.map