react-modal-sheet 3.2.0 → 3.4.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 +21 -19
- package/dist/index.d.mts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +218 -115
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +214 -111
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -4
- package/src/constants.ts +2 -2
- package/src/hooks.tsx +87 -15
- package/src/sheet.tsx +15 -7
- package/src/types.tsx +2 -0
- package/src/use-prevent-scroll.ts +80 -21
- package/src/utils.ts +41 -42
package/README.md
CHANGED
|
@@ -70,25 +70,27 @@ Also, by constructing the sheet from smaller pieces makes it easier to apply any
|
|
|
70
70
|
|
|
71
71
|
## 🎛️ Props
|
|
72
72
|
|
|
73
|
-
| Name
|
|
74
|
-
|
|
|
75
|
-
| `children`
|
|
76
|
-
| `isOpen`
|
|
77
|
-
| `onClose`
|
|
78
|
-
| `disableDrag`
|
|
79
|
-
| `disableScrollLocking`
|
|
80
|
-
| `detent`
|
|
81
|
-
| `onOpenStart`
|
|
82
|
-
| `onOpenEnd`
|
|
83
|
-
| `onCloseStart`
|
|
84
|
-
| `onCloseEnd`
|
|
85
|
-
| `onSnap`
|
|
86
|
-
| `snapPoints`
|
|
87
|
-
| `initialSnap`
|
|
88
|
-
| `rootId`
|
|
89
|
-
| `tweenConfig`
|
|
90
|
-
| `mountPoint`
|
|
91
|
-
| `prefersReducedMotion`
|
|
73
|
+
| Name | Required | Default | Description |
|
|
74
|
+
| ----------------------- | -------- | ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
75
|
+
| `children` | yes | | Use `Sheet.Container/Content/Header/Backdrop` to compose your bottom sheet. |
|
|
76
|
+
| `isOpen` | yes | | Boolean that indicates whether the sheet is open or not. |
|
|
77
|
+
| `onClose` | yes | | Callback fn that is called when the sheet is closed by the user. |
|
|
78
|
+
| `disableDrag` | no | false | Disable drag for the whole sheet. |
|
|
79
|
+
| `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). |
|
|
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'`. |
|
|
81
|
+
| `onOpenStart` | no | | Callback fn that is called when the sheet opening animation starts. |
|
|
82
|
+
| `onOpenEnd` | no | | Callback fn that is called when the sheet opening animation is completed. |
|
|
83
|
+
| `onCloseStart` | no | | Callback fn that is called when the sheet closing animation starts. |
|
|
84
|
+
| `onCloseEnd` | no | | Callback fn that is called when the sheet closing animation is completed. |
|
|
85
|
+
| `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. |
|
|
86
|
+
| `snapPoints` | no | | Eg. `[-50, 0.5, 100, 0]` - where positive values are pixels from the bottom of the screen and negative from the top. Values between 0-1 represent percentages, eg. `0.5` means 50% of window height from the bottom of the sceen. |
|
|
87
|
+
| `initialSnap` | no | 0 | Initial snap point when sheet is opened (index from `snapPoints`). |
|
|
88
|
+
| `rootId` | no | | The id of the element where the main app is mounted, eg. "root". Enables iOS modal effect. |
|
|
89
|
+
| `tweenConfig` | no | `{ ease: 'easeOut', duration: 0.2 }` | Overrides the config for the sheet [tween](https://www.framer.com/motion/transition/#tween) transition when the sheet is opened, closed, or snapped to a point. |
|
|
90
|
+
| `mountPoint` | no | `document.body` | HTML element that should be used as the mount point for the sheet. |
|
|
91
|
+
| `prefersReducedMotion` | no | false | Skip sheet animations (sheet instantly snaps to desired location). |
|
|
92
|
+
| `dragVelocityThreshold` | no | 500 | How fast the sheet must be flicked down to close. Higher values make the sheet harder to close. |
|
|
93
|
+
| `dragCloseThreshold` | no | 0.6 | The portion of the sheet which must be dragged off-screen before it will close. |
|
|
92
94
|
|
|
93
95
|
## ⚙️ Methods and properties
|
|
94
96
|
|
package/dist/index.d.mts
CHANGED
|
@@ -29,6 +29,8 @@ type SheetProps = {
|
|
|
29
29
|
disableDrag?: boolean;
|
|
30
30
|
disableScrollLocking?: boolean;
|
|
31
31
|
prefersReducedMotion?: boolean;
|
|
32
|
+
dragVelocityThreshold?: number;
|
|
33
|
+
dragCloseThreshold?: number;
|
|
32
34
|
} & SheetEvents & ComponentPropsWithoutRef<typeof motion.div>;
|
|
33
35
|
type SheetContainerProps = Omit<CommonProps, 'initial' | 'animate' | 'exit' | 'onAnimationComplete'> & {
|
|
34
36
|
children: ReactNode;
|
package/dist/index.d.ts
CHANGED
|
@@ -29,6 +29,8 @@ type SheetProps = {
|
|
|
29
29
|
disableDrag?: boolean;
|
|
30
30
|
disableScrollLocking?: boolean;
|
|
31
31
|
prefersReducedMotion?: boolean;
|
|
32
|
+
dragVelocityThreshold?: number;
|
|
33
|
+
dragCloseThreshold?: number;
|
|
32
34
|
} & SheetEvents & ComponentPropsWithoutRef<typeof motion.div>;
|
|
33
35
|
type SheetContainerProps = Omit<CommonProps, 'initial' | 'animate' | 'exit' | 'onAnimationComplete'> & {
|
|
34
36
|
children: ReactNode;
|
package/dist/index.js
CHANGED
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
var React2 = require('react');
|
|
4
4
|
var reactDom = require('react-dom');
|
|
5
5
|
var framerMotion = require('framer-motion');
|
|
6
|
-
var utils = require('@react-aria/utils');
|
|
7
6
|
|
|
8
7
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
9
8
|
|
|
@@ -22,107 +21,88 @@ var REDUCED_MOTION_TWEEN_CONFIG = {
|
|
|
22
21
|
ease: "linear",
|
|
23
22
|
duration: 0.01
|
|
24
23
|
};
|
|
25
|
-
var
|
|
26
|
-
var
|
|
24
|
+
var DEFAULT_DRAG_CLOSE_THRESHOLD = 0.6;
|
|
25
|
+
var DEFAULT_DRAG_VELOCITY_THRESHOLD = 500;
|
|
27
26
|
|
|
28
|
-
// src/
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
function applyRootStyles(rootId) {
|
|
42
|
-
const body = document.querySelector("body");
|
|
43
|
-
const root = document.querySelector(`#${rootId}`);
|
|
44
|
-
if (root) {
|
|
45
|
-
const p = 24;
|
|
46
|
-
const h = window.innerHeight;
|
|
47
|
-
const s = (h - p) / h;
|
|
27
|
+
// src/hooks.tsx
|
|
28
|
+
var useIsomorphicLayoutEffect = IS_SSR ? React2.useEffect : React2.useLayoutEffect;
|
|
29
|
+
function useModalEffect({
|
|
30
|
+
y,
|
|
31
|
+
rootId,
|
|
32
|
+
sheetRef
|
|
33
|
+
}) {
|
|
34
|
+
const heightRef = React2.useRef(IS_SSR ? 0 : window.innerHeight);
|
|
35
|
+
function setup() {
|
|
36
|
+
const root = document.querySelector(`#${rootId}`);
|
|
37
|
+
const body = document.querySelector("body");
|
|
38
|
+
if (!root)
|
|
39
|
+
return;
|
|
48
40
|
body.style.backgroundColor = "#000";
|
|
49
41
|
root.style.overflow = "hidden";
|
|
50
|
-
root.style.
|
|
51
|
-
root.style.
|
|
52
|
-
root.style.
|
|
53
|
-
root.style.
|
|
54
|
-
root.style.borderTopLeftRadius = "10px";
|
|
42
|
+
root.style.transitionTimingFunction = "cubic-bezier(0.32, 0.72, 0, 1)";
|
|
43
|
+
root.style.transitionProperty = "transform, border-radius";
|
|
44
|
+
root.style.transitionDuration = "0.5s";
|
|
45
|
+
root.style.transformOrigin = "center top";
|
|
55
46
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
function inDescendingOrder(arr) {
|
|
75
|
-
for (let i = 0; i < arr.length; i++) {
|
|
76
|
-
if (arr[i + 1] > arr[i])
|
|
77
|
-
return false;
|
|
78
|
-
}
|
|
79
|
-
return true;
|
|
80
|
-
}
|
|
81
|
-
function validateSnapTo({
|
|
82
|
-
snapTo,
|
|
83
|
-
sheetHeight
|
|
84
|
-
}) {
|
|
85
|
-
if (snapTo < 0) {
|
|
86
|
-
console.warn(
|
|
87
|
-
`Snap point is out of bounds. Sheet height is ${sheetHeight} but snap point is ${sheetHeight + Math.abs(snapTo)}.`
|
|
88
|
-
);
|
|
47
|
+
function cleanup() {
|
|
48
|
+
const root = document.querySelector(`#${rootId}`);
|
|
49
|
+
const body = document.querySelector("body");
|
|
50
|
+
if (!root)
|
|
51
|
+
return;
|
|
52
|
+
setTimeout(() => {
|
|
53
|
+
body.style.removeProperty("background-color");
|
|
54
|
+
root.style.removeProperty("overflow");
|
|
55
|
+
root.style.removeProperty("transition-timing-function");
|
|
56
|
+
root.style.removeProperty("transition-property");
|
|
57
|
+
root.style.removeProperty("transition-duration");
|
|
58
|
+
root.style.removeProperty("transform-origin");
|
|
59
|
+
root.style.removeProperty("transform");
|
|
60
|
+
root.style.removeProperty("border-top-right-radius");
|
|
61
|
+
root.style.removeProperty("border-top-left-radius");
|
|
62
|
+
}, 100);
|
|
89
63
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
ref(value);
|
|
97
|
-
} else if (ref) {
|
|
98
|
-
ref.current = value;
|
|
99
|
-
}
|
|
100
|
-
});
|
|
101
|
-
};
|
|
102
|
-
}
|
|
103
|
-
function isTouchDevice() {
|
|
104
|
-
if (typeof window === "undefined")
|
|
105
|
-
return false;
|
|
106
|
-
return "ontouchstart" in window || navigator.maxTouchPoints > 0;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// src/hooks.tsx
|
|
110
|
-
var useIsomorphicLayoutEffect = IS_SSR ? React2.useEffect : React2.useLayoutEffect;
|
|
111
|
-
function useModalEffect(isOpen, rootId) {
|
|
112
|
-
const prevOpen = usePrevious(isOpen);
|
|
64
|
+
useIsomorphicLayoutEffect(() => {
|
|
65
|
+
return () => {
|
|
66
|
+
if (rootId)
|
|
67
|
+
cleanup();
|
|
68
|
+
};
|
|
69
|
+
}, []);
|
|
113
70
|
React2.useEffect(() => {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
71
|
+
const root = document.querySelector(`#${rootId}`);
|
|
72
|
+
if (!root)
|
|
73
|
+
return;
|
|
74
|
+
function onCompleted() {
|
|
75
|
+
if (y.get() - 10 >= heightRef.current)
|
|
76
|
+
cleanup();
|
|
118
77
|
}
|
|
119
|
-
|
|
120
|
-
|
|
78
|
+
const removeStartListener = y.on("animationStart", () => {
|
|
79
|
+
var _a;
|
|
80
|
+
heightRef.current = ((_a = sheetRef.current) == null ? void 0 : _a.offsetHeight) || window.innerHeight;
|
|
81
|
+
setup();
|
|
82
|
+
});
|
|
83
|
+
const removeChangeListener = y.on("change", (value) => {
|
|
84
|
+
if (root) {
|
|
85
|
+
const progress = Math.max(0, 1 - value / heightRef.current);
|
|
86
|
+
const pageWidth = window.innerWidth;
|
|
87
|
+
const scale = (pageWidth - 16) / pageWidth;
|
|
88
|
+
const ty = framerMotion.transform(progress, [0, 1], [0, 24]);
|
|
89
|
+
const s = framerMotion.transform(progress, [0, 1], [1, scale]);
|
|
90
|
+
const borderRadius = framerMotion.transform(progress, [0, 1], [0, 10]);
|
|
91
|
+
const inset = "env(safe-area-inset-top)";
|
|
92
|
+
root.style.transform = `scale(${s}) translate3d(0, calc(${inset} + ${ty}px), 0)`;
|
|
93
|
+
root.style.borderTopRightRadius = `${borderRadius}px`;
|
|
94
|
+
root.style.borderTopLeftRadius = `${borderRadius}px`;
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
const removeCompleteListener = y.on("animationComplete", onCompleted);
|
|
98
|
+
const removeCancelListener = y.on("animationCancel", onCompleted);
|
|
121
99
|
return () => {
|
|
122
|
-
|
|
123
|
-
|
|
100
|
+
removeStartListener();
|
|
101
|
+
removeChangeListener();
|
|
102
|
+
removeCompleteListener();
|
|
103
|
+
removeCancelListener();
|
|
124
104
|
};
|
|
125
|
-
}, [
|
|
105
|
+
}, [y, rootId]);
|
|
126
106
|
}
|
|
127
107
|
function useEventCallbacks(isOpen, callbacks) {
|
|
128
108
|
const prevOpen = usePrevious(isOpen);
|
|
@@ -148,7 +128,10 @@ function useEventCallbacks(isOpen, callbacks) {
|
|
|
148
128
|
return { handleAnimationComplete };
|
|
149
129
|
}
|
|
150
130
|
function useDimensions() {
|
|
151
|
-
const [dimensions, setDimensions] = React2.useState({
|
|
131
|
+
const [dimensions, setDimensions] = React2.useState({
|
|
132
|
+
height: !IS_SSR ? window.innerHeight : 0,
|
|
133
|
+
width: !IS_SSR ? window.innerWidth : 0
|
|
134
|
+
});
|
|
152
135
|
useIsomorphicLayoutEffect(() => {
|
|
153
136
|
function handler() {
|
|
154
137
|
setDimensions({
|
|
@@ -224,7 +207,120 @@ var useSheetScrollerContext = () => {
|
|
|
224
207
|
throw Error("Sheet scroller context error");
|
|
225
208
|
return context;
|
|
226
209
|
};
|
|
227
|
-
|
|
210
|
+
|
|
211
|
+
// src/utils.ts
|
|
212
|
+
function getClosest(nums, goal) {
|
|
213
|
+
let closest = nums[0];
|
|
214
|
+
let minDifference = Math.abs(nums[0] - goal);
|
|
215
|
+
for (let i = 1; i < nums.length; i++) {
|
|
216
|
+
const difference = Math.abs(nums[i] - goal);
|
|
217
|
+
if (difference < minDifference) {
|
|
218
|
+
closest = nums[i];
|
|
219
|
+
minDifference = difference;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
return closest;
|
|
223
|
+
}
|
|
224
|
+
function inDescendingOrder(arr) {
|
|
225
|
+
for (let i = 0; i < arr.length; i++) {
|
|
226
|
+
if (arr[i + 1] > arr[i])
|
|
227
|
+
return false;
|
|
228
|
+
}
|
|
229
|
+
return true;
|
|
230
|
+
}
|
|
231
|
+
function validateSnapTo({
|
|
232
|
+
snapTo,
|
|
233
|
+
sheetHeight
|
|
234
|
+
}) {
|
|
235
|
+
if (snapTo < 0) {
|
|
236
|
+
console.warn(
|
|
237
|
+
`Snap point is out of bounds. Sheet height is ${sheetHeight} but snap point is ${sheetHeight + Math.abs(snapTo)}.`
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
return Math.max(Math.round(snapTo), 0);
|
|
241
|
+
}
|
|
242
|
+
function mergeRefs(refs) {
|
|
243
|
+
return (value) => {
|
|
244
|
+
refs.forEach((ref) => {
|
|
245
|
+
if (typeof ref === "function") {
|
|
246
|
+
ref(value);
|
|
247
|
+
} else if (ref) {
|
|
248
|
+
ref.current = value;
|
|
249
|
+
}
|
|
250
|
+
});
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
function isTouchDevice() {
|
|
254
|
+
if (typeof window === "undefined")
|
|
255
|
+
return false;
|
|
256
|
+
return "ontouchstart" in window || navigator.maxTouchPoints > 0;
|
|
257
|
+
}
|
|
258
|
+
function testPlatform(re) {
|
|
259
|
+
var _a;
|
|
260
|
+
return typeof window !== "undefined" && window.navigator != null ? re.test(
|
|
261
|
+
// @ts-expect-error
|
|
262
|
+
// eslint-disable-next-line @typescript-eslint/dot-notation
|
|
263
|
+
((_a = window.navigator["userAgentData"]) == null ? void 0 : _a.platform) || window.navigator.platform
|
|
264
|
+
) : false;
|
|
265
|
+
}
|
|
266
|
+
function cached(fn) {
|
|
267
|
+
let res = null;
|
|
268
|
+
return () => {
|
|
269
|
+
if (res == null) {
|
|
270
|
+
res = fn();
|
|
271
|
+
}
|
|
272
|
+
return res;
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
var isMac = cached(function() {
|
|
276
|
+
return testPlatform(/^Mac/i);
|
|
277
|
+
});
|
|
278
|
+
var isIPhone = cached(function() {
|
|
279
|
+
return testPlatform(/^iPhone/i);
|
|
280
|
+
});
|
|
281
|
+
var isIPad = cached(function() {
|
|
282
|
+
return testPlatform(/^iPad/i) || // iPadOS 13 lies and says it's a Mac, but we can distinguish by detecting touch support.
|
|
283
|
+
isMac() && navigator.maxTouchPoints > 1;
|
|
284
|
+
});
|
|
285
|
+
var isIOS = cached(function() {
|
|
286
|
+
return isIPhone() || isIPad();
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
// src/use-prevent-scroll.ts
|
|
290
|
+
var KEYBOARD_BUFFER = 24;
|
|
291
|
+
function chain(...callbacks) {
|
|
292
|
+
return (...args) => {
|
|
293
|
+
for (const callback of callbacks) {
|
|
294
|
+
if (typeof callback === "function") {
|
|
295
|
+
callback(...args);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
var visualViewport = typeof document !== "undefined" && window.visualViewport;
|
|
301
|
+
function isScrollable(node, checkForOverflow) {
|
|
302
|
+
if (!node) {
|
|
303
|
+
return false;
|
|
304
|
+
}
|
|
305
|
+
const style = window.getComputedStyle(node);
|
|
306
|
+
let isScrollable2 = /(auto|scroll)/.test(
|
|
307
|
+
style.overflow + style.overflowX + style.overflowY
|
|
308
|
+
);
|
|
309
|
+
if (isScrollable2 && checkForOverflow) {
|
|
310
|
+
isScrollable2 = node.scrollHeight !== node.clientHeight || node.scrollWidth !== node.clientWidth;
|
|
311
|
+
}
|
|
312
|
+
return isScrollable2;
|
|
313
|
+
}
|
|
314
|
+
function getScrollParent(node, checkForOverflow) {
|
|
315
|
+
let scrollableNode = node;
|
|
316
|
+
if (isScrollable(scrollableNode, checkForOverflow)) {
|
|
317
|
+
scrollableNode = scrollableNode.parentElement;
|
|
318
|
+
}
|
|
319
|
+
while (scrollableNode && !isScrollable(scrollableNode, checkForOverflow)) {
|
|
320
|
+
scrollableNode = scrollableNode.parentElement;
|
|
321
|
+
}
|
|
322
|
+
return scrollableNode || document.scrollingElement || document.documentElement;
|
|
323
|
+
}
|
|
228
324
|
var nonTextInputTypes = /* @__PURE__ */ new Set([
|
|
229
325
|
"checkbox",
|
|
230
326
|
"radio",
|
|
@@ -240,13 +336,13 @@ var preventScrollCount = 0;
|
|
|
240
336
|
var restore;
|
|
241
337
|
function usePreventScroll(options = {}) {
|
|
242
338
|
const { isDisabled } = options;
|
|
243
|
-
|
|
339
|
+
useIsomorphicLayoutEffect(() => {
|
|
244
340
|
if (isDisabled) {
|
|
245
341
|
return;
|
|
246
342
|
}
|
|
247
343
|
preventScrollCount++;
|
|
248
344
|
if (preventScrollCount === 1) {
|
|
249
|
-
if (
|
|
345
|
+
if (isIOS()) {
|
|
250
346
|
restore = preventScrollMobileSafari();
|
|
251
347
|
} else {
|
|
252
348
|
restore = preventScrollStandard();
|
|
@@ -255,13 +351,13 @@ function usePreventScroll(options = {}) {
|
|
|
255
351
|
return () => {
|
|
256
352
|
preventScrollCount--;
|
|
257
353
|
if (preventScrollCount === 0) {
|
|
258
|
-
restore();
|
|
354
|
+
restore == null ? void 0 : restore();
|
|
259
355
|
}
|
|
260
356
|
};
|
|
261
357
|
}, [isDisabled]);
|
|
262
358
|
}
|
|
263
359
|
function preventScrollStandard() {
|
|
264
|
-
return
|
|
360
|
+
return chain(
|
|
265
361
|
setStyle(
|
|
266
362
|
document.documentElement,
|
|
267
363
|
"paddingRight",
|
|
@@ -274,7 +370,7 @@ function preventScrollMobileSafari() {
|
|
|
274
370
|
let scrollable;
|
|
275
371
|
let lastY = 0;
|
|
276
372
|
const onTouchStart = (e) => {
|
|
277
|
-
scrollable =
|
|
373
|
+
scrollable = getScrollParent(e.target, true);
|
|
278
374
|
if (scrollable === document.documentElement && scrollable === document.body) {
|
|
279
375
|
return;
|
|
280
376
|
}
|
|
@@ -284,7 +380,7 @@ function preventScrollMobileSafari() {
|
|
|
284
380
|
if (scrollable === void 0) {
|
|
285
381
|
return;
|
|
286
382
|
}
|
|
287
|
-
if (scrollable === document.documentElement || scrollable === document.body) {
|
|
383
|
+
if (!scrollable || scrollable === document.documentElement || scrollable === document.body) {
|
|
288
384
|
e.preventDefault();
|
|
289
385
|
return;
|
|
290
386
|
}
|
|
@@ -324,9 +420,7 @@ function preventScrollMobileSafari() {
|
|
|
324
420
|
} else {
|
|
325
421
|
visualViewport.addEventListener(
|
|
326
422
|
"resize",
|
|
327
|
-
() =>
|
|
328
|
-
scrollIntoView(target);
|
|
329
|
-
},
|
|
423
|
+
() => scrollIntoView(target),
|
|
330
424
|
{ once: true }
|
|
331
425
|
);
|
|
332
426
|
}
|
|
@@ -339,7 +433,7 @@ function preventScrollMobileSafari() {
|
|
|
339
433
|
};
|
|
340
434
|
const scrollX = window.pageXOffset;
|
|
341
435
|
const scrollY = window.pageYOffset;
|
|
342
|
-
const restoreStyles =
|
|
436
|
+
const restoreStyles = chain(
|
|
343
437
|
setStyle(
|
|
344
438
|
document.documentElement,
|
|
345
439
|
"paddingRight",
|
|
@@ -349,7 +443,7 @@ function preventScrollMobileSafari() {
|
|
|
349
443
|
setStyle(document.body, "marginTop", `-${scrollY}px`)
|
|
350
444
|
);
|
|
351
445
|
window.scrollTo(0, 0);
|
|
352
|
-
const removeEvents =
|
|
446
|
+
const removeEvents = chain(
|
|
353
447
|
addEvent(document, "touchstart", onTouchStart, {
|
|
354
448
|
passive: false,
|
|
355
449
|
capture: true
|
|
@@ -387,11 +481,13 @@ function addEvent(target, event, handler, options) {
|
|
|
387
481
|
function scrollIntoView(target) {
|
|
388
482
|
const root = document.scrollingElement || document.documentElement;
|
|
389
483
|
while (target && target !== root) {
|
|
390
|
-
const scrollable =
|
|
484
|
+
const scrollable = getScrollParent(target);
|
|
391
485
|
if (scrollable !== document.documentElement && scrollable !== document.body && scrollable !== target) {
|
|
392
486
|
const scrollableTop = scrollable.getBoundingClientRect().top;
|
|
393
487
|
const targetTop = target.getBoundingClientRect().top;
|
|
394
|
-
|
|
488
|
+
const targetBottom = target.getBoundingClientRect().bottom;
|
|
489
|
+
const keyboardHeight = scrollable.getBoundingClientRect().bottom + KEYBOARD_BUFFER;
|
|
490
|
+
if (targetBottom > keyboardHeight) {
|
|
395
491
|
scrollable.scrollTop += targetTop - scrollableTop;
|
|
396
492
|
}
|
|
397
493
|
}
|
|
@@ -482,7 +578,7 @@ var Sheet = React2.forwardRef(
|
|
|
482
578
|
children,
|
|
483
579
|
disableScrollLocking = false,
|
|
484
580
|
isOpen,
|
|
485
|
-
snapPoints,
|
|
581
|
+
snapPoints: snapPointsProp,
|
|
486
582
|
rootId,
|
|
487
583
|
mountPoint,
|
|
488
584
|
style,
|
|
@@ -491,6 +587,8 @@ var Sheet = React2.forwardRef(
|
|
|
491
587
|
disableDrag = false,
|
|
492
588
|
prefersReducedMotion = false,
|
|
493
589
|
tweenConfig = DEFAULT_TWEEN_CONFIG,
|
|
590
|
+
dragVelocityThreshold = DEFAULT_DRAG_VELOCITY_THRESHOLD,
|
|
591
|
+
dragCloseThreshold = DEFAULT_DRAG_CLOSE_THRESHOLD,
|
|
494
592
|
...rest
|
|
495
593
|
}, ref) => {
|
|
496
594
|
const sheetRef = React2.useRef(null);
|
|
@@ -502,7 +600,7 @@ var Sheet = React2.forwardRef(
|
|
|
502
600
|
type: "tween",
|
|
503
601
|
...reduceMotion ? REDUCED_MOTION_TWEEN_CONFIG : tweenConfig
|
|
504
602
|
};
|
|
505
|
-
const y = framerMotion.useMotionValue(
|
|
603
|
+
const y = framerMotion.useMotionValue(windowHeight);
|
|
506
604
|
const zIndex = framerMotion.useTransform(
|
|
507
605
|
y,
|
|
508
606
|
(value) => value + 2 >= windowHeight ? -1 : 9999999
|
|
@@ -525,6 +623,7 @@ var Sheet = React2.forwardRef(
|
|
|
525
623
|
onCloseEnd
|
|
526
624
|
};
|
|
527
625
|
});
|
|
626
|
+
let snapPoints = snapPointsProp;
|
|
528
627
|
if (snapPoints) {
|
|
529
628
|
snapPoints = snapPoints.map((point) => {
|
|
530
629
|
if (point > 0 && point <= 1)
|
|
@@ -556,7 +655,7 @@ var Sheet = React2.forwardRef(
|
|
|
556
655
|
}
|
|
557
656
|
});
|
|
558
657
|
const onDragEnd = useEffectEvent((_, { velocity }) => {
|
|
559
|
-
if (velocity.y >
|
|
658
|
+
if (velocity.y > dragVelocityThreshold) {
|
|
560
659
|
onClose();
|
|
561
660
|
} else {
|
|
562
661
|
const sheetHeight = sheetRef.current.getBoundingClientRect().height;
|
|
@@ -570,7 +669,7 @@ var Sheet = React2.forwardRef(
|
|
|
570
669
|
snapToValues.unshift(0);
|
|
571
670
|
}
|
|
572
671
|
snapTo = getClosest(snapToValues, currentY);
|
|
573
|
-
} else if (currentY / sheetHeight >
|
|
672
|
+
} else if (currentY / sheetHeight > dragCloseThreshold) {
|
|
574
673
|
snapTo = sheetHeight;
|
|
575
674
|
}
|
|
576
675
|
snapTo = validateSnapTo({ snapTo, sheetHeight });
|
|
@@ -612,7 +711,11 @@ var Sheet = React2.forwardRef(
|
|
|
612
711
|
}
|
|
613
712
|
}
|
|
614
713
|
}));
|
|
615
|
-
useModalEffect(
|
|
714
|
+
useModalEffect({
|
|
715
|
+
y,
|
|
716
|
+
rootId,
|
|
717
|
+
sheetRef
|
|
718
|
+
});
|
|
616
719
|
usePreventScroll({ isDisabled: disableScrollLocking || !isOpen });
|
|
617
720
|
const dragProps = React2.useMemo(() => {
|
|
618
721
|
const dragProps2 = {
|
|
@@ -792,8 +895,8 @@ var SheetScroller = React2.forwardRef(
|
|
|
792
895
|
const sheetScrollerContext = useSheetScrollerContext();
|
|
793
896
|
function determineDragState(element) {
|
|
794
897
|
const { scrollTop, scrollHeight, clientHeight } = element;
|
|
795
|
-
const
|
|
796
|
-
if (!
|
|
898
|
+
const isScrollable2 = scrollHeight > clientHeight;
|
|
899
|
+
if (!isScrollable2)
|
|
797
900
|
return;
|
|
798
901
|
const isAtTop = scrollTop <= 0;
|
|
799
902
|
const isAtBottom = scrollHeight - scrollTop === clientHeight;
|