react-modal-sheet 1.6.3 → 1.7.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.
package/README.md CHANGED
@@ -40,11 +40,11 @@ npm install framer-motion
40
40
  ## Usage
41
41
 
42
42
  ```jsx
43
- import React from 'react';
44
43
  import Sheet from 'react-modal-sheet';
44
+ import { useState } from 'react';
45
45
 
46
46
  function Example() {
47
- const [isOpen, setOpen] = React.useState(false);
47
+ const [isOpen, setOpen] = useState(false);
48
48
 
49
49
  return (
50
50
  <>
@@ -77,6 +77,7 @@ Also, by constructing the sheet from smaller pieces makes it easier to apply any
77
77
  | `isOpen` | yes | | Boolean that indicates whether the sheet is open or not. |
78
78
  | `onClose` | yes | | Callback fn that is called when the sheet is closed by the user. |
79
79
  | `disableDrag` | no | false | Disable drag for the whole sheet. |
80
+ | `detent` | no | `'full-height'` | The [detent](https://developer.apple.com/design/human-interface-guidelines/components/presentation/sheets#ios-ipados) in which the sheet should be in when opened. Available values: `'full-height'` or `'content-height'`. |
80
81
  | `onOpenStart` | no | | Callback fn that is called when the sheet opening animation starts. |
81
82
  | `onOpenEnd` | no | | Callback fn that is called when the sheet opening animation is completed. |
82
83
  | `onCloseStart` | no | | Callback fn that is called when the sheet closing animation starts. |
@@ -107,12 +108,12 @@ Spring animation happens when the sheet is opened/closed or when it snaps to a s
107
108
  Imperative method that can be accessed via a ref for snapping to a snap point in given index.
108
109
 
109
110
  ```tsx
110
- import React from 'react';
111
111
  import Sheet, { SheetRef } from 'react-modal-sheet';
112
+ import { useState, useRef } from 'react';
112
113
 
