react-modal-sheet 4.0.2 → 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 +3 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +400 -304
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +401 -305
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/SheetContainer.tsx +4 -14
- 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/dist/index.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import React2, { createContext, forwardRef, useRef, useEffect, useLayoutEffect, useImperativeHandle, useMemo, Children, cloneElement, useState, useCallback, useContext } from 'react';
|
|
2
1
|
import { createPortal } from 'react-dom';
|
|
3
|
-
import {
|
|
2
|
+
import React2, { createContext, forwardRef, useRef, useEffect, useLayoutEffect, useImperativeHandle, useMemo, Children, cloneElement, useState, useCallback, useContext } from 'react';
|
|
3
|
+
import { useMotionValue, useReducedMotion, useTransform, animate, motion, AnimatePresence } from 'motion/react';
|
|
4
|
+
import { transform } from 'motion';
|
|
4
5
|
|
|
5
6
|
// src/sheet.tsx
|
|
6
7
|
|
|
@@ -18,182 +19,33 @@ var REDUCED_MOTION_TWEEN_CONFIG = {
|
|
|
18
19
|
var DEFAULT_DRAG_CLOSE_THRESHOLD = 0.6;
|
|
19
20
|
var DEFAULT_DRAG_VELOCITY_THRESHOLD = 500;
|
|
20
21
|
|
|
21
|
-
// src/
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
function setup() {
|
|
30
|
-
const root = document.querySelector(`#${rootId}`);
|
|
31
|
-
const body = document.querySelector("body");
|
|
32
|
-
if (!root) return;
|
|
33
|
-
body.style.backgroundColor = "#000";
|
|
34
|
-
root.style.overflow = "hidden";
|
|
35
|
-
root.style.transitionTimingFunction = "cubic-bezier(0.32, 0.72, 0, 1)";
|
|
36
|
-
root.style.transitionProperty = "transform, border-radius";
|
|
37
|
-
root.style.transitionDuration = "0.5s";
|
|
38
|
-
root.style.transformOrigin = "center top";
|
|
39
|
-
}
|
|
40
|
-
function cleanup() {
|
|
41
|
-
const root = document.querySelector(`#${rootId}`);
|
|
42
|
-
const body = document.querySelector("body");
|
|
43
|
-
if (!root) return;
|
|
44
|
-
setTimeout(() => {
|
|
45
|
-
body.style.removeProperty("background-color");
|
|
46
|
-
root.style.removeProperty("overflow");
|
|
47
|
-
root.style.removeProperty("transition-timing-function");
|
|
48
|
-
root.style.removeProperty("transition-property");
|
|
49
|
-
root.style.removeProperty("transition-duration");
|
|
50
|
-
root.style.removeProperty("transform-origin");
|
|
51
|
-
root.style.removeProperty("transform");
|
|
52
|
-
root.style.removeProperty("border-top-right-radius");
|
|
53
|
-
root.style.removeProperty("border-top-left-radius");
|
|
54
|
-
}, 100);
|
|
22
|
+
// src/utils.ts
|
|
23
|
+
function getSheetHeight(sheetRef) {
|
|
24
|
+
const sheetEl = sheetRef.current;
|
|
25
|
+
if (!sheetEl) {
|
|
26
|
+
console.warn(
|
|
27
|
+
"Sheet height is not available because the sheet element is not mounted yet."
|
|
28
|
+
);
|
|
29
|
+
return 0;
|
|
55
30
|
}
|
|
56
|
-
|
|
57
|
-
return () => {
|
|
58
|
-
if (rootId) cleanup();
|
|
59
|
-
};
|
|
60
|
-
}, []);
|
|
61
|
-
useEffect(() => {
|
|
62
|
-
const root = document.querySelector(`#${rootId}`);
|
|
63
|
-
if (!root) return;
|
|
64
|
-
function onCompleted() {
|
|
65
|
-
if (y.get() - 10 >= heightRef.current) cleanup();
|
|
66
|
-
}
|
|
67
|
-
const removeStartListener = y.on("animationStart", () => {
|
|
68
|
-
var _a;
|
|
69
|
-
heightRef.current = ((_a = sheetRef.current) == null ? void 0 : _a.offsetHeight) || window.innerHeight;
|
|
70
|
-
setup();
|
|
71
|
-
});
|
|
72
|
-
const removeChangeListener = y.on("change", (value) => {
|
|
73
|
-
if (root) {
|
|
74
|
-
const progress = Math.max(0, 1 - value / heightRef.current);
|
|
75
|
-
const pageWidth = window.innerWidth;
|
|
76
|
-
const scale = (pageWidth - 16) / pageWidth;
|
|
77
|
-
const ty = transform(progress, [0, 1], [0, 24]);
|
|
78
|
-
const s = transform(progress, [0, 1], [1, scale]);
|
|
79
|
-
const borderRadius = transform(progress, [0, 1], [0, 10]);
|
|
80
|
-
const inset = "env(safe-area-inset-top)";
|
|
81
|
-
root.style.transform = `scale(${s}) translate3d(0, calc(${inset} + ${ty}px), 0)`;
|
|
82
|
-
root.style.borderTopRightRadius = `${borderRadius}px`;
|
|
83
|
-
root.style.borderTopLeftRadius = `${borderRadius}px`;
|
|
84
|
-
}
|
|
85
|
-
});
|
|
86
|
-
const removeCompleteListener = y.on("animationComplete", onCompleted);
|
|
87
|
-
const removeCancelListener = y.on("animationCancel", onCompleted);
|
|
88
|
-
return () => {
|
|
89
|
-
removeStartListener();
|
|
90
|
-
removeChangeListener();
|
|
91
|
-
removeCompleteListener();
|
|
92
|
-
removeCancelListener();
|
|
93
|
-
};
|
|
94
|
-
}, [y, rootId]);
|
|
31
|
+
return Math.round(sheetEl.getBoundingClientRect().height);
|
|
95
32
|
}
|
|
96
|
-
function
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
if (
|
|
102
|
-
|
|
103
|
-
didOpen.current = true;
|
|
104
|
-
} else {
|
|
105
|
-
(_d = (_c = callbacks.current).onCloseEnd) == null ? void 0 : _d.call(_c);
|
|
106
|
-
didOpen.current = false;
|
|
107
|
-
}
|
|
108
|
-
}, [isOpen, prevOpen]);
|
|
109
|
-
useEffect(() => {
|
|
110
|
-
var _a, _b, _c, _d;
|
|
111
|
-
if (!prevOpen && isOpen) {
|
|
112
|
-
(_b = (_a = callbacks.current).onOpenStart) == null ? void 0 : _b.call(_a);
|
|
113
|
-
} else if (!isOpen && prevOpen) {
|
|
114
|
-
(_d = (_c = callbacks.current).onCloseStart) == null ? void 0 : _d.call(_c);
|
|
33
|
+
function getSnapPoints({
|
|
34
|
+
snapPointsProp,
|
|
35
|
+
sheetHeight
|
|
36
|
+
}) {
|
|
37
|
+
const snapPointValues = snapPointsProp.map((point) => {
|
|
38
|
+
if (point > 0 && point <= 1) {
|
|
39
|
+
return Math.round(point * sheetHeight);
|
|
115
40
|
}
|
|
116
|
-
|
|
117
|
-
return { handleAnimationComplete };
|
|
118
|
-
}
|
|
119
|
-
function useDimensions() {
|
|
120
|
-
const [dimensions, setDimensions] = useState({
|
|
121
|
-
height: !IS_SSR ? window.innerHeight : 0,
|
|
122
|
-
width: !IS_SSR ? window.innerWidth : 0
|
|
41
|
+
return point < 0 ? sheetHeight + point : point;
|
|
123
42
|
});
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
height: window.innerHeight,
|
|
128
|
-
width: window.innerWidth
|
|
129
|
-
});
|
|
130
|
-
}
|
|
131
|
-
handler();
|
|
132
|
-
window.addEventListener("resize", handler);
|
|
133
|
-
return () => {
|
|
134
|
-
window.removeEventListener("resize", handler);
|
|
135
|
-
};
|
|
136
|
-
}, []);
|
|
137
|
-
return dimensions;
|
|
138
|
-
}
|
|
139
|
-
function usePrevious(state) {
|
|
140
|
-
const ref = useRef(void 0);
|
|
141
|
-
useEffect(() => {
|
|
142
|
-
ref.current = state;
|
|
143
|
-
});
|
|
144
|
-
return ref.current;
|
|
145
|
-
}
|
|
146
|
-
function useEffectEvent(handler) {
|
|
147
|
-
const handlerRef = useRef(void 0);
|
|
148
|
-
useIsomorphicLayoutEffect(() => {
|
|
149
|
-
handlerRef.current = handler;
|
|
150
|
-
});
|
|
151
|
-
return useCallback((...args) => {
|
|
152
|
-
const fn = handlerRef.current;
|
|
153
|
-
return fn == null ? void 0 : fn(...args);
|
|
154
|
-
}, []);
|
|
155
|
-
}
|
|
156
|
-
var constraints = { bottom: 0, top: 0, left: 0, right: 0 };
|
|
157
|
-
function useDragConstraints() {
|
|
158
|
-
const constraintsRef = useRef(null);
|
|
159
|
-
const onMeasureDragConstraints = useCallback(() => constraints, []);
|
|
160
|
-
return { constraintsRef, onMeasureDragConstraints };
|
|
161
|
-
}
|
|
162
|
-
var SheetContext = createContext(
|
|
163
|
-
void 0
|
|
164
|
-
);
|
|
165
|
-
var useSheetContext = () => {
|
|
166
|
-
const context = useContext(SheetContext);
|
|
167
|
-
if (!context) throw new Error("Sheet context error");
|
|
168
|
-
return context;
|
|
169
|
-
};
|
|
170
|
-
var SheetScrollerContext = createContext(void 0);
|
|
171
|
-
function SheetScrollerContextProvider({
|
|
172
|
-
children
|
|
173
|
-
}) {
|
|
174
|
-
const sheetContext = useSheetContext();
|
|
175
|
-
const [disableDrag, setDisableDrag] = useState(!!sheetContext.disableDrag);
|
|
176
|
-
function setDragEnabled() {
|
|
177
|
-
if (!sheetContext.disableDrag) setDisableDrag(false);
|
|
178
|
-
}
|
|
179
|
-
function setDragDisabled() {
|
|
180
|
-
if (!disableDrag) setDisableDrag(true);
|
|
181
|
-
}
|
|
182
|
-
return /* @__PURE__ */ React2.createElement(
|
|
183
|
-
SheetScrollerContext.Provider,
|
|
184
|
-
{
|
|
185
|
-
value: { disableDrag, setDragEnabled, setDragDisabled }
|
|
186
|
-
},
|
|
187
|
-
children
|
|
43
|
+
console.assert(
|
|
44
|
+
inDescendingOrder(snapPointValues) || sheetHeight === 0,
|
|
45
|
+
`Snap points need to be in descending order got: [${snapPointsProp.join(", ")}]`
|
|
188
46
|
);
|
|
47
|
+
return snapPointValues;
|
|
189
48
|
}
|
|
190
|
-
var useSheetScrollerContext = () => {
|
|
191
|
-
const context = useContext(SheetScrollerContext);
|
|
192
|
-
if (!context) throw new Error("Sheet scroller context error");
|
|
193
|
-
return context;
|
|
194
|
-
};
|
|
195
|
-
|
|
196
|
-
// src/utils.ts
|
|
197
49
|
function getClosest(nums, goal) {
|
|
198
50
|
let closest = nums[0];
|
|
199
51
|
let minDifference = Math.abs(nums[0] - goal);
|
|
@@ -212,6 +64,21 @@ function inDescendingOrder(arr) {
|
|
|
212
64
|
}
|
|
213
65
|
return true;
|
|
214
66
|
}
|
|
67
|
+
function getSnapY({
|
|
68
|
+
sheetHeight,
|
|
69
|
+
snapPoints,
|
|
70
|
+
snapIndex
|
|
71
|
+
}) {
|
|
72
|
+
const snapPoint = snapPoints[snapIndex];
|
|
73
|
+
if (snapPoint === void 0) {
|
|
74
|
+
console.warn(
|
|
75
|
+
`Invalid snap index ${snapIndex}. Snap points are: [${snapPoints.join(", ")}]`
|
|
76
|
+
);
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
const y = validateSnapTo({ snapTo: sheetHeight - snapPoint, sheetHeight });
|
|
80
|
+
return y;
|
|
81
|
+
}
|
|
215
82
|
function validateSnapTo({
|
|
216
83
|
snapTo,
|
|
217
84
|
sheetHeight
|
|
@@ -235,7 +102,7 @@ function mergeRefs(refs) {
|
|
|
235
102
|
};
|
|
236
103
|
}
|
|
237
104
|
function isTouchDevice() {
|
|
238
|
-
if (
|
|
105
|
+
if (IS_SSR) return false;
|
|
239
106
|
return "ontouchstart" in window || navigator.maxTouchPoints > 0;
|
|
240
107
|
}
|
|
241
108
|
function testPlatform(re) {
|
|
@@ -261,14 +128,265 @@ var isIPhone = cached(function() {
|
|
|
261
128
|
return testPlatform(/^iPhone/i);
|
|
262
129
|
});
|
|
263
130
|
var isIPad = cached(function() {
|
|
264
|
-
return testPlatform(/^iPad/i) ||
|
|
265
|
-
isMac() && navigator.maxTouchPoints > 1;
|
|
131
|
+
return testPlatform(/^iPad/i) || isMac() && navigator.maxTouchPoints > 1;
|
|
266
132
|
});
|
|
267
133
|
var isIOS = cached(function() {
|
|
268
134
|
return isIPhone() || isIPad();
|
|
269
135
|
});
|
|
136
|
+
var SheetContext = createContext(
|
|
137
|
+
void 0
|
|
138
|
+
);
|
|
139
|
+
var useSheetContext = () => {
|
|
140
|
+
const context = useContext(SheetContext);
|
|
141
|
+
if (!context) throw new Error("Sheet context error");
|
|
142
|
+
return context;
|
|
143
|
+
};
|
|
144
|
+
var SheetScrollerContext = createContext(void 0);
|
|
145
|
+
function SheetScrollerContextProvider({
|
|
146
|
+
children
|
|
147
|
+
}) {
|
|
148
|
+
const sheetContext = useSheetContext();
|
|
149
|
+
const [disableDrag, setDisableDrag] = useState(!!sheetContext.disableDrag);
|
|
150
|
+
function setDragEnabled() {
|
|
151
|
+
if (!sheetContext.disableDrag) setDisableDrag(false);
|
|
152
|
+
}
|
|
153
|
+
function setDragDisabled() {
|
|
154
|
+
if (!disableDrag) setDisableDrag(true);
|
|
155
|
+
}
|
|
156
|
+
return /* @__PURE__ */ React2.createElement(
|
|
157
|
+
SheetScrollerContext.Provider,
|
|
158
|
+
{
|
|
159
|
+
value: { disableDrag, setDragEnabled, setDragDisabled }
|
|
160
|
+
},
|
|
161
|
+
children
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
var useSheetScrollerContext = () => {
|
|
165
|
+
const context = useContext(SheetScrollerContext);
|
|
166
|
+
if (!context) throw new Error("Sheet scroller context error");
|
|
167
|
+
return context;
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
// src/styles.ts
|
|
171
|
+
var styles = {
|
|
172
|
+
wrapper: {
|
|
173
|
+
position: "fixed",
|
|
174
|
+
top: 0,
|
|
175
|
+
bottom: 0,
|
|
176
|
+
left: 0,
|
|
177
|
+
right: 0,
|
|
178
|
+
overflow: "hidden",
|
|
179
|
+
pointerEvents: "none"
|
|
180
|
+
},
|
|
181
|
+
backdrop: {
|
|
182
|
+
zIndex: 1,
|
|
183
|
+
position: "fixed",
|
|
184
|
+
top: 0,
|
|
185
|
+
left: 0,
|
|
186
|
+
width: "100%",
|
|
187
|
+
height: "100%",
|
|
188
|
+
backgroundColor: "rgba(0, 0, 0, 0.2)",
|
|
189
|
+
touchAction: "none",
|
|
190
|
+
border: "none",
|
|
191
|
+
WebkitTapHighlightColor: "transparent"
|
|
192
|
+
},
|
|
193
|
+
container: {
|
|
194
|
+
zIndex: 2,
|
|
195
|
+
position: "absolute",
|
|
196
|
+
left: 0,
|
|
197
|
+
bottom: 0,
|
|
198
|
+
width: "100%",
|
|
199
|
+
backgroundColor: "#fff",
|
|
200
|
+
borderTopRightRadius: "8px",
|
|
201
|
+
borderTopLeftRadius: "8px",
|
|
202
|
+
boxShadow: "0px -2px 16px rgba(0, 0, 0, 0.3)",
|
|
203
|
+
display: "flex",
|
|
204
|
+
flexDirection: "column",
|
|
205
|
+
pointerEvents: "auto"
|
|
206
|
+
},
|
|
207
|
+
headerWrapper: {
|
|
208
|
+
width: "100%"
|
|
209
|
+
},
|
|
210
|
+
header: {
|
|
211
|
+
height: "40px",
|
|
212
|
+
width: "100%",
|
|
213
|
+
position: "relative",
|
|
214
|
+
display: "flex",
|
|
215
|
+
alignItems: "center",
|
|
216
|
+
justifyContent: "center"
|
|
217
|
+
},
|
|
218
|
+
indicator: {
|
|
219
|
+
width: "18px",
|
|
220
|
+
height: "4px",
|
|
221
|
+
borderRadius: "99px",
|
|
222
|
+
backgroundColor: "#ddd"
|
|
223
|
+
},
|
|
224
|
+
content: {
|
|
225
|
+
flexGrow: 1,
|
|
226
|
+
display: "flex",
|
|
227
|
+
flexDirection: "column",
|
|
228
|
+
minHeight: "0px",
|
|
229
|
+
position: "relative"
|
|
230
|
+
},
|
|
231
|
+
scroller: {
|
|
232
|
+
height: "100%",
|
|
233
|
+
overflowY: "auto"
|
|
234
|
+
}
|
|
235
|
+
};
|
|
236
|
+
var useIsomorphicLayoutEffect = IS_SSR ? useEffect : useLayoutEffect;
|
|
237
|
+
function useSafeAreaInsets() {
|
|
238
|
+
const [insets] = useState(() => {
|
|
239
|
+
const fallback = { top: 0, left: 0, right: 0, bottom: 0 };
|
|
240
|
+
if (IS_SSR) return fallback;
|
|
241
|
+
const root = document.querySelector(":root");
|
|
242
|
+
if (!root) return fallback;
|
|
243
|
+
root.style.setProperty("--rms-sat", "env(safe-area-inset-top)");
|
|
244
|
+
root.style.setProperty("--rms-sal", "env(safe-area-inset-left)");
|
|
245
|
+
root.style.setProperty("--rms-sar", "env(safe-area-inset-right)");
|
|
246
|
+
root.style.setProperty("--rms-sab", "env(safe-area-inset-bottom)");
|
|
247
|
+
const computedStyle = getComputedStyle(root);
|
|
248
|
+
const sat = getComputedValue(computedStyle, "--rms-sat");
|
|
249
|
+
const sal = getComputedValue(computedStyle, "--rms-sal");
|
|
250
|
+
const sar = getComputedValue(computedStyle, "--rms-sar");
|
|
251
|
+
const sab = getComputedValue(computedStyle, "--rms-sab");
|
|
252
|
+
root.style.removeProperty("--rms-sat");
|
|
253
|
+
root.style.removeProperty("--rms-sal");
|
|
254
|
+
root.style.removeProperty("--rms-sar");
|
|
255
|
+
root.style.removeProperty("--rms-sab");
|
|
256
|
+
return { top: sat, left: sal, right: sar, bottom: sab };
|
|
257
|
+
});
|
|
258
|
+
return insets;
|
|
259
|
+
}
|
|
260
|
+
function getComputedValue(computed, property) {
|
|
261
|
+
const strValue = computed.getPropertyValue(property).replace("px", "").trim();
|
|
262
|
+
return parseInt(strValue, 10) || 0;
|
|
263
|
+
}
|
|
270
264
|
|
|
271
|
-
// src/use-
|
|
265
|
+
// src/hooks/use-modal-effect.ts
|
|
266
|
+
function useModalEffect({
|
|
267
|
+
y,
|
|
268
|
+
rootId,
|
|
269
|
+
sheetRef,
|
|
270
|
+
snapPointsProp,
|
|
271
|
+
startThreshold
|
|
272
|
+
}) {
|
|
273
|
+
const insetTop = useSafeAreaInsets().top;
|
|
274
|
+
useIsomorphicLayoutEffect(() => {
|
|
275
|
+
return () => {
|
|
276
|
+
if (rootId) cleanupModalEffect(rootId);
|
|
277
|
+
};
|
|
278
|
+
}, []);
|
|
279
|
+
useIsomorphicLayoutEffect(() => {
|
|
280
|
+
if (!rootId) return;
|
|
281
|
+
const root = document.querySelector(`#${rootId}`);
|
|
282
|
+
if (!root) return;
|
|
283
|
+
let sheetHeight = 0;
|
|
284
|
+
const removeStartListener = y.on("animationStart", () => {
|
|
285
|
+
sheetHeight = getSheetHeight(sheetRef);
|
|
286
|
+
setupModalEffect(rootId);
|
|
287
|
+
});
|
|
288
|
+
const removeChangeListener = y.on("change", (yValue) => {
|
|
289
|
+
if (!root) return;
|
|
290
|
+
let progress = Math.max(0, 1 - yValue / sheetHeight);
|
|
291
|
+
const snapPoints = snapPointsProp ? getSnapPoints({ snapPointsProp, sheetHeight }) : void 0;
|
|
292
|
+
const snapThresholdPoint = snapPoints && snapPoints.length > 1 ? snapPoints[1] : void 0;
|
|
293
|
+
if (snapThresholdPoint !== void 0) {
|
|
294
|
+
const snapThresholdValue = sheetHeight - Math.min(snapThresholdPoint, sheetHeight);
|
|
295
|
+
if (yValue <= snapThresholdValue) {
|
|
296
|
+
progress = (snapThresholdValue - yValue) / snapThresholdValue;
|
|
297
|
+
} else {
|
|
298
|
+
progress = 0;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
if (startThreshold !== void 0) {
|
|
302
|
+
const startThresholdValue = sheetHeight - Math.min(Math.floor(startThreshold * sheetHeight), sheetHeight);
|
|
303
|
+
if (yValue <= startThresholdValue) {
|
|
304
|
+
progress = (startThresholdValue - yValue) / startThresholdValue;
|
|
305
|
+
} else {
|
|
306
|
+
progress = 0;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
progress = Math.max(0, Math.min(1, progress));
|
|
310
|
+
const pageWidth = window.innerWidth;
|
|
311
|
+
const ty = transform(progress, [0, 1], [0, 24 + insetTop]);
|
|
312
|
+
const s = transform(progress, [0, 1], [1, (pageWidth - 16) / pageWidth]);
|
|
313
|
+
const borderRadius = transform(progress, [0, 1], [0, 10]);
|
|
314
|
+
root.style.transform = `scale(${s}) translate3d(0, ${ty}px, 0)`;
|
|
315
|
+
root.style.borderTopRightRadius = `${borderRadius}px`;
|
|
316
|
+
root.style.borderTopLeftRadius = `${borderRadius}px`;
|
|
317
|
+
});
|
|
318
|
+
function onCompleted() {
|
|
319
|
+
if (y.get() - 5 >= sheetHeight) {
|
|
320
|
+
cleanupModalEffect(rootId);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
const removeCompleteListener = y.on("animationComplete", onCompleted);
|
|
324
|
+
const removeCancelListener = y.on("animationCancel", onCompleted);
|
|
325
|
+
return () => {
|
|
326
|
+
removeStartListener();
|
|
327
|
+
removeChangeListener();
|
|
328
|
+
removeCompleteListener();
|
|
329
|
+
removeCancelListener();
|
|
330
|
+
};
|
|
331
|
+
}, [y, rootId, insetTop, startThreshold, snapPointsProp]);
|
|
332
|
+
}
|
|
333
|
+
function setupModalEffect(rootId) {
|
|
334
|
+
const root = document.querySelector(`#${rootId}`);
|
|
335
|
+
const body = document.querySelector("body");
|
|
336
|
+
if (!root) return;
|
|
337
|
+
body.style.backgroundColor = "#000";
|
|
338
|
+
root.style.overflow = "hidden";
|
|
339
|
+
root.style.transitionTimingFunction = "cubic-bezier(0.32, 0.72, 0, 1)";
|
|
340
|
+
root.style.transitionProperty = "transform, border-radius";
|
|
341
|
+
root.style.transitionDuration = "0.5s";
|
|
342
|
+
root.style.transformOrigin = "center top";
|
|
343
|
+
}
|
|
344
|
+
function cleanupModalEffect(rootId) {
|
|
345
|
+
const root = document.querySelector(`#${rootId}`);
|
|
346
|
+
const body = document.querySelector("body");
|
|
347
|
+
if (!root) return;
|
|
348
|
+
body.style.removeProperty("background-color");
|
|
349
|
+
root.style.removeProperty("overflow");
|
|
350
|
+
root.style.removeProperty("transition-timing-function");
|
|
351
|
+
root.style.removeProperty("transition-property");
|
|
352
|
+
root.style.removeProperty("transition-duration");
|
|
353
|
+
root.style.removeProperty("transform-origin");
|
|
354
|
+
root.style.removeProperty("transform");
|
|
355
|
+
root.style.removeProperty("border-top-right-radius");
|
|
356
|
+
root.style.removeProperty("border-top-left-radius");
|
|
357
|
+
}
|
|
358
|
+
function useDimensions() {
|
|
359
|
+
const [dimensions, setDimensions] = useState(() => ({
|
|
360
|
+
height: !IS_SSR ? window.innerHeight : 0,
|
|
361
|
+
width: !IS_SSR ? window.innerWidth : 0
|
|
362
|
+
}));
|
|
363
|
+
useIsomorphicLayoutEffect(() => {
|
|
364
|
+
function handler() {
|
|
365
|
+
setDimensions({
|
|
366
|
+
height: window.innerHeight,
|
|
367
|
+
width: window.innerWidth
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
handler();
|
|
371
|
+
window.addEventListener("resize", handler);
|
|
372
|
+
return () => {
|
|
373
|
+
window.removeEventListener("resize", handler);
|
|
374
|
+
};
|
|
375
|
+
}, []);
|
|
376
|
+
return dimensions;
|
|
377
|
+
}
|
|
378
|
+
function useStableCallback(handler) {
|
|
379
|
+
const handlerRef = useRef(void 0);
|
|
380
|
+
useIsomorphicLayoutEffect(() => {
|
|
381
|
+
handlerRef.current = handler;
|
|
382
|
+
});
|
|
383
|
+
return useCallback((...args) => {
|
|
384
|
+
const fn = handlerRef.current;
|
|
385
|
+
return fn == null ? void 0 : fn(...args);
|
|
386
|
+
}, []);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// src/hooks/use-prevent-scroll.ts
|
|
272
390
|
var KEYBOARD_BUFFER = 24;
|
|
273
391
|
function chain(...callbacks) {
|
|
274
392
|
return (...args) => {
|
|
@@ -484,74 +602,6 @@ function willOpenKeyboard(target) {
|
|
|
484
602
|
return target instanceof HTMLInputElement && !nonTextInputTypes.has(target.type) || target instanceof HTMLTextAreaElement || target instanceof HTMLElement && target.isContentEditable;
|
|
485
603
|
}
|
|
486
604
|
|
|
487
|
-
// src/styles.ts
|
|
488
|
-
var styles = {
|
|
489
|
-
wrapper: {
|
|
490
|
-
position: "fixed",
|
|
491
|
-
top: 0,
|
|
492
|
-
bottom: 0,
|
|
493
|
-
left: 0,
|
|
494
|
-
right: 0,
|
|
495
|
-
overflow: "hidden",
|
|
496
|
-
pointerEvents: "none"
|
|
497
|
-
},
|
|
498
|
-
backdrop: {
|
|
499
|
-
zIndex: 1,
|
|
500
|
-
position: "fixed",
|
|
501
|
-
top: 0,
|
|
502
|
-
left: 0,
|
|
503
|
-
width: "100%",
|
|
504
|
-
height: "100%",
|
|
505
|
-
backgroundColor: "rgba(0, 0, 0, 0.2)",
|
|
506
|
-
touchAction: "none",
|
|
507
|
-
// Disable iOS body scrolling
|
|
508
|
-
border: "none",
|
|
509
|
-
WebkitTapHighlightColor: "transparent"
|
|
510
|
-
},
|
|
511
|
-
container: {
|
|
512
|
-
zIndex: 2,
|
|
513
|
-
position: "absolute",
|
|
514
|
-
left: 0,
|
|
515
|
-
bottom: 0,
|
|
516
|
-
width: "100%",
|
|
517
|
-
backgroundColor: "#fff",
|
|
518
|
-
borderTopRightRadius: "8px",
|
|
519
|
-
borderTopLeftRadius: "8px",
|
|
520
|
-
boxShadow: "0px -2px 16px rgba(0, 0, 0, 0.3)",
|
|
521
|
-
display: "flex",
|
|
522
|
-
flexDirection: "column",
|
|
523
|
-
pointerEvents: "auto"
|
|
524
|
-
},
|
|
525
|
-
headerWrapper: {
|
|
526
|
-
width: "100%"
|
|
527
|
-
},
|
|
528
|
-
header: {
|
|
529
|
-
height: "40px",
|
|
530
|
-
width: "100%",
|
|
531
|
-
position: "relative",
|
|
532
|
-
display: "flex",
|
|
533
|
-
alignItems: "center",
|
|
534
|
-
justifyContent: "center"
|
|
535
|
-
},
|
|
536
|
-
indicator: {
|
|
537
|
-
width: "18px",
|
|
538
|
-
height: "4px",
|
|
539
|
-
borderRadius: "99px",
|
|
540
|
-
backgroundColor: "#ddd"
|
|
541
|
-
},
|
|
542
|
-
content: {
|
|
543
|
-
flexGrow: 1,
|
|
544
|
-
display: "flex",
|
|
545
|
-
flexDirection: "column",
|
|
546
|
-
minHeight: "0px",
|
|
547
|
-
position: "relative"
|
|
548
|
-
},
|
|
549
|
-
scroller: {
|
|
550
|
-
height: "100%",
|
|
551
|
-
overflowY: "auto"
|
|
552
|
-
}
|
|
553
|
-
};
|
|
554
|
-
|
|
555
605
|
// src/sheet.tsx
|
|
556
606
|
var Sheet = forwardRef(
|
|
557
607
|
({
|
|
@@ -566,6 +616,7 @@ var Sheet = forwardRef(
|
|
|
566
616
|
isOpen,
|
|
567
617
|
snapPoints: snapPointsProp,
|
|
568
618
|
rootId,
|
|
619
|
+
modalEffectRootId,
|
|
569
620
|
mountPoint,
|
|
570
621
|
style,
|
|
571
622
|
detent = "full-height",
|
|
@@ -575,6 +626,7 @@ var Sheet = forwardRef(
|
|
|
575
626
|
tweenConfig = DEFAULT_TWEEN_CONFIG,
|
|
576
627
|
dragVelocityThreshold = DEFAULT_DRAG_VELOCITY_THRESHOLD,
|
|
577
628
|
dragCloseThreshold = DEFAULT_DRAG_CLOSE_THRESHOLD,
|
|
629
|
+
modalEffectThreshold,
|
|
578
630
|
...rest
|
|
579
631
|
}, ref) => {
|
|
580
632
|
const sheetRef = useRef(null);
|
|
@@ -609,26 +661,13 @@ var Sheet = forwardRef(
|
|
|
609
661
|
onCloseEnd
|
|
610
662
|
};
|
|
611
663
|
});
|
|
612
|
-
|
|
613
|
-
if (snapPoints) {
|
|
614
|
-
snapPoints = snapPoints.map((point) => {
|
|
615
|
-
if (point > 0 && point <= 1) return Math.round(point * windowHeight);
|
|
616
|
-
return point < 0 ? windowHeight + point : point;
|
|
617
|
-
});
|
|
618
|
-
console.assert(
|
|
619
|
-
inDescendingOrder(snapPoints) || windowHeight === 0,
|
|
620
|
-
`Snap points need to be in descending order got: [${snapPoints.join(
|
|
621
|
-
", "
|
|
622
|
-
)}]`
|
|
623
|
-
);
|
|
624
|
-
}
|
|
625
|
-
const onDrag = useEffectEvent((_, { delta }) => {
|
|
664
|
+
const onDrag = useStableCallback((_, { delta }) => {
|
|
626
665
|
const velocity = y.getVelocity();
|
|
627
666
|
if (velocity > 0) indicatorRotation.set(10);
|
|
628
667
|
if (velocity < 0) indicatorRotation.set(-10);
|
|
629
668
|
y.set(Math.max(y.get() + delta.y, 0));
|
|
630
669
|
});
|
|
631
|
-
const onDragStart =
|
|
670
|
+
const onDragStart = useStableCallback(() => {
|
|
632
671
|
const focusedElement = document.activeElement;
|
|
633
672
|
if (!focusedElement || !sheetRef.current) return;
|
|
634
673
|
const isInput = focusedElement.tagName === "INPUT" || focusedElement.tagName === "TEXTAREA";
|
|
@@ -636,67 +675,91 @@ var Sheet = forwardRef(
|
|
|
636
675
|
focusedElement.blur();
|
|
637
676
|
}
|
|
638
677
|
});
|
|
639
|
-
const onDragEnd =
|
|
678
|
+
const onDragEnd = useStableCallback((_, { velocity }) => {
|
|
640
679
|
if (velocity.y > dragVelocityThreshold) {
|
|
641
680
|
onClose();
|
|
642
681
|
} else {
|
|
643
|
-
const sheetHeight = sheetRef
|
|
682
|
+
const sheetHeight = getSheetHeight(sheetRef);
|
|
644
683
|
const currentY = y.get();
|
|
645
|
-
let
|
|
684
|
+
let yTo = 0;
|
|
685
|
+
const snapPoints = snapPointsProp ? getSnapPoints({ snapPointsProp, sheetHeight }) : void 0;
|
|
646
686
|
if (snapPoints) {
|
|
647
|
-
const
|
|
687
|
+
const snapInverse = snapPoints.map(
|
|
648
688
|
(p) => sheetHeight - Math.min(p, sheetHeight)
|
|
649
689
|
);
|
|
650
|
-
if (detent === "content-height" && !
|
|
651
|
-
|
|
690
|
+
if (detent === "content-height" && !snapInverse.includes(0)) {
|
|
691
|
+
snapInverse.unshift(0);
|
|
652
692
|
}
|
|
653
|
-
snapTo = getClosest(
|
|
693
|
+
const snapTo = getClosest(snapInverse, currentY);
|
|
694
|
+
const snapIndex = snapInverse.indexOf(snapTo);
|
|
695
|
+
yTo = validateSnapTo({
|
|
696
|
+
snapTo: getClosest(snapInverse, currentY),
|
|
697
|
+
sheetHeight
|
|
698
|
+
});
|
|
699
|
+
onSnap == null ? void 0 : onSnap(snapIndex);
|
|
654
700
|
} else if (currentY / sheetHeight > dragCloseThreshold) {
|
|
655
|
-
|
|
701
|
+
yTo = sheetHeight;
|
|
656
702
|
}
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
const snapValue = Math.abs(Math.round(snapPoints[0] - snapTo));
|
|
661
|
-
const snapIndex = snapPoints.indexOf(
|
|
662
|
-
getClosest(snapPoints, snapValue)
|
|
663
|
-
);
|
|
664
|
-
onSnap(snapIndex);
|
|
703
|
+
animate(y, yTo, animationOptions);
|
|
704
|
+
if (yTo + 1 >= sheetHeight) {
|
|
705
|
+
onClose();
|
|
665
706
|
}
|
|
666
|
-
const roundedSheetHeight = Math.round(sheetHeight);
|
|
667
|
-
const shouldClose = snapTo + 2 >= roundedSheetHeight;
|
|
668
|
-
if (shouldClose) onClose();
|
|
669
707
|
}
|
|
670
708
|
indicatorRotation.set(0);
|
|
671
709
|
});
|
|
672
710
|
useEffect(() => {
|
|
673
|
-
if (
|
|
674
|
-
|
|
675
|
-
|
|
711
|
+
if (snapPointsProp && onSnap) {
|
|
712
|
+
onSnap(isOpen ? initialSnap : snapPointsProp.length - 1);
|
|
713
|
+
}
|
|
714
|
+
}, [isOpen]);
|
|
715
|
+
useEffect(() => {
|
|
716
|
+
if (!isOpen) return;
|
|
717
|
+
let yTo = 0;
|
|
718
|
+
if (snapPointsProp) {
|
|
719
|
+
const snapIndex = initialSnap;
|
|
720
|
+
const sheetHeight = getSheetHeight(sheetRef);
|
|
721
|
+
const snapPoints = getSnapPoints({ snapPointsProp, sheetHeight });
|
|
722
|
+
const ySnap = getSnapY({ sheetHeight, snapPoints, snapIndex });
|
|
723
|
+
if (ySnap !== null) {
|
|
724
|
+
yTo = ySnap;
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
animate(y, yTo, animationOptions);
|
|
676
728
|
}, [isOpen]);
|
|
677
729
|
useImperativeHandle(ref, () => ({
|
|
678
730
|
y,
|
|
679
731
|
snapTo: (snapIndex) => {
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
animate(y,
|
|
689
|
-
|
|
690
|
-
if (
|
|
732
|
+
if (!snapPointsProp) {
|
|
733
|
+
console.warn("Snapping is not possible without `snapPoints` prop.");
|
|
734
|
+
return;
|
|
735
|
+
}
|
|
736
|
+
const sheetHeight = getSheetHeight(sheetRef);
|
|
737
|
+
const snapPoints = getSnapPoints({ snapPointsProp, sheetHeight });
|
|
738
|
+
const ySnap = getSnapY({ sheetHeight, snapPoints, snapIndex });
|
|
739
|
+
if (ySnap !== null) {
|
|
740
|
+
animate(y, ySnap, animationOptions);
|
|
741
|
+
onSnap == null ? void 0 : onSnap(snapIndex);
|
|
742
|
+
if (ySnap + 1 >= sheetHeight) {
|
|
743
|
+
onClose();
|
|
744
|
+
}
|
|
691
745
|
}
|
|
692
746
|
}
|
|
693
747
|
}));
|
|
748
|
+
if (rootId) {
|
|
749
|
+
console.warn(
|
|
750
|
+
"The `rootId` prop is deprecated and will be removed in the next major version. Use `modalEffectRootId` instead."
|
|
751
|
+
);
|
|
752
|
+
}
|
|
694
753
|
useModalEffect({
|
|
695
754
|
y,
|
|
696
|
-
|
|
697
|
-
|
|
755
|
+
sheetRef,
|
|
756
|
+
snapPointsProp,
|
|
757
|
+
rootId: rootId || modalEffectRootId,
|
|
758
|
+
startThreshold: modalEffectThreshold
|
|
759
|
+
});
|
|
760
|
+
usePreventScroll({
|
|
761
|
+
isDisabled: disableScrollLocking || !isOpen
|
|
698
762
|
});
|
|
699
|
-
usePreventScroll({ isDisabled: disableScrollLocking || !isOpen });
|
|
700
763
|
const dragProps = useMemo(() => {
|
|
701
764
|
const dragProps2 = {
|
|
702
765
|
drag: "y",
|
|
@@ -714,7 +777,6 @@ var Sheet = forwardRef(
|
|
|
714
777
|
sheetRef,
|
|
715
778
|
isOpen,
|
|
716
779
|
initialSnap,
|
|
717
|
-
snapPoints,
|
|
718
780
|
detent,
|
|
719
781
|
indicatorRotation,
|
|
720
782
|
callbacks,
|
|
@@ -741,23 +803,50 @@ var Sheet = forwardRef(
|
|
|
741
803
|
}
|
|
742
804
|
);
|
|
743
805
|
Sheet.displayName = "Sheet";
|
|
806
|
+
function useEventCallbacks(isOpen, callbacks) {
|
|
807
|
+
const prevOpen = usePrevious(isOpen);
|
|
808
|
+
const didOpen = useRef(false);
|
|
809
|
+
const handleAnimationComplete = useCallback(() => {
|
|
810
|
+
var _a, _b, _c, _d;
|
|
811
|
+
if (!didOpen.current) {
|
|
812
|
+
(_b = (_a = callbacks.current).onOpenEnd) == null ? void 0 : _b.call(_a);
|
|
813
|
+
didOpen.current = true;
|
|
814
|
+
} else {
|
|
815
|
+
(_d = (_c = callbacks.current).onCloseEnd) == null ? void 0 : _d.call(_c);
|
|
816
|
+
didOpen.current = false;
|
|
817
|
+
}
|
|
818
|
+
}, [isOpen, prevOpen]);
|
|
819
|
+
useEffect(() => {
|
|
820
|
+
var _a, _b, _c, _d;
|
|
821
|
+
if (!prevOpen && isOpen) {
|
|
822
|
+
(_b = (_a = callbacks.current).onOpenStart) == null ? void 0 : _b.call(_a);
|
|
823
|
+
} else if (!isOpen && prevOpen) {
|
|
824
|
+
(_d = (_c = callbacks.current).onCloseStart) == null ? void 0 : _d.call(_c);
|
|
825
|
+
}
|
|
826
|
+
}, [isOpen, prevOpen]);
|
|
827
|
+
return { handleAnimationComplete };
|
|
828
|
+
}
|
|
829
|
+
function usePrevious(state) {
|
|
830
|
+
const ref = useRef(void 0);
|
|
831
|
+
useEffect(() => {
|
|
832
|
+
ref.current = state;
|
|
833
|
+
});
|
|
834
|
+
return ref.current;
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
// src/SheetContainer.tsx
|
|
744
838
|
var SheetContainer = forwardRef(
|
|
745
|
-
({ children, style
|
|
839
|
+
({ children, style, className = "", ...rest }, ref) => {
|
|
746
840
|
const {
|
|
747
841
|
y,
|
|
748
842
|
isOpen,
|
|
749
843
|
callbacks,
|
|
750
|
-
snapPoints,
|
|
751
|
-
initialSnap = 0,
|
|
752
844
|
sheetRef,
|
|
753
845
|
windowHeight,
|
|
754
846
|
detent,
|
|
755
847
|
animationOptions
|
|
756
848
|
} = useSheetContext();
|
|
757
849
|
const { handleAnimationComplete } = useEventCallbacks(isOpen, callbacks);
|
|
758
|
-
const initialY = snapPoints ? snapPoints[0] - snapPoints[initialSnap] : 0;
|
|
759
|
-
const maxSnapHeight = snapPoints ? snapPoints[0] : null;
|
|
760
|
-
const height = maxSnapHeight !== null ? `min(${maxSnapHeight}px, ${MAX_HEIGHT})` : MAX_HEIGHT;
|
|
761
850
|
return /* @__PURE__ */ React2.createElement(
|
|
762
851
|
motion.div,
|
|
763
852
|
{
|
|
@@ -767,12 +856,11 @@ var SheetContainer = forwardRef(
|
|
|
767
856
|
style: {
|
|
768
857
|
...styles.container,
|
|
769
858
|
...style,
|
|
770
|
-
...detent === "full-height" && { height },
|
|
771
|
-
...detent === "content-height" && { maxHeight:
|
|
859
|
+
...detent === "full-height" && { height: MAX_HEIGHT },
|
|
860
|
+
...detent === "content-height" && { maxHeight: MAX_HEIGHT },
|
|
772
861
|
y
|
|
773
862
|
},
|
|
774
863
|
initial: { y: windowHeight },
|
|
775
|
-
animate: { y: initialY, transition: animationOptions },
|
|
776
864
|
exit: { y: windowHeight, transition: animationOptions },
|
|
777
865
|
onAnimationComplete: handleAnimationComplete
|
|
778
866
|
},
|
|
@@ -781,6 +869,14 @@ var SheetContainer = forwardRef(
|
|
|
781
869
|
}
|
|
782
870
|
);
|
|
783
871
|
SheetContainer.displayName = "SheetContainer";
|
|
872
|
+
var constraints = { bottom: 0, top: 0, left: 0, right: 0 };
|
|
873
|
+
function useDragConstraints() {
|
|
874
|
+
const constraintsRef = useRef(null);
|
|
875
|
+
const onMeasureDragConstraints = useCallback(() => constraints, []);
|
|
876
|
+
return { constraintsRef, onMeasureDragConstraints };
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
// src/SheetContent.tsx
|
|
784
880
|
var SheetContent = forwardRef(
|
|
785
881
|
({ children, style, disableDrag, className = "", ...rest }, ref) => {
|
|
786
882
|
const sheetContext = useSheetContext();
|