react-modal-sheet 4.0.1 → 4.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.
- package/README.md +46 -26
- package/dist/index.d.mts +5 -2
- package/dist/index.d.ts +5 -2
- package/dist/index.js +403 -308
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +404 -309
- package/dist/index.mjs.map +1 -1
- package/package.json +13 -13
- package/src/SheetContainer.tsx +5 -16
- package/src/SheetContent.tsx +1 -1
- package/src/SheetHeader.tsx +1 -1
- package/src/hooks/use-dimensions.ts +30 -0
- package/src/hooks/use-drag-constraints.ts +14 -0
- package/src/hooks/use-event-callbacks.ts +44 -0
- package/src/hooks/use-isomorphic-layout-effect.ts +5 -0
- package/src/hooks/use-modal-effect.ts +165 -0
- package/src/{use-prevent-scroll.ts → hooks/use-prevent-scroll.ts} +2 -2
- package/src/hooks/use-safe-area-insets.ts +40 -0
- package/src/hooks/use-stable-callback.ts +18 -0
- package/src/sheet.tsx +96 -72
- package/src/styles.ts +1 -1
- package/src/types.tsx +6 -3
- package/src/utils.ts +88 -10
- package/src/hooks.tsx +0 -197
package/README.md
CHANGED
|
@@ -84,27 +84,28 @@ Also, by constructing the sheet from smaller pieces makes it easier to apply any
|
|
|
84
84
|
|
|
85
85
|
## 🎛️ Props
|
|
86
86
|
|
|
87
|
-
| Name | Required | Default | Description
|
|
88
|
-
| ----------------------- | -------- | ------------------------------------ |
|
|
89
|
-
| `children` | yes | | Use `Sheet.Container/Content/Header/Backdrop` to compose your bottom sheet.
|
|
90
|
-
| `isOpen` | yes | | Boolean that indicates whether the sheet is open or not.
|
|
91
|
-
| `onClose` | yes | | Callback fn that is called when the sheet is closed by the user.
|
|
92
|
-
| `disableDrag` | no | false | Disable drag for the whole sheet.
|
|
93
|
-
| `disableScrollLocking` | no | false | Disable scroll locking for the `body` element while sheet is open. Can be useful if you face issues with input elements and the iOS virtual keyboard. See related [issue](https://github.com/Temzasse/react-modal-sheet/issues/135).
|
|
94
|
-
| `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'`.
|
|
95
|
-
| `onOpenStart` | no | | Callback fn that is called when the sheet opening animation starts.
|
|
96
|
-
| `onOpenEnd` | no | | Callback fn that is called when the sheet opening animation is completed.
|
|
97
|
-
| `onCloseStart` | no | | Callback fn that is called when the sheet closing animation starts.
|
|
98
|
-
| `onCloseEnd` | no | | Callback fn that is called when the sheet closing animation is completed.
|
|
99
|
-
| `onSnap` | no | | Callback fn that is called with the current snap point index when the sheet snaps to a new snap point. Requires `snapPoints` prop.
|
|
100
|
-
| `snapPoints` | no | | Eg. `[-50, 0.5, 100, 0]` - where positive values are pixels from the bottom of the
|
|
101
|
-
| `initialSnap` | no | 0 | Initial snap point when sheet is opened (index from `snapPoints`).
|
|
102
|
-
| `
|
|
103
|
-
| `
|
|
104
|
-
| `
|
|
105
|
-
| `
|
|
106
|
-
| `
|
|
107
|
-
| `
|
|
87
|
+
| Name | Required | Default | Description |
|
|
88
|
+
| ----------------------- | -------- | ------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
89
|
+
| `children` | yes | | Use `Sheet.Container/Content/Header/Backdrop` to compose your bottom sheet. |
|
|
90
|
+
| `isOpen` | yes | | Boolean that indicates whether the sheet is open or not. |
|
|
91
|
+
| `onClose` | yes | | Callback fn that is called when the sheet is closed by the user. |
|
|
92
|
+
| `disableDrag` | no | false | Disable drag for the whole sheet. |
|
|
93
|
+
| `disableScrollLocking` | no | false | Disable scroll locking for the `body` element while sheet is open. Can be useful if you face issues with input elements and the iOS virtual keyboard. See related [issue](https://github.com/Temzasse/react-modal-sheet/issues/135). |
|
|
94
|
+
| `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'`. |
|
|
95
|
+
| `onOpenStart` | no | | Callback fn that is called when the sheet opening animation starts. |
|
|
96
|
+
| `onOpenEnd` | no | | Callback fn that is called when the sheet opening animation is completed. |
|
|
97
|
+
| `onCloseStart` | no | | Callback fn that is called when the sheet closing animation starts. |
|
|
98
|
+
| `onCloseEnd` | no | | Callback fn that is called when the sheet closing animation is completed. |
|
|
99
|
+
| `onSnap` | no | | Callback fn that is called with the current snap point index when the sheet snaps to a new snap point. Requires `snapPoints` prop. |
|
|
100
|
+
| `snapPoints` | no | | Eg. `[-50, 0.5, 100, 0]` - where positive values are pixels from the bottom of the sheet and negative from the top. Values between 0-1 represent percentages, eg. `0.5` means 50% of sheet height. **Prefer using `1` for representing a snap point for a fully visible sheet.** |
|
|
101
|
+
| `initialSnap` | no | 0 | Initial snap point when sheet is opened (index from `snapPoints`). |
|
|
102
|
+
| `modalEffectRootId` | no | | The id of the element where the main app is mounted, eg. "root". Enables [iOS modal effect](#-ios-modal-view-effect). |
|
|
103
|
+
| `modalEffectThreshold` | no | 0 | Threshold value between 0-1 which determines when the iOS modal effect will start while dragging the sheet - `0` corresponding to the start of the drag (0% has been dragged into view) and `1` corresponding to the end of the drag (100% of the sheet is visible). |
|
|
104
|
+
| `tweenConfig` | no | `{ ease: 'easeOut', duration: 0.2 }` | Overrides the config for the sheet [tween](https://motion.dev/docs/react-transitions#tween) transition when the sheet is opened, closed, or snapped to a point. |
|
|
105
|
+
| `mountPoint` | no | `document.body` | HTML element that should be used as the mount point for the sheet. |
|
|
106
|
+
| `prefersReducedMotion` | no | false | Skip sheet animations (sheet instantly snaps to desired location). |
|
|
107
|
+
| `dragVelocityThreshold` | no | 500 | How fast the sheet must be flicked down to close. Higher values make the sheet harder to close. |
|
|
108
|
+
| `dragCloseThreshold` | no | 0.6 | The portion of the sheet which must be dragged off-screen before it will close. |
|
|
108
109
|
|
|
109
110
|
## ⚙️ Methods and properties
|
|
110
111
|
|
|
@@ -116,6 +117,8 @@ Imperative method that can be accessed via a ref for snapping to a snap point in
|
|
|
116
117
|
import { Sheet, SheetRef } from 'react-modal-sheet';
|
|
117
118
|
import { useState, useRef } from 'react';
|
|
118
119
|
|
|
120
|
+
const snapPoints = [1, 0.5, 0];
|
|
121
|
+
|
|
119
122
|
function Example() {
|
|
120
123
|
const [isOpen, setOpen] = useState(false);
|
|
121
124
|
const ref = useRef<SheetRef>(null);
|
|
@@ -125,13 +128,13 @@ function Example() {
|
|
|
125
128
|
<>
|
|
126
129
|
<button onClick={() => setOpen(true)}>Open sheet</button>
|
|
127
130
|
|
|
128
|
-
{/* Opens to
|
|
131
|
+
{/* Opens to 50% since initial index is 1 */}
|
|
129
132
|
<Sheet
|
|
130
133
|
ref={ref}
|
|
131
134
|
isOpen={isOpen}
|
|
132
135
|
onClose={() => setOpen(false)}
|
|
133
|
-
snapPoints={[600, 400, 100, 0]}
|
|
134
136
|
initialSnap={1}
|
|
137
|
+
snapPoints={snapPoints}
|
|
135
138
|
onSnap={(snapIndex) =>
|
|
136
139
|
console.log('> Current snap point index:', snapIndex)
|
|
137
140
|
}
|
|
@@ -162,6 +165,8 @@ The `y` value can be useful for certain situtation eg. when you want to combine
|
|
|
162
165
|
import { Sheet, SheetRef } from 'react-modal-sheet';
|
|
163
166
|
import { useState, useRef } from 'react';
|
|
164
167
|
|
|
168
|
+
const snapPoints = [1, 0.5, 0];
|
|
169
|
+
|
|
165
170
|
function Example() {
|
|
166
171
|
const [isOpen, setOpen] = useState(false);
|
|
167
172
|
const ref = useRef<SheetRef>(null);
|
|
@@ -173,8 +178,8 @@ function Example() {
|
|
|
173
178
|
<Sheet
|
|
174
179
|
isOpen={isOpen}
|
|
175
180
|
onClose={() => setOpen(false)}
|
|
176
|
-
snapPoints={[600, 400, 100, 0]}
|
|
177
181
|
initialSnap={1}
|
|
182
|
+
snapPoints={snapPoints}
|
|
178
183
|
>
|
|
179
184
|
<Sheet.Container>
|
|
180
185
|
{/**
|
|
@@ -346,11 +351,26 @@ To enable this effect you can provide the id of the root element where your appl
|
|
|
346
351
|
|
|
347
352
|
```tsx
|
|
348
353
|
function Example() {
|
|
349
|
-
return <Sheet
|
|
354
|
+
return <Sheet modalEffectRootId="root">{/*...*/}</Sheet>;
|
|
350
355
|
}
|
|
351
356
|
```
|
|
352
357
|
|
|
353
|
-
>
|
|
358
|
+
> [!IMPORTANT]
|
|
359
|
+
> ⚠️ **Limitations**: Since the effect is applied to the root element it will NOT work as desired if the HTML body element is scrolled down at all. One way to avoid this is to use something like `height: 100vh;` and `overflow: auto;` on the root element to make it fill the whole screen and be scrollable instead of the body element. You can check the example apps [root styles](https://github.com/Temzasse/react-modal-sheet/blob/main/example/src/index.css) for a working example of this.
|
|
360
|
+
|
|
361
|
+
#### Controlling the effect
|
|
362
|
+
|
|
363
|
+
You can control the start threshold of the effect with the `modalEffectThreshold` prop which should have a value between `0` and `1` representing the fraction at which the effect starts relative to the dragging distance. So for example, a value of `0.5` means the effect will start when the sheet is dragged halfway and `0.7` would mean the effect will start when the sheet is dragged 70% of the way to the top. The default value is `0` which means that the effect will start as soon as the sheet is opened or dragged into view.
|
|
364
|
+
|
|
365
|
+
> [!NOTE]
|
|
366
|
+
> If you are using `snapPoints` the start threshold is calculated based on the
|
|
367
|
+
> second snap point so that when the sheet is snapping to the first snap point,
|
|
368
|
+
> meaning the sheet is fully open, the effect will only apply for that snap point
|
|
369
|
+
> range and not for the whole draggable distance.
|
|
370
|
+
>
|
|
371
|
+
> This gives a more natural UX and follows the native iOS effect more closely.
|
|
372
|
+
>
|
|
373
|
+
> You can still override this behavior by providing a custom `modalEffectThreshold` value.
|
|
354
374
|
|
|
355
375
|
## 🎨 Customization
|
|
356
376
|
|
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { motion, MotionProps,
|
|
2
|
-
import { ReactNode, ComponentPropsWithoutRef,
|
|
1
|
+
import { EasingDefinition, motion, MotionProps, MotionValue } from 'motion/react';
|
|
2
|
+
import { ForwardRefExoticComponent, ReactNode, ComponentPropsWithoutRef, RefAttributes, HTMLAttributes } from 'react';
|
|
3
3
|
|
|
4
4
|
interface SheetEvents {
|
|
5
5
|
onOpenStart?: () => void;
|
|
@@ -20,7 +20,9 @@ type SheetProps = {
|
|
|
20
20
|
isOpen: boolean;
|
|
21
21
|
children: ReactNode;
|
|
22
22
|
onClose: () => void;
|
|
23
|
+
/** @deprecated Use `modalEffectRootId` instead! */
|
|
23
24
|
rootId?: string;
|
|
25
|
+
modalEffectRootId?: string;
|
|
24
26
|
mountPoint?: Element;
|
|
25
27
|
snapPoints?: number[];
|
|
26
28
|
detent?: SheetDetent;
|
|
@@ -31,6 +33,7 @@ type SheetProps = {
|
|
|
31
33
|
prefersReducedMotion?: boolean;
|
|
32
34
|
dragVelocityThreshold?: number;
|
|
33
35
|
dragCloseThreshold?: number;
|
|
36
|
+
modalEffectThreshold?: number;
|
|
34
37
|
} & SheetEvents & ComponentPropsWithoutRef<typeof motion.div>;
|
|
35
38
|
type SheetContainerProps = Omit<CommonProps, 'initial' | 'animate' | 'exit' | 'onAnimationComplete'> & {
|
|
36
39
|
children: ReactNode;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { motion, MotionProps,
|
|
2
|
-
import { ReactNode, ComponentPropsWithoutRef,
|
|
1
|
+
import { EasingDefinition, motion, MotionProps, MotionValue } from 'motion/react';
|
|
2
|
+
import { ForwardRefExoticComponent, ReactNode, ComponentPropsWithoutRef, RefAttributes, HTMLAttributes } from 'react';
|
|
3
3
|
|
|
4
4
|
interface SheetEvents {
|
|
5
5
|
onOpenStart?: () => void;
|
|
@@ -20,7 +20,9 @@ type SheetProps = {
|
|
|
20
20
|
isOpen: boolean;
|
|
21
21
|
children: ReactNode;
|
|
22
22
|
onClose: () => void;
|
|
23
|
+
/** @deprecated Use `modalEffectRootId` instead! */
|
|
23
24
|
rootId?: string;
|
|
25
|
+
modalEffectRootId?: string;
|
|
24
26
|
mountPoint?: Element;
|
|
25
27
|
snapPoints?: number[];
|
|
26
28
|
detent?: SheetDetent;
|
|
@@ -31,6 +33,7 @@ type SheetProps = {
|
|
|
31
33
|
prefersReducedMotion?: boolean;
|
|
32
34
|
dragVelocityThreshold?: number;
|
|
33
35
|
dragCloseThreshold?: number;
|
|
36
|
+
modalEffectThreshold?: number;
|
|
34
37
|
} & SheetEvents & ComponentPropsWithoutRef<typeof motion.div>;
|
|
35
38
|
type SheetContainerProps = Omit<CommonProps, 'initial' | 'animate' | 'exit' | 'onAnimationComplete'> & {
|
|
36
39
|
children: ReactNode;
|