113
114
  function Example() {
114
- const [isOpen, setOpen] = React.useState(false);
115
- const ref = React.useRef<SheetRef>();
115
+ const [isOpen, setOpen] = useState(false);
116
+ const ref = useRef<SheetRef>();
116
117
  const snapTo = (i: number) => ref.current?.snapTo(i);
117
118
 
118
119
  return (
@@ -153,12 +154,12 @@ Similarly to the `snapTo` method the `y` value can be accessed via a ref.
153
154
  The `y` value can be useful for certain situtation eg. when you want to combine snap points with scrollable sheet content and ensure that the content stays properly scrollable in any snap point. Below you can see a simplified example of this situation and for a more detailed example take a look at the [ScrollableSnapPoints](example/components/ScrollableSnapPoints.tsx) component in the example app.
154
155
 
155
156
  ```tsx
156
- import React from 'react';
157
157
  import Sheet, { SheetRef } from 'react-modal-sheet';
158
+ import { useState, useRef } from 'react';
158
159
 
159
160
  function Example() {
160
- const [isOpen, setOpen] = React.useState(false);
161
- const ref = React.useRef<SheetRef>();
161
+ const [isOpen, setOpen] = useState(false);
162
+ const ref = useRef<SheetRef>();
162
163
 
163
164
  return (
164
165
  <>
@@ -189,6 +190,36 @@ function Example() {
189
190
  }
190
191
  ```
191
192
 
193
+ ### Detents
194
+
195
+ By default the sheet will take the full height of the page minus top padding and safe area inset. If you want the sheet height to be based on it's content you can pass `detent="content-height"` prop to the `Sheet` component:
196
+
197
+ ```tsx
198
+ function Example() {
199
+ const [isOpen, setOpen] = useState(false);
200
+
201
+ return (
202
+ <Sheet
203
+ isOpen={isOpen}
204
+ onClose={() => setOpen(false)}
205
+ detent="content-height"
206
+ >
207
+ <Sheet.Container>
208
+ <Sheet.Content>
209
+ <div style={{ height: 200 }}>Some content</div>
210
+ </Sheet.Content>
211
+ </Sheet.Container>
212
+ </Sheet>
213
+ );
214
+ }
215
+ ```
216
+
217
+ If the sheet height changes dynamically the sheet will grow until it hits the maximum full height after which it becomes scrollable.
218
+
219
+ It is possible to use snap points with `detent="content-height"` **but** the snap points are restricted by the content height. For example if one of the snap points is 800px and the sheet height is only 700px then snapping to the 800px snap point would only snap to 700px since otherwise the sheet would become detached from the bottom.
220
+
221
+ > ℹ️ If you are wondering where the term `detent` comes from it's from Apple's [Human Interface Guidelines](<[detent](https://developer.apple.com/design/human-interface-guidelines/components/presentation/sheets#ios-ipados)>).
222
+
192
223
  ## Compound Components
193
224
 
194
225
  ### `Sheet.Container`
@@ -300,9 +331,9 @@ You can add your own styles or override the default sheet styles via the exposed
300
331
  #### CSS-in-JS
301
332
 
302
333
  ```jsx
303
- import React from 'react';
304
- import styled from 'styled-components';
305
334
  import Sheet from 'react-modal-sheeet';
335
+ import styled from 'styled-components';
336
+ import { useState } from 'react';
306
337
 
307
338
  const CustomSheet = styled(Sheet)`
308
339
  .react-modal-sheet-backdrop {
@@ -323,7 +354,7 @@ const CustomSheet = styled(Sheet)`
323
354
  `;
324
355
 
325
356
  function Example() {
326
- const [isOpen, setOpen] = React.useState(false);
357
+ const [isOpen, setOpen] = useState(false);
327
358
 
328
359
  return (
329
360
  <>
@@ -351,8 +382,8 @@ The example below utilizes React Aria to achieve an accessible modal-like bottom
351
382
  > ℹ️ The example was built by following the React Aria's [useDialog](https://react-spectrum.adobe.com/react-aria/useDialog.html) documentation.
352
383
 
353
384
  ```jsx
354
- import React from 'react';
355
385
  import Sheet from 'react-modal-sheet';
386
+ import { useRef } from 'react';
356
387
  import { useOverlayTriggerState } from 'react-stately';
357
388
 
358
389
  import {
@@ -366,7 +397,7 @@ import {
366
397
 
367
398
  const A11yExample = () => {
368
399
  const sheetState = useOverlayTriggerState({});
369
- const openButtonRef = React.useRef(null);
400
+ const openButtonRef = useRef(null);
370
401
  const openButton = useButton({ onPress: sheetState.open }, openButtonRef);
371
402
 
372
403
  return (
@@ -387,14 +418,14 @@ const A11yExample = () => {
387
418
  };
388
419
 
389
420
  const SheetComp = ({ sheetState }) => {
390
- const containerRef = React.useRef(null);
421
+ const containerRef = useRef(null);
391
422
  const dialog = useDialog({}, containerRef);
392
423
  const overlay = useOverlay(
393
424
  { onClose: sheetState.close, isOpen: true, isDismissable: true },
394
425
  containerRef
395
426
  );
396
427
 
397
- const closeButtonRef = React.useRef(null);
428
+ const closeButtonRef = useRef(null);
398
429
  const closeButton = useButton(
399
430
  { onPress: sheetState.close, 'aria-label': 'Close sheet' },
400
431
  closeButtonRef
@@ -1,3 +1,3 @@
1
1
  import * as React from 'react';
2
- declare const SheetBackdrop: React.ForwardRefExoticComponent<Pick<import("framer-motion").MotionProps, "style" | "transformTemplate" | "transformValues" | "variants" | "transition" | "onViewportBoxUpdate" | "onBeforeLayoutMeasure" | "onLayoutMeasure" | "onUpdate" | "onAnimationStart" | "onAnimationComplete" | "onLayoutAnimationComplete" | "onUnmount" | "onPan" | "onPanStart" | "onPanSessionStart" | "onPanEnd" | "onTap" | "onTapStart" | "onTapCancel" | "whileTap" | "whileHover" | "onHoverStart" | "onHoverEnd" | "whileFocus" | "drag" | "whileDrag" | "dragDirectionLock" | "dragPropagation" | "dragConstraints" | "dragElastic" | "dragMomentum" | "dragTransition" | "dragControls" | "dragListener" | "onMeasureDragConstraints" | "_dragX" | "_dragY" | "onDragStart" | "onDragEnd" | "onDrag" | "onDirectionLock" | "onDragTransitionEnd" | "layout" | "layoutId" | "custom" | "inherit"> & React.RefAttributes<any>>;
2
+ declare const SheetBackdrop: React.ForwardRefExoticComponent<Pick<import("framer-motion").MotionProps, "style" | "transformTemplate" | "transformValues" | "variants" | "transition" | "onBeforeLayoutMeasure" | "onLayoutMeasure" | "onLayoutAnimationStart" | "onLayoutAnimationComplete" | "onUpdate" | "onAnimationStart" | "onAnimationComplete" | "onUnmount" | "onPan" | "onPanStart" | "onPanSessionStart" | "onPanEnd" | "onTap" | "onTapStart" | "onTapCancel" | "whileTap" | "whileHover" | "onHoverStart" | "onHoverEnd" | "whileFocus" | "whileInView" | "onViewportEnter" | "onViewportLeave" | "viewport" | "drag" | "whileDrag" | "dragDirectionLock" | "dragPropagation" | "dragConstraints" | "dragElastic" | "dragMomentum" | "dragTransition" | "dragControls" | "dragSnapToOrigin" | "dragListener" | "onMeasureDragConstraints" | "_dragX" | "_dragY" | "onDragStart" | "onDragEnd" | "onDrag" | "onDirectionLock" | "onDragTransitionEnd" | "layout" | "layoutId" | "layoutDependency" | "layoutScroll" | "custom" | "inherit"> & React.RefAttributes<any>>;
3
3
  export default SheetBackdrop;
@@ -1,5 +1,5 @@
1
1
  import * as React from 'react';
2
- declare const SheetContent: React.ForwardRefExoticComponent<Pick<import("framer-motion").MotionProps, "initial" | "style" | "transformTemplate" | "transformValues" | "animate" | "exit" | "variants" | "transition" | "onViewportBoxUpdate" | "onBeforeLayoutMeasure" | "onLayoutMeasure" | "onUpdate" | "onAnimationStart" | "onAnimationComplete" | "onLayoutAnimationComplete" | "onPan" | "onPanStart" | "onPanSessionStart" | "onPanEnd" | "onTap" | "onTapStart" | "onTapCancel" | "whileTap" | "whileHover" | "onHoverStart" | "onHoverEnd" | "whileFocus" | "whileDrag" | "dragDirectionLock" | "dragPropagation" | "dragTransition" | "dragControls" | "dragListener" | "onMeasureDragConstraints" | "_dragX" | "_dragY" | "onDirectionLock" | "onDragTransitionEnd" | "layout" | "layoutId" | "custom" | "inherit"> & {
2
+ declare const SheetContent: React.ForwardRefExoticComponent<Pick<import("framer-motion").MotionProps, "style" | "transformTemplate" | "transformValues" | "initial" | "animate" | "exit" | "variants" | "transition" | "onBeforeLayoutMeasure" | "onLayoutMeasure" | "onLayoutAnimationStart" | "onLayoutAnimationComplete" | "onUpdate" | "onAnimationStart" | "onAnimationComplete" | "onUnmount" | "onPan" | "onPanStart" | "onPanSessionStart" | "onPanEnd" | "onTap" | "onTapStart" | "onTapCancel" | "whileTap" | "whileHover" | "onHoverStart" | "onHoverEnd" | "whileFocus" | "whileInView" | "onViewportEnter" | "onViewportLeave" | "viewport" | "whileDrag" | "dragDirectionLock" | "dragPropagation" | "dragTransition" | "dragControls" | "dragSnapToOrigin" | "dragListener" | "onMeasureDragConstraints" | "_dragX" | "_dragY" | "onDirectionLock" | "onDragTransitionEnd" | "layout" | "layoutId" | "layoutDependency" | "layoutScroll" | "custom" | "inherit"> & {
3
3
  children?: React.ReactNode;
4
4
  disableDrag?: boolean | undefined;
5
5
  } & React.RefAttributes<any>>;
@@ -1,5 +1,5 @@
1
1
  import * as React from 'react';
2
- declare const SheetHeader: React.ForwardRefExoticComponent<Pick<import("framer-motion").MotionProps, "initial" | "style" | "transformTemplate" | "transformValues" | "animate" | "exit" | "variants" | "transition" | "onViewportBoxUpdate" | "onBeforeLayoutMeasure" | "onLayoutMeasure" | "onUpdate" | "onAnimationStart" | "onAnimationComplete" | "onLayoutAnimationComplete" | "onPan" | "onPanStart" | "onPanSessionStart" | "onPanEnd" | "onTap" | "onTapStart" | "onTapCancel" | "whileTap" | "whileHover" | "onHoverStart" | "onHoverEnd" | "whileFocus" | "whileDrag" | "dragDirectionLock" | "dragPropagation" | "dragTransition" | "dragControls" | "dragListener" | "onMeasureDragConstraints" | "_dragX" | "_dragY" | "onDirectionLock" | "onDragTransitionEnd" | "layout" | "layoutId" | "custom" | "inherit"> & {
2
+ declare const SheetHeader: React.ForwardRefExoticComponent<Pick<import("framer-motion").MotionProps, "style" | "transformTemplate" | "transformValues" | "initial" | "animate" | "exit" | "variants" | "transition" | "onBeforeLayoutMeasure" | "onLayoutMeasure" | "onLayoutAnimationStart" | "onLayoutAnimationComplete" | "onUpdate" | "onAnimationStart" | "onAnimationComplete" | "onUnmount" | "onPan" | "onPanStart" | "onPanSessionStart" | "onPanEnd" | "onTap" | "onTapStart" | "onTapCancel" | "whileTap" | "whileHover" | "onHoverStart" | "onHoverEnd" | "whileFocus" | "whileInView" | "onViewportEnter" | "onViewportLeave" | "viewport" | "whileDrag" | "dragDirectionLock" | "dragPropagation" | "dragTransition" | "dragControls" | "dragSnapToOrigin" | "dragListener" | "onMeasureDragConstraints" | "_dragX" | "_dragY" | "onDirectionLock" | "onDragTransitionEnd" | "layout" | "layoutId" | "layoutDependency" | "layoutScroll" | "custom" | "inherit"> & {
3
3
  children?: React.ReactNode;
4
4
  disableDrag?: boolean | undefined;
5
5
  } & React.RefAttributes<any>>;
@@ -0,0 +1,9 @@
1
+ export declare const MAX_HEIGHT = "calc(100% - env(safe-area-inset-top) - 34px)";
2
+ export declare const IS_SSR: boolean;
3
+ export declare const DEFAULT_SPRING_CONFIG: {
4
+ stiffness: number;
5
+ damping: number;
6
+ mass: number;
7
+ };
8
+ export declare const DRAG_CLOSE_THRESHOLD = 0.6;
9
+ export declare const DRAG_VELOCITY_THRESHOLD = 500;
package/dist/hooks.d.ts CHANGED
@@ -1,6 +1,10 @@
1
1
  import * as React from 'react';
2
2
  import { SheetEvents } from './types';
3
+ export declare const useIsomorphicLayoutEffect: typeof React.useEffect;
3
4
  export declare const useModalEffect: (isOpen: boolean, rootId?: string | undefined) => void;
4
5
  export declare const useEventCallbacks: (isOpen: boolean, callbacks: React.MutableRefObject<SheetEvents>) => {
5
6
  handleAnimationComplete: () => void;
6
7
  };
8
+ export declare function useWindowHeight(): number;
9
+ export declare function usePrevious<T>(state: T): T | undefined;
10
+ export declare function useEvent<T extends (...args: any[]) => any>(handler: T): T;
@@ -43,12 +43,21 @@ function _objectWithoutPropertiesLoose(source, excluded) {
43
43
  return target;
44
44
  }
45
45
 
46
- var isSSR = typeof window === 'undefined';
47
- var getClosest = function getClosest(nums, goal) {
46
+ var MAX_HEIGHT = 'calc(100% - env(safe-area-inset-top) - 34px)';
47
+ var IS_SSR = typeof window === 'undefined';
48
+ var DEFAULT_SPRING_CONFIG = {
49
+ stiffness: 300,
50
+ damping: 30,
51
+ mass: 0.2
52
+ };
53
+ var DRAG_CLOSE_THRESHOLD = 0.6;
54
+ var DRAG_VELOCITY_THRESHOLD = 500;
55
+
56
+ function getClosest(nums, goal) {
48
57
  return nums.reduce(function (prev, curr) {
49
58
  return Math.abs(curr - goal) < Math.abs(prev - goal) ? curr : prev;
50
59
  });
51
- };
60
+ }
52
61
  var highlightId = 'react-modal-sheet-highlight';
53
62
  function applyRootStyles(rootId) {
54
63
  var body = document.querySelector('body');
@@ -110,40 +119,25 @@ function cleanupRootStyles(rootId) {
110
119
  root.addEventListener('transitionend', onTransitionEnd);
111
120
  }
112
121
  }
113
- var useIsomorphicLayoutEffect = isSSR ? React.useEffect : React.useLayoutEffect;
114
- function useWindowHeight() {
115
- var _useState = React.useState(0),
116
- windowHeight = _useState[0],
117
- setWindowHeight = _useState[1];
118
-
119
- useIsomorphicLayoutEffect(function () {
120
- var updateHeight = function updateHeight() {
121
- return setWindowHeight(window.innerHeight);
122
- };
123
-
124
- window.addEventListener('resize', updateHeight);
125
- updateHeight();
126
- return function () {
127
- return window.removeEventListener('resize', updateHeight);
128
- };
129
- }, []);
130
- return windowHeight;
131
- }
132
- var inDescendingOrder = function inDescendingOrder(arr) {
122
+ function inDescendingOrder(arr) {
133
123
  for (var i = 0; i < arr.length; i++) {
134
124
  if (arr[i + 1] > arr[i]) return false;
135
125
  }
136
126
 
137
127
  return true;
138
- };
128
+ }
129
+ function validateSnapTo(_ref) {
130
+ var snapTo = _ref.snapTo,
131
+ sheetHeight = _ref.sheetHeight;
139
132
 
140
- var SheetContext = /*#__PURE__*/React.createContext(undefined);
141
- var useSheetContext = function useSheetContext() {
142
- var context = React.useContext(SheetContext);
143
- if (!context) throw Error('Sheet context error');
144
- return context;
145
- };
133
+ if (snapTo < 0) {
134
+ console.warn("Snap point is out of bounds. Sheet height is " + sheetHeight + " but snap point is " + (sheetHeight + Math.abs(snapTo)) + ".");
135
+ }
136
+
137
+ return Math.max(Math.round(snapTo), 0);
138
+ }
146
139
 
140
+ var useIsomorphicLayoutEffect = IS_SSR ? React.useEffect : React.useLayoutEffect;
147
141
  var useModalEffect = function useModalEffect(isOpen, rootId) {
148
142
  var prevOpen = usePrevious(isOpen); // Automatically apply the iOS modal effect to the body when sheet opens/closes
149
143
 
@@ -190,13 +184,54 @@ var useEventCallbacks = function useEventCallbacks(isOpen, callbacks) {
190
184
  handleAnimationComplete: handleAnimationComplete
191
185
  };
192
186
  };
187
+ function useWindowHeight() {
188
+ var _React$useState = React.useState(0),
189
+ windowHeight = _React$useState[0],
190
+ setWindowHeight = _React$useState[1];
191
+
192
+ useIsomorphicLayoutEffect(function () {
193
+ var updateHeight = function updateHeight() {
194
+ return setWindowHeight(window.innerHeight);
195
+ };
193
196
 
194
- var usePrevious = function usePrevious(state) {
197
+ window.addEventListener('resize', updateHeight);
198
+ updateHeight();
199
+ return function () {
200
+ return window.removeEventListener('resize', updateHeight);
201
+ };
202
+ }, []);
203
+ return windowHeight;
204
+ }
205
+ function usePrevious(state) {
195
206
  var ref = React.useRef();
196
207
  React.useEffect(function () {
197
208
  ref.current = state;
198
209
  });
199
210
  return ref.current;
211
+ } // Userland version of the `useEvent` React hook:
212
+ // RFC: https://github.com/reactjs/rfcs/blob/useevent/text/0000-useevent.md
213
+
214
+ function useEvent(handler) {
215
+ var handlerRef = React.useRef();
216
+ useIsomorphicLayoutEffect(function () {
217
+ handlerRef.current = handler;
218
+ });
219
+ return React.useCallback(function () {
220
+ var fn = handlerRef.current;
221
+
222
+ for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
223
+ args[_key] = arguments[_key];
224
+ }
225
+
226
+ return fn == null ? void 0 : fn.apply(void 0, args);
227
+ }, []);
228
+ }
229
+
230
+ var SheetContext = /*#__PURE__*/React.createContext(undefined);
231
+ var useSheetContext = function useSheetContext() {
232
+ var context = React.useContext(SheetContext);
233
+ if (!context) throw Error('Sheet context error');
234
+ return context;
200
235
  };
201
236
 
202
237
  var styles = {
@@ -259,56 +294,62 @@ var styles = {
259
294
  };
260
295
 
261
296
  var Sheet = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
262
- var children = _ref.children,
263
- isOpen = _ref.isOpen,
264
- onClose = _ref.onClose,
265
- onOpenStart = _ref.onOpenStart,
297
+ var onOpenStart = _ref.onOpenStart,
266
298
  onOpenEnd = _ref.onOpenEnd,
299
+ onClose = _ref.onClose,
267
300
  onCloseStart = _ref.onCloseStart,
268
301
  onCloseEnd = _ref.onCloseEnd,
269
302
  onSnap = _ref.onSnap,
303
+ children = _ref.children,
304
+ isOpen = _ref.isOpen,
270
305
  snapPoints = _ref.snapPoints,
306
+ detent = _ref.detent,
271
307
  _ref$initialSnap = _ref.initialSnap,
272
308
  initialSnap = _ref$initialSnap === void 0 ? 0 : _ref$initialSnap,
273
309
  rootId = _ref.rootId,
274
310
  mountPoint = _ref.mountPoint,
275
311
  _ref$springConfig = _ref.springConfig,
276
- springConfig = _ref$springConfig === void 0 ? {
277
- stiffness: 300,
278
- damping: 30,
279
- mass: 0.2
280
- } : _ref$springConfig,
312
+ springConfig = _ref$springConfig === void 0 ? DEFAULT_SPRING_CONFIG : _ref$springConfig,
281
313
  _ref$disableDrag = _ref.disableDrag,
282
314
  disableDrag = _ref$disableDrag === void 0 ? false : _ref$disableDrag,
283
315
  style = _ref.style,
284
- rest = _objectWithoutPropertiesLoose(_ref, ["children", "isOpen", "onClose", "onOpenStart", "onOpenEnd", "onCloseStart", "onCloseEnd", "onSnap", "snapPoints", "initialSnap", "rootId", "mountPoint", "springConfig", "disableDrag", "style"]);
316
+ rest = _objectWithoutPropertiesLoose(_ref, ["onOpenStart", "onOpenEnd", "onClose", "onCloseStart", "onCloseEnd", "onSnap", "children", "isOpen", "snapPoints", "detent", "initialSnap", "rootId", "mountPoint", "springConfig", "disableDrag", "style"]);
285
317
 
286
318
  var sheetRef = React.useRef(null);
287
- var callbacks = React.useRef({
288
- onOpenStart: onOpenStart,
289
- onOpenEnd: onOpenEnd,
290
- onCloseStart: onCloseStart,
291
- onCloseEnd: onCloseEnd
292
- }); // prettier-ignore
293
-
294
319
  var indicatorRotation = framerMotion.useMotionValue(0);
295
320
  var windowHeight = useWindowHeight(); // NOTE: the inital value for `y` doesn't matter since it is overwritten by
296
321
  // the value driven by the `AnimatePresence` component when the sheet is opened
297
322
  // and after that it is driven by the gestures and/or snapping
298
323
 
299
- var y = framerMotion.useMotionValue(0);
324
+ var y = framerMotion.useMotionValue(0); // Keep the callback fns up-to-date so that they can be accessed inside
325
+ // the effect without including them to the dependencies array
326
+
327
+ var callbacks = React.useRef({
328
+ onOpenStart: onOpenStart,
329
+ onOpenEnd: onOpenEnd,
330
+ onCloseStart: onCloseStart,
331
+ onCloseEnd: onCloseEnd
332
+ });
333
+ useIsomorphicLayoutEffect(function () {
334
+ callbacks.current = {
335
+ onOpenStart: onOpenStart,
336
+ onOpenEnd: onOpenEnd,
337
+ onCloseStart: onCloseStart,
338
+ onCloseEnd: onCloseEnd
339
+ };
340
+ });
300
341
 
301
342
  if (snapPoints) {
302
343
  // Convert negative / percentage snap points to absolute values
303
344
  snapPoints = snapPoints.map(function (point) {
304
- if (point > 0 && point <= 1) return Math.round(point * windowHeight); // percentage values e.g. between 0.0 and 1.0
305
-
345
+ // Percentage values e.g. between 0.0 and 1.0
346
+ if (point > 0 && point <= 1) return Math.round(point * windowHeight);
306
347
  return point < 0 ? windowHeight + point : point; // negative values
307
348
  });
308
349
  console.assert(inDescendingOrder(snapPoints) || windowHeight === 0, "Snap points need to be in descending order got: [" + snapPoints + "]");
309
350
  }
310
351
 
311
- var handleDrag = React.useCallback(function (_, _ref2) {
352
+ var onDrag = useEvent(function (_, _ref2) {
312
353
  var delta = _ref2.delta;
313
354
  // Update drag indicator rotation based on drag velocity
314
355
  var velocity = y.getVelocity();
@@ -316,22 +357,42 @@ var Sheet = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
316
357
  if (velocity < 0) indicatorRotation.set(-10); // Make sure user cannot drag beyond the top of the sheet
317
358
 
318
359
  y.set(Math.max(y.get() + delta.y, 0));
319
- }, []); // eslint-disable-line
320
-
321
- var handleDragEnd = React.useCallback(function (_, _ref3) {
360
+ });
361
+ var onDragEnd = useEvent(function (_, _ref3) {
322
362
  var velocity = _ref3.velocity;
323
363
 
324
- if (velocity.y > 500) {
364
+ if (velocity.y > DRAG_VELOCITY_THRESHOLD) {
325
365
  // User flicked the sheet down
326
366
  onClose();
327
367
  } else {
328
368
  var sheetEl = sheetRef.current;
329
- var contentHeight = sheetEl.getBoundingClientRect().height;
330
- var snapTo = Math.round(snapPoints ? getClosest(snapPoints.map(function (p) {
331
- return contentHeight - p;
332
- }), y.get()) // prettier-ignore
333
- : y.get() / contentHeight > 0.6 // Close if dragged over 60%
334
- ? contentHeight : 0); // Update the spring value so that the sheet is animated to the snap point
369
+ var sheetHeight = sheetEl.getBoundingClientRect().height;
370
+ var currentY = y.get();
371
+ var snapTo = 0;
372
+
373
+ if (snapPoints) {
374
+ var snapToValues = snapPoints.map(function (p) {
375
+ return sheetHeight - p;
376
+ }).filter(function (p) {
377
+ return p >= 0;
378
+ }); // negative values can occur with `content-height` detent
379
+ // Allow snapping to the top of the sheet if detent is set to `content-height`
380
+
381
+ if (detent === 'content-height' && !snapToValues.includes(0)) {
382
+ snapToValues.unshift(0);
383
+ } // Get the closest snap point
384
+
385
+
386
+ snapTo = getClosest(snapToValues, currentY);
387
+ } else if (currentY / sheetHeight > DRAG_CLOSE_THRESHOLD) {
388
+ // Close if dragged over enough far
389
+ snapTo = sheetHeight;
390
+ }
391
+
392
+ snapTo = validateSnapTo({
393
+ snapTo: snapTo,
394
+ sheetHeight: sheetHeight
395
+ }); // Update the spring value so that the sheet is animated to the snap point
335
396
 
336
397
  framerMotion.animate(y, snapTo, _extends({
337
398
  type: 'spring'
@@ -344,22 +405,11 @@ var Sheet = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
344
405
  onSnap(snapIndex);
345
406
  }
346
407
 
347
- if (snapTo >= contentHeight) onClose();
408
+ if (snapTo >= sheetHeight) onClose();
348
409
  } // Reset indicator rotation after dragging
349
410
 
350
411
 
351
412
  indicatorRotation.set(0);
352
- }, [onClose, onSnap] // eslint-disable-line
353
- ); // Keep the callback fns up-to-date so that they can be accessed inside
354
- // the effect without including them to the dependencies array
355
-
356
- React.useEffect(function () {
357
- callbacks.current = {
358
- onOpenStart: onOpenStart,
359
- onOpenEnd: onOpenEnd,
360
- onCloseStart: onCloseStart,
361
- onCloseEnd: onCloseEnd
362
- };
363
413
  }); // Trigger onSnap callback when sheet is opened or closed
364
414
 
365
415
  React.useEffect(function () {
@@ -375,13 +425,17 @@ var Sheet = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
375
425
  var sheetEl = sheetRef.current;
376
426
 
377
427
  if (snapPoints && snapPoints[snapIndex] !== undefined && sheetEl !== null) {
378
- var contentHeight = sheetEl.getBoundingClientRect().height;
379
- var snapTo = contentHeight - snapPoints[snapIndex];
428
+ var sheetHeight = sheetEl.getBoundingClientRect().height;
429
+ var snapPoint = snapPoints[snapIndex];
430
+ var snapTo = validateSnapTo({
431
+ snapTo: sheetHeight - snapPoint,
432
+ sheetHeight: sheetHeight
433
+ });
380
434
  framerMotion.animate(y, snapTo, _extends({
381
435
  type: 'spring'
382
436
  }, springConfig));
383
437
  if (onSnap) onSnap(snapIndex);
384
- if (snapTo >= contentHeight) onClose();
438
+ if (snapTo >= sheetHeight) onClose();
385
439
  }
386
440
  }
387
441
  };
@@ -392,47 +446,48 @@ var Sheet = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
392
446
  overlays.usePreventScroll({
393
447
  isDisabled: !isOpen
394
448
  });
395
- var dragProps = disableDrag ? {} : {
396
- drag: 'y',
397
- dragElastic: 0,
398
- dragConstraints: {
399
- top: 0,
400
- bottom: 0
401
- },
402
- dragMomentum: false,
403
- onDrag: handleDrag,
404
- onDragEnd: handleDragEnd
405
- };
449
+ var dragProps = React.useMemo(function () {
450
+ var dragProps = {
451
+ drag: 'y',
452
+ dragElastic: 0,
453
+ dragConstraints: {
454
+ top: 0,
455
+ bottom: 0
456
+ },
457
+ dragMomentum: false,
458
+ onDrag: onDrag,
459
+ onDragEnd: onDragEnd
460
+ };
461
+ return disableDrag ? undefined : dragProps;
462
+ }, [disableDrag]); // eslint-disable-line
463
+
406
464
  var context = {
407
465
  y: y,
408
466
  sheetRef: sheetRef,
409
467
  isOpen: isOpen,
410
468
  initialSnap: initialSnap,
411
469
  snapPoints: snapPoints,
470
+ detent: detent,
412
471
  indicatorRotation: indicatorRotation,
413
472
  callbacks: callbacks,
414
473
  dragProps: dragProps,
415
474
  windowHeight: windowHeight,
416
475
  springConfig: springConfig
417
476
  };
418
-
419
- var wrapperProps = _extends({}, rest, {
420
- ref: ref,
421
- style: _extends({}, styles.wrapper, style)
422
- });
423
-
424
477
  var sheet = React.createElement(SheetContext.Provider, {
425
478
  value: context
426
- }, React.createElement("div", Object.assign({}, wrapperProps), React.createElement(framerMotion.AnimatePresence, null, isOpen ? React.Children.map(children, function (child, i) {
479
+ }, React.createElement("div", Object.assign({}, rest, {
480
+ ref: ref,
481
+ style: _extends({}, styles.wrapper, style)
482
+ }), React.createElement(framerMotion.AnimatePresence, null, isOpen ? React.Children.map(children, function (child, i) {
427
483
  return React.cloneElement(child, {
428
484
  key: "sheet-child-" + i
429
485
  });
430
486
  }) : null)));
431
- if (isSSR) return sheet;
487
+ if (IS_SSR) return sheet;
432
488
  return ReactDOM.createPortal(sheet, mountPoint != null ? mountPoint : document.body);
433
489
  });
434
490
 
435
- var MAX_HEIGHT = 'calc(100% - env(safe-area-inset-top) - 34px)';
436
491
  var SheetContainer = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
437
492
  var children = _ref.children,
438
493
  _ref$style = _ref.style,
@@ -448,20 +503,24 @@ var SheetContainer = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
448
503
  initialSnap = _useSheetContext$init === void 0 ? 0 : _useSheetContext$init,
449
504
  sheetRef = _useSheetContext.sheetRef,
450
505
  windowHeight = _useSheetContext.windowHeight,
451
- springConfig = _useSheetContext.springConfig;
506
+ springConfig = _useSheetContext.springConfig,
507
+ _useSheetContext$dete = _useSheetContext.detent,
508
+ detent = _useSheetContext$dete === void 0 ? 'full-height' : _useSheetContext$dete;
452
509
 
453
510
  var _useEventCallbacks = useEventCallbacks(isOpen, callbacks),
454
511
  handleAnimationComplete = _useEventCallbacks.handleAnimationComplete;
455
512
 
456
513
  var initialY = snapPoints ? snapPoints[0] - snapPoints[initialSnap] : 0;
457
- var h = snapPoints ? snapPoints[0] : null;
458
- var sheetHeight = h ? "min(" + h + "px, " + MAX_HEIGHT + ")" : MAX_HEIGHT;
514
+ var maxSnapHeight = snapPoints ? snapPoints[0] : null;
515
+ var height = maxSnapHeight !== null ? "min(" + maxSnapHeight + "px, " + MAX_HEIGHT + ")" : MAX_HEIGHT;
459
516
  return React.createElement(framerMotion.motion.div, Object.assign({}, rest, {
460
517
  ref: mergeRefs([sheetRef, ref]),
461
518
  className: "react-modal-sheet-container",
462
- style: _extends({}, styles.container, {
463
- height: sheetHeight
464
- }, style, {
519
+ style: _extends({}, styles.container, style, detent === 'full-height' && {
520
+ height: height
521
+ }, detent === 'content-height' && {
522
+ maxHeight: height
523
+ }, {
465
524
  y: y
466
525
  }),
467
526
  initial: {
@@ -482,11 +541,8 @@ var SheetContainer = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
482
541
 
483
542
  var SheetContent = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
484
543
  var children = _ref.children,
485
- _ref$disableDrag = _ref.disableDrag,
486
- disableDrag = _ref$disableDrag === void 0 ? false : _ref$disableDrag,
487
- _ref$style = _ref.style,
488
- style = _ref$style === void 0 ? {} : _ref$style,
489
- rest = _objectWithoutPropertiesLoose(_ref, ["children", "disableDrag", "style"]);
544
+ style = _ref.style,
545
+ rest = _objectWithoutPropertiesLoose(_ref, ["children", "style"]);
490
546
 
491
547
  var _useSheetContext = useSheetContext(),
492
548
  dragProps = _useSheetContext.dragProps;
@@ -495,16 +551,13 @@ var SheetContent = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
495
551
  ref: ref,
496
552
  className: "react-modal-sheet-content",
497
553
  style: _extends({}, styles.content, style)
498
- }, disableDrag ? {} : dragProps), children);
554
+ }, dragProps), children);
499
555
  });
500
556
 
501
557
  var SheetHeader = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
502
558
  var children = _ref.children,
503
- _ref$disableDrag = _ref.disableDrag,
504
- disableDrag = _ref$disableDrag === void 0 ? false : _ref$disableDrag,
505
- _ref$style = _ref.style,
506
- style = _ref$style === void 0 ? {} : _ref$style,
507
- rest = _objectWithoutPropertiesLoose(_ref, ["children", "disableDrag", "style"]);
559
+ style = _ref.style,
560
+ rest = _objectWithoutPropertiesLoose(_ref, ["children", "style"]);
508
561
 
509
562
  var _useSheetContext = useSheetContext(),
510
563
  indicatorRotation = _useSheetContext.indicatorRotation,
@@ -519,7 +572,7 @@ var SheetHeader = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
519
572
  return React.createElement(framerMotion.motion.div, Object.assign({}, rest, {
520
573
  ref: ref,
521
574
  style: _extends({}, styles.headerWrapper, style)
522
- }, disableDrag ? {} : dragProps), children || React.createElement("div", {
575
+ }, dragProps), children || React.createElement("div", {
523
576
  className: "react-modal-sheet-header",
524
577
  style: styles.header
525
578
  }, React.createElement(framerMotion.motion.span, {