@simoneggert/react-modal-sheet 5.4.3
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/LICENSE +21 -0
- package/README.md +994 -0
- package/dist/index.d.mts +188 -0
- package/dist/index.d.ts +188 -0
- package/dist/index.js +1511 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +1502 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +87 -0
- package/src/SheetBackdrop.tsx +46 -0
- package/src/SheetContainer.tsx +53 -0
- package/src/SheetContent.tsx +117 -0
- package/src/SheetDragIndicator.tsx +52 -0
- package/src/SheetHeader.tsx +52 -0
- package/src/constants.ts +19 -0
- package/src/context.tsx +12 -0
- package/src/debug.ts +38 -0
- package/src/hooks/use-dimensions.ts +30 -0
- package/src/hooks/use-drag-constraints.ts +14 -0
- package/src/hooks/use-isomorphic-layout-effect.ts +5 -0
- package/src/hooks/use-keyboard-avoidance.ts +75 -0
- package/src/hooks/use-modal-effect.ts +161 -0
- package/src/hooks/use-prevent-scroll.ts +357 -0
- package/src/hooks/use-resize-observer.ts +31 -0
- package/src/hooks/use-safe-area-insets.ts +40 -0
- package/src/hooks/use-scroll-position.ts +142 -0
- package/src/hooks/use-sheet-state.ts +63 -0
- package/src/hooks/use-stable-callback.ts +18 -0
- package/src/hooks/use-virtual-keyboard.ts +269 -0
- package/src/index.tsx +41 -0
- package/src/sheet.tsx +414 -0
- package/src/snap.ts +242 -0
- package/src/styles.ts +110 -0
- package/src/types.tsx +154 -0
- package/src/utils.ts +116 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1511 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var react = require('motion/react');
|
|
4
|
+
var React4 = require('react');
|
|
5
|
+
var reactDom = require('react-dom');
|
|
6
|
+
var useMeasure = require('react-use-measure');
|
|
7
|
+
var motion = require('motion');
|
|
8
|
+
|
|
9
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
10
|
+
|
|
11
|
+
var React4__default = /*#__PURE__*/_interopDefault(React4);
|
|
12
|
+
var useMeasure__default = /*#__PURE__*/_interopDefault(useMeasure);
|
|
13
|
+
|
|
14
|
+
// src/SheetBackdrop.tsx
|
|
15
|
+
var SheetContext = React4.createContext(
|
|
16
|
+
void 0
|
|
17
|
+
);
|
|
18
|
+
function useSheetContext() {
|
|
19
|
+
const context = React4.useContext(SheetContext);
|
|
20
|
+
if (!context) throw new Error("Sheet context error");
|
|
21
|
+
return context;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// src/styles.ts
|
|
25
|
+
var styles = {
|
|
26
|
+
root: {
|
|
27
|
+
base: {
|
|
28
|
+
position: "fixed",
|
|
29
|
+
top: 0,
|
|
30
|
+
bottom: 0,
|
|
31
|
+
left: 0,
|
|
32
|
+
right: 0,
|
|
33
|
+
overflow: "hidden",
|
|
34
|
+
pointerEvents: "none"
|
|
35
|
+
},
|
|
36
|
+
decorative: {}
|
|
37
|
+
},
|
|
38
|
+
backdrop: {
|
|
39
|
+
base: {
|
|
40
|
+
zIndex: 1,
|
|
41
|
+
position: "fixed",
|
|
42
|
+
top: 0,
|
|
43
|
+
left: 0,
|
|
44
|
+
width: "100%",
|
|
45
|
+
height: "100%",
|
|
46
|
+
touchAction: "none",
|
|
47
|
+
userSelect: "none"
|
|
48
|
+
},
|
|
49
|
+
decorative: {
|
|
50
|
+
backgroundColor: "rgba(0, 0, 0, 0.2)",
|
|
51
|
+
border: "none",
|
|
52
|
+
WebkitTapHighlightColor: "transparent"
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
container: {
|
|
56
|
+
base: {
|
|
57
|
+
zIndex: 2,
|
|
58
|
+
position: "absolute",
|
|
59
|
+
left: 0,
|
|
60
|
+
bottom: 0,
|
|
61
|
+
width: "100%",
|
|
62
|
+
pointerEvents: "auto",
|
|
63
|
+
display: "flex",
|
|
64
|
+
flexDirection: "column"
|
|
65
|
+
},
|
|
66
|
+
decorative: {
|
|
67
|
+
backgroundColor: "#fff",
|
|
68
|
+
borderTopRightRadius: "8px",
|
|
69
|
+
borderTopLeftRadius: "8px",
|
|
70
|
+
boxShadow: "0px -2px 16px rgba(0, 0, 0, 0.3)"
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
headerWrapper: {
|
|
74
|
+
base: {
|
|
75
|
+
width: "100%"
|
|
76
|
+
},
|
|
77
|
+
decorative: {}
|
|
78
|
+
},
|
|
79
|
+
header: {
|
|
80
|
+
base: {
|
|
81
|
+
width: "100%",
|
|
82
|
+
position: "relative"
|
|
83
|
+
},
|
|
84
|
+
decorative: {
|
|
85
|
+
height: "40px",
|
|
86
|
+
display: "flex",
|
|
87
|
+
alignItems: "center",
|
|
88
|
+
justifyContent: "center"
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
indicatorWrapper: {
|
|
92
|
+
base: {
|
|
93
|
+
display: "flex"
|
|
94
|
+
},
|
|
95
|
+
decorative: {}
|
|
96
|
+
},
|
|
97
|
+
indicator: {
|
|
98
|
+
base: {
|
|
99
|
+
display: "inline-block"
|
|
100
|
+
},
|
|
101
|
+
decorative: {
|
|
102
|
+
width: "18px",
|
|
103
|
+
height: "4px",
|
|
104
|
+
borderRadius: "99px",
|
|
105
|
+
backgroundColor: "#ddd"
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
content: {
|
|
109
|
+
base: {
|
|
110
|
+
minHeight: "0px",
|
|
111
|
+
position: "relative",
|
|
112
|
+
flexGrow: 1,
|
|
113
|
+
display: "flex",
|
|
114
|
+
flexDirection: "column"
|
|
115
|
+
},
|
|
116
|
+
decorative: {}
|
|
117
|
+
},
|
|
118
|
+
scroller: {
|
|
119
|
+
base: {
|
|
120
|
+
height: "100%",
|
|
121
|
+
overflowY: "auto",
|
|
122
|
+
overscrollBehaviorY: "none"
|
|
123
|
+
},
|
|
124
|
+
decorative: {}
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
// src/constants.ts
|
|
129
|
+
var DEFAULT_HEIGHT = "calc(100% - env(safe-area-inset-top) - 34px)";
|
|
130
|
+
var IS_SSR = typeof window === "undefined";
|
|
131
|
+
var DEFAULT_TWEEN_CONFIG = {
|
|
132
|
+
ease: "easeOut",
|
|
133
|
+
duration: 0.2
|
|
134
|
+
};
|
|
135
|
+
var REDUCED_MOTION_TWEEN_CONFIG = {
|
|
136
|
+
ease: "linear",
|
|
137
|
+
duration: 0.01
|
|
138
|
+
};
|
|
139
|
+
var DEFAULT_DRAG_CLOSE_THRESHOLD = 0.6;
|
|
140
|
+
var DEFAULT_DRAG_VELOCITY_THRESHOLD = 500;
|
|
141
|
+
|
|
142
|
+
// src/utils.ts
|
|
143
|
+
function applyStyles(styles2, unstyled) {
|
|
144
|
+
return unstyled ? styles2.base : { ...styles2.base, ...styles2.decorative };
|
|
145
|
+
}
|
|
146
|
+
function isAscendingOrder(arr) {
|
|
147
|
+
for (let i = 0; i < arr.length; i++) {
|
|
148
|
+
if (arr[i + 1] < arr[i]) return false;
|
|
149
|
+
}
|
|
150
|
+
return true;
|
|
151
|
+
}
|
|
152
|
+
function mergeRefs(refs) {
|
|
153
|
+
return (value) => {
|
|
154
|
+
refs.forEach((ref) => {
|
|
155
|
+
if (typeof ref === "function") {
|
|
156
|
+
ref(value);
|
|
157
|
+
} else if (ref) {
|
|
158
|
+
ref.current = value;
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
function testPlatform(re) {
|
|
164
|
+
var _a;
|
|
165
|
+
return typeof window !== "undefined" && window.navigator != null ? re.test(
|
|
166
|
+
// @ts-expect-error
|
|
167
|
+
((_a = window.navigator.userAgentData) == null ? void 0 : _a.platform) || window.navigator.platform
|
|
168
|
+
) : false;
|
|
169
|
+
}
|
|
170
|
+
function cached(fn) {
|
|
171
|
+
let res = null;
|
|
172
|
+
return () => {
|
|
173
|
+
if (res == null) {
|
|
174
|
+
res = fn();
|
|
175
|
+
}
|
|
176
|
+
return res;
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
var isMac = cached(function() {
|
|
180
|
+
return testPlatform(/^Mac/i);
|
|
181
|
+
});
|
|
182
|
+
var isIPhone = cached(function() {
|
|
183
|
+
return testPlatform(/^iPhone/i);
|
|
184
|
+
});
|
|
185
|
+
var isIPad = cached(function() {
|
|
186
|
+
return testPlatform(/^iPad/i) || isMac() && navigator.maxTouchPoints > 1;
|
|
187
|
+
});
|
|
188
|
+
var isIOS = cached(function() {
|
|
189
|
+
return isIPhone() || isIPad();
|
|
190
|
+
});
|
|
191
|
+
function waitForElement(className, interval = 50, maxAttempts = 20) {
|
|
192
|
+
return new Promise((resolve) => {
|
|
193
|
+
let attempts = 0;
|
|
194
|
+
const timer = setInterval(() => {
|
|
195
|
+
const element = document.getElementsByClassName(
|
|
196
|
+
className
|
|
197
|
+
)[0];
|
|
198
|
+
attempts++;
|
|
199
|
+
if (element || attempts >= maxAttempts) {
|
|
200
|
+
clearInterval(timer);
|
|
201
|
+
resolve(element);
|
|
202
|
+
}
|
|
203
|
+
}, interval);
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
var nonTextInputTypes = /* @__PURE__ */ new Set([
|
|
207
|
+
"checkbox",
|
|
208
|
+
"radio",
|
|
209
|
+
"range",
|
|
210
|
+
"color",
|
|
211
|
+
"file",
|
|
212
|
+
"image",
|
|
213
|
+
"button",
|
|
214
|
+
"submit",
|
|
215
|
+
"reset"
|
|
216
|
+
]);
|
|
217
|
+
function willOpenKeyboard(target) {
|
|
218
|
+
return target instanceof HTMLInputElement && !nonTextInputTypes.has(target.type) || target instanceof HTMLTextAreaElement || target instanceof HTMLElement && target.isContentEditable;
|
|
219
|
+
}
|
|
220
|
+
function isHTTPS() {
|
|
221
|
+
return typeof window !== "undefined" && window.isSecureContext;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// src/SheetBackdrop.tsx
|
|
225
|
+
var isClickable = (props) => !!props.onClick || !!props.onTap;
|
|
226
|
+
var SheetBackdrop = React4.forwardRef(
|
|
227
|
+
({ style, className = "", unstyled, ...rest }, ref) => {
|
|
228
|
+
const sheetContext = useSheetContext();
|
|
229
|
+
const clickable = isClickable(rest);
|
|
230
|
+
const Comp = clickable ? react.motion.button : react.motion.div;
|
|
231
|
+
const pointerEvents = clickable ? "auto" : "none";
|
|
232
|
+
const isUnstyled = unstyled ?? sheetContext.unstyled;
|
|
233
|
+
const backdropStyle = {
|
|
234
|
+
...applyStyles(styles.backdrop, isUnstyled),
|
|
235
|
+
...style,
|
|
236
|
+
pointerEvents
|
|
237
|
+
};
|
|
238
|
+
const animationProps = sheetContext.prefersReducedMotion ? {} : {
|
|
239
|
+
initial: { opacity: 0 },
|
|
240
|
+
animate: { opacity: 1 },
|
|
241
|
+
exit: { opacity: 0 },
|
|
242
|
+
transition: { duration: 1 }
|
|
243
|
+
};
|
|
244
|
+
return /* @__PURE__ */ React4__default.default.createElement(
|
|
245
|
+
Comp,
|
|
246
|
+
{
|
|
247
|
+
...animationProps,
|
|
248
|
+
...rest,
|
|
249
|
+
ref,
|
|
250
|
+
className: `react-modal-sheet-backdrop ${className}`,
|
|
251
|
+
style: backdropStyle
|
|
252
|
+
}
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
);
|
|
256
|
+
SheetBackdrop.displayName = "SheetBackdrop";
|
|
257
|
+
var SheetContainer = React4.forwardRef(
|
|
258
|
+
({ children, style, className = "", unstyled, ...rest }, ref) => {
|
|
259
|
+
const sheetContext = useSheetContext();
|
|
260
|
+
const isUnstyled = unstyled ?? sheetContext.unstyled;
|
|
261
|
+
const containerStyle = {
|
|
262
|
+
...applyStyles(styles.container, isUnstyled),
|
|
263
|
+
...style,
|
|
264
|
+
y: sheetContext.y
|
|
265
|
+
};
|
|
266
|
+
if (sheetContext.detent === "default") {
|
|
267
|
+
containerStyle.height = DEFAULT_HEIGHT;
|
|
268
|
+
}
|
|
269
|
+
if (sheetContext.detent === "full") {
|
|
270
|
+
containerStyle.height = "100%";
|
|
271
|
+
containerStyle.maxHeight = "100%";
|
|
272
|
+
}
|
|
273
|
+
if (sheetContext.detent === "content") {
|
|
274
|
+
containerStyle.height = "auto";
|
|
275
|
+
containerStyle.maxHeight = DEFAULT_HEIGHT;
|
|
276
|
+
}
|
|
277
|
+
return /* @__PURE__ */ React4__default.default.createElement(
|
|
278
|
+
react.motion.div,
|
|
279
|
+
{
|
|
280
|
+
...rest,
|
|
281
|
+
ref: mergeRefs([
|
|
282
|
+
ref,
|
|
283
|
+
sheetContext.sheetRef,
|
|
284
|
+
sheetContext.sheetBoundsRef
|
|
285
|
+
]),
|
|
286
|
+
className: `react-modal-sheet-container ${className}`,
|
|
287
|
+
style: containerStyle
|
|
288
|
+
},
|
|
289
|
+
children
|
|
290
|
+
);
|
|
291
|
+
}
|
|
292
|
+
);
|
|
293
|
+
SheetContainer.displayName = "SheetContainer";
|
|
294
|
+
var constraints = { bottom: 0, top: 0, left: 0, right: 0 };
|
|
295
|
+
function useDragConstraints() {
|
|
296
|
+
const ref = React4.useRef(null);
|
|
297
|
+
const onMeasure = React4.useCallback(() => constraints, []);
|
|
298
|
+
return { ref, onMeasure };
|
|
299
|
+
}
|
|
300
|
+
var useIsomorphicLayoutEffect = IS_SSR ? React4.useEffect : React4.useLayoutEffect;
|
|
301
|
+
|
|
302
|
+
// src/hooks/use-stable-callback.ts
|
|
303
|
+
function useStableCallback(handler) {
|
|
304
|
+
const handlerRef = React4.useRef(void 0);
|
|
305
|
+
useIsomorphicLayoutEffect(() => {
|
|
306
|
+
handlerRef.current = handler;
|
|
307
|
+
});
|
|
308
|
+
return React4.useCallback((...args) => {
|
|
309
|
+
const fn = handlerRef.current;
|
|
310
|
+
return fn == null ? void 0 : fn(...args);
|
|
311
|
+
}, []);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// src/hooks/use-scroll-position.ts
|
|
315
|
+
function useScrollPosition(options = {}) {
|
|
316
|
+
const { debounceDelay = 32, isEnabled = true } = options;
|
|
317
|
+
const scrollTimeoutRef = React4.useRef(null);
|
|
318
|
+
const [element, setElement] = React4.useState(null);
|
|
319
|
+
const [scrollPosition, setScrollPosition] = React4.useState(void 0);
|
|
320
|
+
const scrollRef = React4.useMemo(
|
|
321
|
+
() => (element2) => {
|
|
322
|
+
setElement(element2);
|
|
323
|
+
},
|
|
324
|
+
[]
|
|
325
|
+
);
|
|
326
|
+
const determineScrollPosition = useStableCallback((element2) => {
|
|
327
|
+
const { scrollTop, scrollHeight, clientHeight } = element2;
|
|
328
|
+
const isScrollable2 = scrollHeight > clientHeight;
|
|
329
|
+
if (!isScrollable2) {
|
|
330
|
+
if (scrollPosition) setScrollPosition(void 0);
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
const isAtTop = scrollTop <= 0;
|
|
334
|
+
const isAtBottom = Math.ceil(scrollHeight) - Math.ceil(scrollTop) === Math.ceil(clientHeight);
|
|
335
|
+
let position;
|
|
336
|
+
if (isAtTop) {
|
|
337
|
+
position = "top";
|
|
338
|
+
} else if (isAtBottom) {
|
|
339
|
+
position = "bottom";
|
|
340
|
+
} else {
|
|
341
|
+
position = "middle";
|
|
342
|
+
}
|
|
343
|
+
element2.style.touchAction = isAtTop ? "pan-down" : "";
|
|
344
|
+
if (position === scrollPosition) return;
|
|
345
|
+
setScrollPosition(position);
|
|
346
|
+
});
|
|
347
|
+
const onScroll = useStableCallback((event) => {
|
|
348
|
+
if (event.currentTarget instanceof HTMLElement) {
|
|
349
|
+
const el = event.currentTarget;
|
|
350
|
+
if (scrollTimeoutRef.current) clearTimeout(scrollTimeoutRef.current);
|
|
351
|
+
if (debounceDelay === 0) {
|
|
352
|
+
determineScrollPosition(el);
|
|
353
|
+
} else {
|
|
354
|
+
scrollTimeoutRef.current = setTimeout(
|
|
355
|
+
() => determineScrollPosition(el),
|
|
356
|
+
debounceDelay
|
|
357
|
+
);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
});
|
|
361
|
+
const onTouchStart = useStableCallback((event) => {
|
|
362
|
+
if (event.currentTarget instanceof HTMLElement) {
|
|
363
|
+
const element2 = event.currentTarget;
|
|
364
|
+
requestAnimationFrame(() => {
|
|
365
|
+
determineScrollPosition(element2);
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
});
|
|
369
|
+
React4.useEffect(() => {
|
|
370
|
+
if (!element || !isEnabled) return;
|
|
371
|
+
determineScrollPosition(element);
|
|
372
|
+
element.addEventListener("scroll", onScroll);
|
|
373
|
+
element.addEventListener("touchstart", onTouchStart);
|
|
374
|
+
return () => {
|
|
375
|
+
if (scrollTimeoutRef.current) clearTimeout(scrollTimeoutRef.current);
|
|
376
|
+
element.removeEventListener("scroll", onScroll);
|
|
377
|
+
element.removeEventListener("touchstart", onTouchStart);
|
|
378
|
+
};
|
|
379
|
+
}, [element, isEnabled]);
|
|
380
|
+
return {
|
|
381
|
+
scrollRef,
|
|
382
|
+
scrollPosition
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// src/SheetContent.tsx
|
|
387
|
+
var SheetContent = React4.forwardRef(
|
|
388
|
+
({
|
|
389
|
+
disableScroll: disableScrollProp,
|
|
390
|
+
disableDrag: disableDragProp,
|
|
391
|
+
children,
|
|
392
|
+
style: styleProp,
|
|
393
|
+
className = "",
|
|
394
|
+
scrollClassName = "",
|
|
395
|
+
scrollStyle: scrollStyleProp,
|
|
396
|
+
scrollRef: scrollRefProp = null,
|
|
397
|
+
unstyled,
|
|
398
|
+
...rest
|
|
399
|
+
}, ref) => {
|
|
400
|
+
const sheetContext = useSheetContext();
|
|
401
|
+
const dragConstraints = useDragConstraints();
|
|
402
|
+
const scroll = useScrollPosition({
|
|
403
|
+
isEnabled: typeof disableScrollProp === "function" ? true : !disableScrollProp
|
|
404
|
+
});
|
|
405
|
+
const disableScroll = typeof disableScrollProp === "function" ? disableScrollProp({
|
|
406
|
+
scrollPosition: scroll.scrollPosition,
|
|
407
|
+
currentSnap: sheetContext.currentSnap
|
|
408
|
+
}) : Boolean(disableScrollProp);
|
|
409
|
+
const disableDragDueToScroll = !disableScroll && scroll.scrollPosition && scroll.scrollPosition !== "top";
|
|
410
|
+
const disableDragDueToProp = typeof disableDragProp === "function" ? disableDragProp({
|
|
411
|
+
scrollPosition: scroll.scrollPosition,
|
|
412
|
+
currentSnap: sheetContext.currentSnap
|
|
413
|
+
}) : Boolean(disableDragProp);
|
|
414
|
+
const disableDrag = disableDragDueToProp || disableDragDueToScroll || sheetContext.disableDrag;
|
|
415
|
+
const dragProps = disableDrag || sheetContext.disableDrag ? void 0 : sheetContext.dragProps;
|
|
416
|
+
const isUnstyled = unstyled ?? sheetContext.unstyled;
|
|
417
|
+
const contentStyle = {
|
|
418
|
+
...applyStyles(styles.content, isUnstyled),
|
|
419
|
+
...styleProp
|
|
420
|
+
};
|
|
421
|
+
const scrollStyle = applyStyles(styles.scroller, isUnstyled);
|
|
422
|
+
if (sheetContext.avoidKeyboard) {
|
|
423
|
+
scrollStyle.paddingBottom = isHTTPS() ? "env(keyboard-inset-height, var(--keyboard-inset-height, 0px))" : "var(--keyboard-inset-height, 0px)";
|
|
424
|
+
}
|
|
425
|
+
if (disableScroll) {
|
|
426
|
+
scrollStyle.overflowY = "hidden";
|
|
427
|
+
}
|
|
428
|
+
if (dragProps !== void 0) {
|
|
429
|
+
scrollStyle.touchAction = "pan-down";
|
|
430
|
+
}
|
|
431
|
+
return /* @__PURE__ */ React4__default.default.createElement(
|
|
432
|
+
react.motion.div,
|
|
433
|
+
{
|
|
434
|
+
...rest,
|
|
435
|
+
ref: mergeRefs([ref, dragConstraints.ref]),
|
|
436
|
+
className: `react-modal-sheet-content ${className}`,
|
|
437
|
+
style: contentStyle,
|
|
438
|
+
...dragProps,
|
|
439
|
+
dragConstraints: dragConstraints.ref,
|
|
440
|
+
onMeasureDragConstraints: dragConstraints.onMeasure
|
|
441
|
+
},
|
|
442
|
+
/* @__PURE__ */ React4__default.default.createElement(
|
|
443
|
+
react.motion.div,
|
|
444
|
+
{
|
|
445
|
+
ref: mergeRefs([scroll.scrollRef, scrollRefProp]),
|
|
446
|
+
style: { ...scrollStyle, ...scrollStyleProp },
|
|
447
|
+
className: `react-modal-sheet-content-scroller ${scrollClassName}`
|
|
448
|
+
},
|
|
449
|
+
children
|
|
450
|
+
)
|
|
451
|
+
);
|
|
452
|
+
}
|
|
453
|
+
);
|
|
454
|
+
SheetContent.displayName = "SheetContent";
|
|
455
|
+
function SheetDragIndicator({
|
|
456
|
+
style,
|
|
457
|
+
className = "",
|
|
458
|
+
unstyled,
|
|
459
|
+
...rest
|
|
460
|
+
}) {
|
|
461
|
+
const sheetContext = useSheetContext();
|
|
462
|
+
const indicator1Transform = react.useTransform(
|
|
463
|
+
sheetContext.indicatorRotation,
|
|
464
|
+
(r) => `translateX(2px) rotate(${r}deg)`
|
|
465
|
+
);
|
|
466
|
+
const indicator2Transform = react.useTransform(
|
|
467
|
+
sheetContext.indicatorRotation,
|
|
468
|
+
(r) => `translateX(-2px) rotate(${ -1 * r}deg)`
|
|
469
|
+
);
|
|
470
|
+
const isUnstyled = unstyled ?? sheetContext.unstyled;
|
|
471
|
+
const indicatorWrapperStyle = {
|
|
472
|
+
...applyStyles(styles.indicatorWrapper, isUnstyled),
|
|
473
|
+
...style
|
|
474
|
+
};
|
|
475
|
+
const indicatorStyle = applyStyles(styles.indicator, isUnstyled);
|
|
476
|
+
return /* @__PURE__ */ React4__default.default.createElement(
|
|
477
|
+
"div",
|
|
478
|
+
{
|
|
479
|
+
className: `react-modal-sheet-drag-indicator-container ${className}`,
|
|
480
|
+
style: indicatorWrapperStyle,
|
|
481
|
+
...rest
|
|
482
|
+
},
|
|
483
|
+
/* @__PURE__ */ React4__default.default.createElement(
|
|
484
|
+
react.motion.span,
|
|
485
|
+
{
|
|
486
|
+
className: "react-modal-sheet-drag-indicator",
|
|
487
|
+
style: { ...indicatorStyle, transform: indicator1Transform }
|
|
488
|
+
}
|
|
489
|
+
),
|
|
490
|
+
/* @__PURE__ */ React4__default.default.createElement(
|
|
491
|
+
react.motion.span,
|
|
492
|
+
{
|
|
493
|
+
className: "react-modal-sheet-drag-indicator",
|
|
494
|
+
style: { ...indicatorStyle, transform: indicator2Transform }
|
|
495
|
+
}
|
|
496
|
+
)
|
|
497
|
+
);
|
|
498
|
+
}
|
|
499
|
+
var SheetHeader = React4.forwardRef(
|
|
500
|
+
({ children, style, disableDrag, unstyled, className = "", ...rest }, ref) => {
|
|
501
|
+
const sheetContext = useSheetContext();
|
|
502
|
+
const dragConstraints = useDragConstraints();
|
|
503
|
+
const dragProps = disableDrag || sheetContext.disableDrag ? void 0 : sheetContext.dragProps;
|
|
504
|
+
const isUnstyled = unstyled ?? sheetContext.unstyled;
|
|
505
|
+
const headerWrapperStyle = {
|
|
506
|
+
...applyStyles(styles.headerWrapper, isUnstyled),
|
|
507
|
+
...style
|
|
508
|
+
};
|
|
509
|
+
const headerStyle = applyStyles(styles.header, isUnstyled);
|
|
510
|
+
return /* @__PURE__ */ React4__default.default.createElement(
|
|
511
|
+
react.motion.div,
|
|
512
|
+
{
|
|
513
|
+
...rest,
|
|
514
|
+
ref: mergeRefs([ref, dragConstraints.ref]),
|
|
515
|
+
style: headerWrapperStyle,
|
|
516
|
+
className: `react-modal-sheet-header-container ${className}`,
|
|
517
|
+
...dragProps,
|
|
518
|
+
dragConstraints: dragConstraints.ref,
|
|
519
|
+
onMeasureDragConstraints: dragConstraints.onMeasure
|
|
520
|
+
},
|
|
521
|
+
children || /* @__PURE__ */ React4__default.default.createElement("div", { className: "react-modal-sheet-header", style: headerStyle }, /* @__PURE__ */ React4__default.default.createElement(SheetDragIndicator, null))
|
|
522
|
+
);
|
|
523
|
+
}
|
|
524
|
+
);
|
|
525
|
+
SheetHeader.displayName = "SheetHeader";
|
|
526
|
+
function useDimensions() {
|
|
527
|
+
const [dimensions, setDimensions] = React4.useState(() => ({
|
|
528
|
+
windowHeight: !IS_SSR ? window.innerHeight : 0,
|
|
529
|
+
windowWidth: !IS_SSR ? window.innerWidth : 0
|
|
530
|
+
}));
|
|
531
|
+
useIsomorphicLayoutEffect(() => {
|
|
532
|
+
function handler() {
|
|
533
|
+
setDimensions({
|
|
534
|
+
windowHeight: window.innerHeight,
|
|
535
|
+
windowWidth: window.innerWidth
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
handler();
|
|
539
|
+
window.addEventListener("resize", handler);
|
|
540
|
+
return () => {
|
|
541
|
+
window.removeEventListener("resize", handler);
|
|
542
|
+
};
|
|
543
|
+
}, []);
|
|
544
|
+
return dimensions;
|
|
545
|
+
}
|
|
546
|
+
var virtualKeyboardOverlayUsers = 0;
|
|
547
|
+
var initialVirtualKeyboardOverlaysContent = null;
|
|
548
|
+
function useVirtualKeyboard(options = {}) {
|
|
549
|
+
const {
|
|
550
|
+
containerRef,
|
|
551
|
+
isEnabled = true,
|
|
552
|
+
debounceDelay = 100,
|
|
553
|
+
visualViewportThreshold = 100
|
|
554
|
+
} = options;
|
|
555
|
+
const [state, setState] = React4.useState({
|
|
556
|
+
isVisible: false,
|
|
557
|
+
height: 0
|
|
558
|
+
});
|
|
559
|
+
const focusedElementRef = React4.useRef(null);
|
|
560
|
+
const debounceTimer = React4.useRef(null);
|
|
561
|
+
React4.useEffect(() => {
|
|
562
|
+
const vv = window.visualViewport;
|
|
563
|
+
const vk = getVirtualKeyboardApi();
|
|
564
|
+
function setKeyboardInsetHeightEnv(height) {
|
|
565
|
+
const element = (containerRef == null ? void 0 : containerRef.current) || document.documentElement;
|
|
566
|
+
if (vk) {
|
|
567
|
+
element.style.setProperty(
|
|
568
|
+
"--keyboard-inset-height",
|
|
569
|
+
`env(keyboard-inset-height, ${height}px)`
|
|
570
|
+
);
|
|
571
|
+
} else {
|
|
572
|
+
element.style.setProperty("--keyboard-inset-height", `${height}px`);
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
function setKeyboardState(nextState) {
|
|
576
|
+
setState(
|
|
577
|
+
(prevState) => prevState.isVisible === nextState.isVisible && prevState.height === nextState.height ? prevState : nextState
|
|
578
|
+
);
|
|
579
|
+
}
|
|
580
|
+
function resetKeyboardState() {
|
|
581
|
+
focusedElementRef.current = null;
|
|
582
|
+
setKeyboardInsetHeightEnv(0);
|
|
583
|
+
setKeyboardState({ isVisible: false, height: 0 });
|
|
584
|
+
}
|
|
585
|
+
if (!isEnabled) {
|
|
586
|
+
resetKeyboardState();
|
|
587
|
+
return;
|
|
588
|
+
}
|
|
589
|
+
function updateKeyboardState() {
|
|
590
|
+
if (debounceTimer.current) {
|
|
591
|
+
clearTimeout(debounceTimer.current);
|
|
592
|
+
}
|
|
593
|
+
debounceTimer.current = setTimeout(() => {
|
|
594
|
+
const active = getActiveElement() ?? focusedElementRef.current;
|
|
595
|
+
const inputIsFocused = active ? willOpenKeyboard(active) : false;
|
|
596
|
+
if (!inputIsFocused) {
|
|
597
|
+
resetKeyboardState();
|
|
598
|
+
return;
|
|
599
|
+
}
|
|
600
|
+
focusedElementRef.current = active;
|
|
601
|
+
if (vk == null ? void 0 : vk.overlaysContent) {
|
|
602
|
+
const keyboardHeight = vk.boundingRect.height;
|
|
603
|
+
setKeyboardInsetHeightEnv(keyboardHeight);
|
|
604
|
+
setKeyboardState({ isVisible: true, height: keyboardHeight });
|
|
605
|
+
return;
|
|
606
|
+
}
|
|
607
|
+
if (vv) {
|
|
608
|
+
const heightDiff = window.innerHeight - vv.height;
|
|
609
|
+
if (heightDiff > visualViewportThreshold) {
|
|
610
|
+
setKeyboardInsetHeightEnv(heightDiff);
|
|
611
|
+
setKeyboardState({ isVisible: true, height: heightDiff });
|
|
612
|
+
return;
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
resetKeyboardState();
|
|
616
|
+
}, debounceDelay);
|
|
617
|
+
}
|
|
618
|
+
function handleFocusIn(e) {
|
|
619
|
+
if (e.target instanceof HTMLElement && willOpenKeyboard(e.target)) {
|
|
620
|
+
focusedElementRef.current = e.target;
|
|
621
|
+
updateKeyboardState();
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
function handleFocusOut() {
|
|
625
|
+
requestAnimationFrame(() => {
|
|
626
|
+
focusedElementRef.current = getActiveElement();
|
|
627
|
+
updateKeyboardState();
|
|
628
|
+
});
|
|
629
|
+
}
|
|
630
|
+
document.addEventListener("focusin", handleFocusIn);
|
|
631
|
+
document.addEventListener("focusout", handleFocusOut);
|
|
632
|
+
if (vv) {
|
|
633
|
+
vv.addEventListener("resize", updateKeyboardState);
|
|
634
|
+
vv.addEventListener("scroll", updateKeyboardState);
|
|
635
|
+
}
|
|
636
|
+
if (vk) {
|
|
637
|
+
if (virtualKeyboardOverlayUsers === 0) {
|
|
638
|
+
initialVirtualKeyboardOverlaysContent = vk.overlaysContent;
|
|
639
|
+
vk.overlaysContent = true;
|
|
640
|
+
}
|
|
641
|
+
virtualKeyboardOverlayUsers++;
|
|
642
|
+
vk.addEventListener("geometrychange", updateKeyboardState);
|
|
643
|
+
}
|
|
644
|
+
focusedElementRef.current = getActiveElement();
|
|
645
|
+
updateKeyboardState();
|
|
646
|
+
return () => {
|
|
647
|
+
document.removeEventListener("focusin", handleFocusIn);
|
|
648
|
+
document.removeEventListener("focusout", handleFocusOut);
|
|
649
|
+
if (vv) {
|
|
650
|
+
vv.removeEventListener("resize", updateKeyboardState);
|
|
651
|
+
vv.removeEventListener("scroll", updateKeyboardState);
|
|
652
|
+
}
|
|
653
|
+
if (vk) {
|
|
654
|
+
vk.removeEventListener("geometrychange", updateKeyboardState);
|
|
655
|
+
virtualKeyboardOverlayUsers = Math.max(
|
|
656
|
+
0,
|
|
657
|
+
virtualKeyboardOverlayUsers - 1
|
|
658
|
+
);
|
|
659
|
+
if (virtualKeyboardOverlayUsers === 0) {
|
|
660
|
+
vk.overlaysContent = initialVirtualKeyboardOverlaysContent ?? false;
|
|
661
|
+
initialVirtualKeyboardOverlaysContent = null;
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
if (debounceTimer.current) {
|
|
665
|
+
clearTimeout(debounceTimer.current);
|
|
666
|
+
}
|
|
667
|
+
resetKeyboardState();
|
|
668
|
+
};
|
|
669
|
+
}, [debounceDelay, isEnabled, visualViewportThreshold]);
|
|
670
|
+
return {
|
|
671
|
+
keyboardHeight: state.height,
|
|
672
|
+
isKeyboardOpen: state.isVisible
|
|
673
|
+
};
|
|
674
|
+
}
|
|
675
|
+
function getVirtualKeyboardApi() {
|
|
676
|
+
return isHTTPS() && "virtualKeyboard" in navigator ? navigator.virtualKeyboard : null;
|
|
677
|
+
}
|
|
678
|
+
function getActiveElement() {
|
|
679
|
+
var _a;
|
|
680
|
+
let activeElement = document.activeElement;
|
|
681
|
+
while (activeElement instanceof HTMLElement && ((_a = activeElement.shadowRoot) == null ? void 0 : _a.activeElement)) {
|
|
682
|
+
activeElement = activeElement.shadowRoot.activeElement;
|
|
683
|
+
}
|
|
684
|
+
return activeElement && willOpenKeyboard(activeElement) ? activeElement : null;
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
// src/hooks/use-keyboard-avoidance.ts
|
|
688
|
+
function useKeyboardAvoidance({
|
|
689
|
+
isEnabled,
|
|
690
|
+
containerRef,
|
|
691
|
+
onWillOpenKeyboard,
|
|
692
|
+
onDidOpenKeyboard
|
|
693
|
+
}) {
|
|
694
|
+
const [focusedElement, setFocusedElement] = React4.useState(
|
|
695
|
+
null
|
|
696
|
+
);
|
|
697
|
+
const keyboard = useVirtualKeyboard({
|
|
698
|
+
isEnabled,
|
|
699
|
+
containerRef
|
|
700
|
+
});
|
|
701
|
+
const handleFocusIn = useStableCallback(async (event) => {
|
|
702
|
+
var _a;
|
|
703
|
+
const element = event.target;
|
|
704
|
+
if (willOpenKeyboard(element) && ((_a = containerRef.current) == null ? void 0 : _a.contains(element))) {
|
|
705
|
+
await (onWillOpenKeyboard == null ? void 0 : onWillOpenKeyboard(event));
|
|
706
|
+
setFocusedElement(element);
|
|
707
|
+
}
|
|
708
|
+
});
|
|
709
|
+
const handleFocusOut = useStableCallback((event) => {
|
|
710
|
+
const element = event.target;
|
|
711
|
+
if (focusedElement === element) {
|
|
712
|
+
setFocusedElement(null);
|
|
713
|
+
}
|
|
714
|
+
});
|
|
715
|
+
React4.useEffect(() => {
|
|
716
|
+
if (!isEnabled) return;
|
|
717
|
+
document.addEventListener("focusin", handleFocusIn);
|
|
718
|
+
document.addEventListener("focusout", handleFocusOut);
|
|
719
|
+
return () => {
|
|
720
|
+
document.removeEventListener("focusin", handleFocusIn);
|
|
721
|
+
document.removeEventListener("focusout", handleFocusOut);
|
|
722
|
+
};
|
|
723
|
+
}, [isEnabled]);
|
|
724
|
+
React4.useEffect(() => {
|
|
725
|
+
const containerElement = containerRef.current;
|
|
726
|
+
if (!isEnabled || !focusedElement || !containerElement || !keyboard.isKeyboardOpen) {
|
|
727
|
+
return;
|
|
728
|
+
}
|
|
729
|
+
requestAnimationFrame(() => {
|
|
730
|
+
onDidOpenKeyboard == null ? void 0 : onDidOpenKeyboard(focusedElement);
|
|
731
|
+
});
|
|
732
|
+
}, [isEnabled, keyboard.isKeyboardOpen, focusedElement]);
|
|
733
|
+
return keyboard;
|
|
734
|
+
}
|
|
735
|
+
function useSafeAreaInsets() {
|
|
736
|
+
const [insets] = React4.useState(() => {
|
|
737
|
+
const fallback = { top: 0, left: 0, right: 0, bottom: 0 };
|
|
738
|
+
if (IS_SSR) return fallback;
|
|
739
|
+
const root = document.querySelector(":root");
|
|
740
|
+
if (!root) return fallback;
|
|
741
|
+
root.style.setProperty("--rms-sat", "env(safe-area-inset-top)");
|
|
742
|
+
root.style.setProperty("--rms-sal", "env(safe-area-inset-left)");
|
|
743
|
+
root.style.setProperty("--rms-sar", "env(safe-area-inset-right)");
|
|
744
|
+
root.style.setProperty("--rms-sab", "env(safe-area-inset-bottom)");
|
|
745
|
+
const computedStyle = getComputedStyle(root);
|
|
746
|
+
const sat = getComputedValue(computedStyle, "--rms-sat");
|
|
747
|
+
const sal = getComputedValue(computedStyle, "--rms-sal");
|
|
748
|
+
const sar = getComputedValue(computedStyle, "--rms-sar");
|
|
749
|
+
const sab = getComputedValue(computedStyle, "--rms-sab");
|
|
750
|
+
root.style.removeProperty("--rms-sat");
|
|
751
|
+
root.style.removeProperty("--rms-sal");
|
|
752
|
+
root.style.removeProperty("--rms-sar");
|
|
753
|
+
root.style.removeProperty("--rms-sab");
|
|
754
|
+
return { top: sat, left: sal, right: sar, bottom: sab };
|
|
755
|
+
});
|
|
756
|
+
return insets;
|
|
757
|
+
}
|
|
758
|
+
function getComputedValue(computed, property) {
|
|
759
|
+
const strValue = computed.getPropertyValue(property).replace("px", "").trim();
|
|
760
|
+
return parseInt(strValue, 10) || 0;
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
// src/hooks/use-modal-effect.ts
|
|
764
|
+
function useModalEffect({
|
|
765
|
+
y,
|
|
766
|
+
detent,
|
|
767
|
+
rootId: _rootId,
|
|
768
|
+
sheetHeight,
|
|
769
|
+
snapPoints,
|
|
770
|
+
startThreshold
|
|
771
|
+
}) {
|
|
772
|
+
const insetTop = useSafeAreaInsets().top;
|
|
773
|
+
let rootId = _rootId;
|
|
774
|
+
if (rootId && detent === "full") {
|
|
775
|
+
console.warn('Using "full" detent with modal effect is not supported.');
|
|
776
|
+
rootId = void 0;
|
|
777
|
+
}
|
|
778
|
+
useIsomorphicLayoutEffect(() => {
|
|
779
|
+
return () => {
|
|
780
|
+
if (rootId) cleanupModalEffect(rootId);
|
|
781
|
+
};
|
|
782
|
+
}, []);
|
|
783
|
+
useIsomorphicLayoutEffect(() => {
|
|
784
|
+
if (!rootId) return;
|
|
785
|
+
const root = document.querySelector(`#${rootId}`);
|
|
786
|
+
if (!root) return;
|
|
787
|
+
const removeStartListener = y.on("animationStart", () => {
|
|
788
|
+
setupModalEffect(rootId);
|
|
789
|
+
});
|
|
790
|
+
const removeChangeListener = y.on("change", (yValue) => {
|
|
791
|
+
if (!root) return;
|
|
792
|
+
let progress = Math.max(0, 1 - yValue / sheetHeight);
|
|
793
|
+
const snapThresholdPoint = snapPoints.length > 1 ? snapPoints[snapPoints.length - 2] : void 0;
|
|
794
|
+
if (snapThresholdPoint !== void 0) {
|
|
795
|
+
const snapThresholdValue = snapThresholdPoint.snapValueY;
|
|
796
|
+
if (yValue <= snapThresholdValue) {
|
|
797
|
+
progress = (snapThresholdValue - yValue) / snapThresholdValue;
|
|
798
|
+
} else {
|
|
799
|
+
progress = 0;
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
if (startThreshold !== void 0) {
|
|
803
|
+
const startThresholdValue = sheetHeight - Math.min(Math.floor(startThreshold * sheetHeight), sheetHeight);
|
|
804
|
+
if (yValue <= startThresholdValue) {
|
|
805
|
+
progress = (startThresholdValue - yValue) / startThresholdValue;
|
|
806
|
+
} else {
|
|
807
|
+
progress = 0;
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
progress = Math.max(0, Math.min(1, progress));
|
|
811
|
+
const pageWidth = window.innerWidth;
|
|
812
|
+
const ty = motion.transform(progress, [0, 1], [0, 24 + insetTop]);
|
|
813
|
+
const s = motion.transform(progress, [0, 1], [1, (pageWidth - 16) / pageWidth]);
|
|
814
|
+
const borderRadius = motion.transform(progress, [0, 1], [0, 10]);
|
|
815
|
+
root.style.transform = `scale(${s}) translate3d(0, ${ty}px, 0)`;
|
|
816
|
+
root.style.borderTopRightRadius = `${borderRadius}px`;
|
|
817
|
+
root.style.borderTopLeftRadius = `${borderRadius}px`;
|
|
818
|
+
});
|
|
819
|
+
function onCompleted() {
|
|
820
|
+
if (y.get() - 5 >= sheetHeight) {
|
|
821
|
+
cleanupModalEffect(rootId);
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
const removeCompleteListener = y.on("animationComplete", onCompleted);
|
|
825
|
+
const removeCancelListener = y.on("animationCancel", onCompleted);
|
|
826
|
+
return () => {
|
|
827
|
+
removeStartListener();
|
|
828
|
+
removeChangeListener();
|
|
829
|
+
removeCompleteListener();
|
|
830
|
+
removeCancelListener();
|
|
831
|
+
};
|
|
832
|
+
}, [y, rootId, insetTop, startThreshold, sheetHeight]);
|
|
833
|
+
}
|
|
834
|
+
function setupModalEffect(rootId) {
|
|
835
|
+
const root = document.querySelector(`#${rootId}`);
|
|
836
|
+
const body = document.querySelector("body");
|
|
837
|
+
if (!root) return;
|
|
838
|
+
body.style.backgroundColor = "#000";
|
|
839
|
+
root.style.overflow = "hidden";
|
|
840
|
+
root.style.transitionTimingFunction = "cubic-bezier(0.32, 0.72, 0, 1)";
|
|
841
|
+
root.style.transitionProperty = "transform, border-radius";
|
|
842
|
+
root.style.transitionDuration = "0.5s";
|
|
843
|
+
root.style.transformOrigin = "center top";
|
|
844
|
+
}
|
|
845
|
+
function cleanupModalEffect(rootId) {
|
|
846
|
+
const root = document.querySelector(`#${rootId}`);
|
|
847
|
+
const body = document.querySelector("body");
|
|
848
|
+
if (!root) return;
|
|
849
|
+
body.style.removeProperty("background-color");
|
|
850
|
+
root.style.removeProperty("overflow");
|
|
851
|
+
root.style.removeProperty("transition-timing-function");
|
|
852
|
+
root.style.removeProperty("transition-property");
|
|
853
|
+
root.style.removeProperty("transition-duration");
|
|
854
|
+
root.style.removeProperty("transform-origin");
|
|
855
|
+
root.style.removeProperty("transform");
|
|
856
|
+
root.style.removeProperty("border-top-right-radius");
|
|
857
|
+
root.style.removeProperty("border-top-left-radius");
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
// src/hooks/use-prevent-scroll.ts
|
|
861
|
+
var KEYBOARD_BUFFER = 24;
|
|
862
|
+
function chain(...callbacks) {
|
|
863
|
+
return (...args) => {
|
|
864
|
+
for (const callback of callbacks) {
|
|
865
|
+
if (typeof callback === "function") {
|
|
866
|
+
callback(...args);
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
};
|
|
870
|
+
}
|
|
871
|
+
var visualViewport = typeof document !== "undefined" && window.visualViewport;
|
|
872
|
+
function isScrollable(node, checkForOverflow) {
|
|
873
|
+
if (!node) {
|
|
874
|
+
return false;
|
|
875
|
+
}
|
|
876
|
+
const style = window.getComputedStyle(node);
|
|
877
|
+
let scrollable = /(auto|scroll)/.test(
|
|
878
|
+
style.overflow + style.overflowX + style.overflowY
|
|
879
|
+
);
|
|
880
|
+
if (scrollable && checkForOverflow) {
|
|
881
|
+
scrollable = node.scrollHeight !== node.clientHeight || node.scrollWidth !== node.clientWidth;
|
|
882
|
+
}
|
|
883
|
+
return scrollable;
|
|
884
|
+
}
|
|
885
|
+
function getScrollParent(node, checkForOverflow) {
|
|
886
|
+
let scrollableNode = node;
|
|
887
|
+
if (isScrollable(scrollableNode, checkForOverflow)) {
|
|
888
|
+
scrollableNode = scrollableNode.parentElement;
|
|
889
|
+
}
|
|
890
|
+
while (scrollableNode && !isScrollable(scrollableNode, checkForOverflow)) {
|
|
891
|
+
scrollableNode = scrollableNode.parentElement;
|
|
892
|
+
}
|
|
893
|
+
return scrollableNode || document.scrollingElement || document.documentElement;
|
|
894
|
+
}
|
|
895
|
+
var preventScrollCount = 0;
|
|
896
|
+
var restore;
|
|
897
|
+
function usePreventScroll(options = {}) {
|
|
898
|
+
const { isDisabled } = options;
|
|
899
|
+
useIsomorphicLayoutEffect(() => {
|
|
900
|
+
if (isDisabled) {
|
|
901
|
+
return;
|
|
902
|
+
}
|
|
903
|
+
preventScrollCount++;
|
|
904
|
+
if (preventScrollCount === 1) {
|
|
905
|
+
if (isIOS()) {
|
|
906
|
+
restore = preventScrollMobileSafari();
|
|
907
|
+
} else {
|
|
908
|
+
restore = preventScrollStandard();
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
return () => {
|
|
912
|
+
preventScrollCount--;
|
|
913
|
+
if (preventScrollCount === 0) {
|
|
914
|
+
restore == null ? void 0 : restore();
|
|
915
|
+
}
|
|
916
|
+
};
|
|
917
|
+
}, [isDisabled]);
|
|
918
|
+
}
|
|
919
|
+
function preventScrollStandard() {
|
|
920
|
+
return chain(
|
|
921
|
+
setStyle(
|
|
922
|
+
document.documentElement,
|
|
923
|
+
"paddingRight",
|
|
924
|
+
`${window.innerWidth - document.documentElement.clientWidth}px`
|
|
925
|
+
),
|
|
926
|
+
setStyle(document.documentElement, "overflow", "hidden")
|
|
927
|
+
);
|
|
928
|
+
}
|
|
929
|
+
function preventScrollMobileSafari() {
|
|
930
|
+
let scrollable;
|
|
931
|
+
let lastY = 0;
|
|
932
|
+
const onTouchStart = (e) => {
|
|
933
|
+
var _a;
|
|
934
|
+
const target = (_a = e.composedPath()) == null ? void 0 : _a[0];
|
|
935
|
+
scrollable = getScrollParent(target, true);
|
|
936
|
+
if (scrollable === document.documentElement && scrollable === document.body) {
|
|
937
|
+
return;
|
|
938
|
+
}
|
|
939
|
+
lastY = e.changedTouches[0].pageY;
|
|
940
|
+
};
|
|
941
|
+
const onTouchMove = (e) => {
|
|
942
|
+
if (scrollable === void 0) {
|
|
943
|
+
return;
|
|
944
|
+
}
|
|
945
|
+
if (!scrollable || scrollable === document.documentElement || scrollable === document.body) {
|
|
946
|
+
e.preventDefault();
|
|
947
|
+
return;
|
|
948
|
+
}
|
|
949
|
+
const y = e.changedTouches[0].pageY;
|
|
950
|
+
const scrollTop = scrollable.scrollTop;
|
|
951
|
+
const bottom = scrollable.scrollHeight - scrollable.clientHeight;
|
|
952
|
+
if (bottom === 0) {
|
|
953
|
+
return;
|
|
954
|
+
}
|
|
955
|
+
if (scrollTop <= 0 && y > lastY || scrollTop >= bottom && y < lastY) {
|
|
956
|
+
e.preventDefault();
|
|
957
|
+
}
|
|
958
|
+
lastY = y;
|
|
959
|
+
};
|
|
960
|
+
const onTouchEnd = (e) => {
|
|
961
|
+
var _a;
|
|
962
|
+
const target = (_a = e.composedPath()) == null ? void 0 : _a[0];
|
|
963
|
+
if (willOpenKeyboard(target) && target !== document.activeElement) {
|
|
964
|
+
e.preventDefault();
|
|
965
|
+
target.style.transform = "translateY(-2000px)";
|
|
966
|
+
target.focus();
|
|
967
|
+
requestAnimationFrame(() => {
|
|
968
|
+
target.style.transform = "";
|
|
969
|
+
});
|
|
970
|
+
}
|
|
971
|
+
};
|
|
972
|
+
const onFocus = (e) => {
|
|
973
|
+
var _a;
|
|
974
|
+
const target = (_a = e.composedPath()) == null ? void 0 : _a[0];
|
|
975
|
+
if (willOpenKeyboard(target)) {
|
|
976
|
+
target.style.transform = "translateY(-2000px)";
|
|
977
|
+
requestAnimationFrame(() => {
|
|
978
|
+
target.style.transform = "";
|
|
979
|
+
if (visualViewport) {
|
|
980
|
+
if (visualViewport.height < window.innerHeight) {
|
|
981
|
+
requestAnimationFrame(() => {
|
|
982
|
+
scrollIntoView(target);
|
|
983
|
+
});
|
|
984
|
+
} else {
|
|
985
|
+
visualViewport.addEventListener(
|
|
986
|
+
"resize",
|
|
987
|
+
() => scrollIntoView(target),
|
|
988
|
+
{ once: true }
|
|
989
|
+
);
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
});
|
|
993
|
+
}
|
|
994
|
+
};
|
|
995
|
+
const onWindowScroll = () => {
|
|
996
|
+
window.scrollTo(0, 0);
|
|
997
|
+
};
|
|
998
|
+
const scrollX = window.pageXOffset;
|
|
999
|
+
const scrollY = window.pageYOffset;
|
|
1000
|
+
const restoreStyles = chain(
|
|
1001
|
+
setStyle(
|
|
1002
|
+
document.documentElement,
|
|
1003
|
+
"paddingRight",
|
|
1004
|
+
`${window.innerWidth - document.documentElement.clientWidth}px`
|
|
1005
|
+
),
|
|
1006
|
+
setStyle(document.documentElement, "overflow", "hidden")
|
|
1007
|
+
// setStyle(document.body, 'marginTop', `-${scrollY}px`)
|
|
1008
|
+
);
|
|
1009
|
+
const removeEvents = chain(
|
|
1010
|
+
addEvent(document, "touchstart", onTouchStart, {
|
|
1011
|
+
passive: false,
|
|
1012
|
+
capture: true
|
|
1013
|
+
}),
|
|
1014
|
+
addEvent(document, "touchmove", onTouchMove, {
|
|
1015
|
+
passive: false,
|
|
1016
|
+
capture: true
|
|
1017
|
+
}),
|
|
1018
|
+
addEvent(document, "touchend", onTouchEnd, {
|
|
1019
|
+
passive: false,
|
|
1020
|
+
capture: true
|
|
1021
|
+
}),
|
|
1022
|
+
addEvent(document, "focus", onFocus, true),
|
|
1023
|
+
addEvent(window, "scroll", onWindowScroll)
|
|
1024
|
+
);
|
|
1025
|
+
return () => {
|
|
1026
|
+
restoreStyles();
|
|
1027
|
+
removeEvents();
|
|
1028
|
+
window.scrollTo(scrollX, scrollY);
|
|
1029
|
+
};
|
|
1030
|
+
}
|
|
1031
|
+
function setStyle(element, style, value) {
|
|
1032
|
+
const cur = element.style[style];
|
|
1033
|
+
element.style[style] = value;
|
|
1034
|
+
return () => {
|
|
1035
|
+
element.style[style] = cur;
|
|
1036
|
+
};
|
|
1037
|
+
}
|
|
1038
|
+
function addEvent(target, event, handler, options) {
|
|
1039
|
+
target.addEventListener(event, handler, options);
|
|
1040
|
+
return () => {
|
|
1041
|
+
target.removeEventListener(event, handler, options);
|
|
1042
|
+
};
|
|
1043
|
+
}
|
|
1044
|
+
function scrollIntoView(target) {
|
|
1045
|
+
const root = document.scrollingElement || document.documentElement;
|
|
1046
|
+
while (target && target !== root) {
|
|
1047
|
+
const scrollable = getScrollParent(target);
|
|
1048
|
+
if (scrollable !== document.documentElement && scrollable !== document.body && scrollable !== target) {
|
|
1049
|
+
const scrollableTop = scrollable.getBoundingClientRect().top;
|
|
1050
|
+
const targetTop = target.getBoundingClientRect().top;
|
|
1051
|
+
const targetBottom = target.getBoundingClientRect().bottom;
|
|
1052
|
+
const keyboardHeight = scrollable.getBoundingClientRect().bottom + KEYBOARD_BUFFER;
|
|
1053
|
+
if (targetBottom > keyboardHeight) {
|
|
1054
|
+
scrollable.scrollTop += targetTop - scrollableTop;
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
target = scrollable.parentElement;
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
function useSheetState({
|
|
1061
|
+
isOpen,
|
|
1062
|
+
onClosed: _onClosed,
|
|
1063
|
+
onOpening: _onOpening,
|
|
1064
|
+
onOpen: _onOpen,
|
|
1065
|
+
onClosing: _onClosing
|
|
1066
|
+
}) {
|
|
1067
|
+
const [state, setState] = React4.useState(isOpen ? "opening" : "closed");
|
|
1068
|
+
const onClosed = useStableCallback(() => _onClosed == null ? void 0 : _onClosed());
|
|
1069
|
+
const onOpening = useStableCallback(() => _onOpening == null ? void 0 : _onOpening());
|
|
1070
|
+
const onOpen = useStableCallback(() => _onOpen == null ? void 0 : _onOpen());
|
|
1071
|
+
const onClosing = useStableCallback(() => _onClosing == null ? void 0 : _onClosing());
|
|
1072
|
+
React4.useEffect(() => {
|
|
1073
|
+
if (isOpen && state === "closed") {
|
|
1074
|
+
setState("opening");
|
|
1075
|
+
} else if (!isOpen && (state === "open" || state === "opening")) {
|
|
1076
|
+
setState("closing");
|
|
1077
|
+
}
|
|
1078
|
+
}, [isOpen, state]);
|
|
1079
|
+
React4.useEffect(() => {
|
|
1080
|
+
async function handle() {
|
|
1081
|
+
switch (state) {
|
|
1082
|
+
case "closed":
|
|
1083
|
+
await (onClosed == null ? void 0 : onClosed());
|
|
1084
|
+
break;
|
|
1085
|
+
case "opening":
|
|
1086
|
+
await (onOpening == null ? void 0 : onOpening());
|
|
1087
|
+
setState("open");
|
|
1088
|
+
break;
|
|
1089
|
+
case "open":
|
|
1090
|
+
await (onOpen == null ? void 0 : onOpen());
|
|
1091
|
+
break;
|
|
1092
|
+
case "closing":
|
|
1093
|
+
await (onClosing == null ? void 0 : onClosing());
|
|
1094
|
+
setState("closed");
|
|
1095
|
+
break;
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
handle().catch((error) => {
|
|
1099
|
+
console.error("Internal sheet state error:", error);
|
|
1100
|
+
});
|
|
1101
|
+
}, [state]);
|
|
1102
|
+
return state;
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1105
|
+
// src/snap.ts
|
|
1106
|
+
function computeSnapPoints({
|
|
1107
|
+
snapPointsProp,
|
|
1108
|
+
sheetHeight
|
|
1109
|
+
}) {
|
|
1110
|
+
if (snapPointsProp[0] !== 0) {
|
|
1111
|
+
console.error(
|
|
1112
|
+
`First snap point should be 0 to ensure the sheet can be fully closed. Got: [${snapPointsProp.join(", ")}]`
|
|
1113
|
+
);
|
|
1114
|
+
snapPointsProp.unshift(0);
|
|
1115
|
+
}
|
|
1116
|
+
if (snapPointsProp[snapPointsProp.length - 1] !== 1) {
|
|
1117
|
+
console.error(
|
|
1118
|
+
`Last snap point should be 1 to ensure the sheet can be fully opened. Got: [${snapPointsProp.join(", ")}]`
|
|
1119
|
+
);
|
|
1120
|
+
snapPointsProp.push(1);
|
|
1121
|
+
}
|
|
1122
|
+
if (sheetHeight <= 0) {
|
|
1123
|
+
console.error(
|
|
1124
|
+
`Sheet height is ${sheetHeight}, cannot compute snap points. Make sure the sheet is mounted and has a valid height.`
|
|
1125
|
+
);
|
|
1126
|
+
return [];
|
|
1127
|
+
}
|
|
1128
|
+
const snapPointValues = snapPointsProp.map((point) => {
|
|
1129
|
+
if (point > 0 && point <= 1) {
|
|
1130
|
+
return Math.round(point * sheetHeight);
|
|
1131
|
+
}
|
|
1132
|
+
return point < 0 ? sheetHeight + point : point;
|
|
1133
|
+
});
|
|
1134
|
+
console.assert(
|
|
1135
|
+
isAscendingOrder(snapPointValues),
|
|
1136
|
+
`Snap points need to be in ascending order got: [${snapPointsProp.join(", ")}]`
|
|
1137
|
+
);
|
|
1138
|
+
snapPointValues.forEach((snap) => {
|
|
1139
|
+
if (snap < 0 || snap > sheetHeight) {
|
|
1140
|
+
console.warn(
|
|
1141
|
+
`Snap point ${snap} is outside of the sheet height ${sheetHeight}. This can cause unexpected behavior. Consider adjusting your snap points.`
|
|
1142
|
+
);
|
|
1143
|
+
}
|
|
1144
|
+
});
|
|
1145
|
+
if (!snapPointValues.includes(sheetHeight)) {
|
|
1146
|
+
console.warn(
|
|
1147
|
+
"Snap points do not include the sheet height.Please include `1` as the last snap point or it will be included automatically.This is to ensure the sheet can be fully opened."
|
|
1148
|
+
);
|
|
1149
|
+
snapPointValues.push(sheetHeight);
|
|
1150
|
+
}
|
|
1151
|
+
return snapPointValues.map((snap, index) => ({
|
|
1152
|
+
snapIndex: index,
|
|
1153
|
+
snapValue: snap,
|
|
1154
|
+
// Absolute value from the bottom of the sheet
|
|
1155
|
+
snapValueY: sheetHeight - snap
|
|
1156
|
+
// Y value is inverted as `y = 0` means sheet is at the top
|
|
1157
|
+
}));
|
|
1158
|
+
}
|
|
1159
|
+
function findClosestSnapPoint({
|
|
1160
|
+
snapPoints,
|
|
1161
|
+
currentY
|
|
1162
|
+
}) {
|
|
1163
|
+
return snapPoints.reduce(
|
|
1164
|
+
(closest, snap) => Math.abs(snap.snapValueY - currentY) < Math.abs(closest.snapValueY - currentY) ? snap : closest
|
|
1165
|
+
);
|
|
1166
|
+
}
|
|
1167
|
+
function findNextSnapPointInDirection({
|
|
1168
|
+
y,
|
|
1169
|
+
snapPoints,
|
|
1170
|
+
dragDirection
|
|
1171
|
+
}) {
|
|
1172
|
+
if (dragDirection === "down") {
|
|
1173
|
+
return snapPoints.slice().reverse().find((s) => s.snapValueY > y);
|
|
1174
|
+
} else {
|
|
1175
|
+
return snapPoints.find((s) => s.snapValueY < y);
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1178
|
+
function handleHighVelocityDrag({
|
|
1179
|
+
dragDirection,
|
|
1180
|
+
snapPoints
|
|
1181
|
+
}) {
|
|
1182
|
+
const bottomSnapPoint = snapPoints[0];
|
|
1183
|
+
const topSnapPoint = snapPoints[snapPoints.length - 1];
|
|
1184
|
+
if (dragDirection === "down") {
|
|
1185
|
+
return {
|
|
1186
|
+
yTo: bottomSnapPoint.snapValueY,
|
|
1187
|
+
snapIndex: bottomSnapPoint.snapIndex
|
|
1188
|
+
};
|
|
1189
|
+
}
|
|
1190
|
+
return {
|
|
1191
|
+
yTo: topSnapPoint.snapValueY,
|
|
1192
|
+
snapIndex: topSnapPoint.snapIndex
|
|
1193
|
+
};
|
|
1194
|
+
}
|
|
1195
|
+
function handleLowVelocityDrag({
|
|
1196
|
+
currentSnapPoint,
|
|
1197
|
+
currentY,
|
|
1198
|
+
dragDirection,
|
|
1199
|
+
snapPoints,
|
|
1200
|
+
velocity
|
|
1201
|
+
}) {
|
|
1202
|
+
const closestSnapRelativeToCurrentY = findClosestSnapPoint({
|
|
1203
|
+
snapPoints,
|
|
1204
|
+
currentY
|
|
1205
|
+
});
|
|
1206
|
+
if (Math.abs(velocity) < 20) {
|
|
1207
|
+
return {
|
|
1208
|
+
yTo: closestSnapRelativeToCurrentY.snapValueY,
|
|
1209
|
+
snapIndex: closestSnapRelativeToCurrentY.snapIndex
|
|
1210
|
+
};
|
|
1211
|
+
}
|
|
1212
|
+
const nextSnapInDirectionRelativeToCurrentY = findNextSnapPointInDirection({
|
|
1213
|
+
y: currentY,
|
|
1214
|
+
snapPoints,
|
|
1215
|
+
dragDirection
|
|
1216
|
+
});
|
|
1217
|
+
if (nextSnapInDirectionRelativeToCurrentY) {
|
|
1218
|
+
return {
|
|
1219
|
+
yTo: nextSnapInDirectionRelativeToCurrentY.snapValueY,
|
|
1220
|
+
snapIndex: nextSnapInDirectionRelativeToCurrentY.snapIndex
|
|
1221
|
+
};
|
|
1222
|
+
}
|
|
1223
|
+
return {
|
|
1224
|
+
yTo: currentSnapPoint.snapValueY,
|
|
1225
|
+
snapIndex: currentSnapPoint.snapIndex
|
|
1226
|
+
};
|
|
1227
|
+
}
|
|
1228
|
+
|
|
1229
|
+
// src/sheet.tsx
|
|
1230
|
+
var Sheet = React4.forwardRef(
|
|
1231
|
+
({
|
|
1232
|
+
avoidKeyboard = true,
|
|
1233
|
+
children,
|
|
1234
|
+
className = "",
|
|
1235
|
+
detent = "default",
|
|
1236
|
+
disableDismiss = false,
|
|
1237
|
+
disableDrag: disableDragProp = false,
|
|
1238
|
+
disableScrollLocking = false,
|
|
1239
|
+
dragCloseThreshold = DEFAULT_DRAG_CLOSE_THRESHOLD,
|
|
1240
|
+
dragVelocityThreshold = DEFAULT_DRAG_VELOCITY_THRESHOLD,
|
|
1241
|
+
initialSnap,
|
|
1242
|
+
isOpen,
|
|
1243
|
+
modalEffectRootId,
|
|
1244
|
+
modalEffectThreshold,
|
|
1245
|
+
mountPoint,
|
|
1246
|
+
prefersReducedMotion = false,
|
|
1247
|
+
snapPoints: snapPointsProp,
|
|
1248
|
+
style,
|
|
1249
|
+
tweenConfig = DEFAULT_TWEEN_CONFIG,
|
|
1250
|
+
unstyled = false,
|
|
1251
|
+
onOpenStart,
|
|
1252
|
+
onOpenEnd,
|
|
1253
|
+
onClose,
|
|
1254
|
+
onCloseStart,
|
|
1255
|
+
onCloseEnd,
|
|
1256
|
+
onSnap,
|
|
1257
|
+
onDrag: onDragProp,
|
|
1258
|
+
onDragStart: onDragStartProp,
|
|
1259
|
+
onDragEnd: onDragEndProp,
|
|
1260
|
+
...rest
|
|
1261
|
+
}, ref) => {
|
|
1262
|
+
const [sheetBoundsRef, sheetBounds] = useMeasure__default.default();
|
|
1263
|
+
const sheetRef = React4.useRef(null);
|
|
1264
|
+
const sheetHeight = Math.round(sheetBounds.height);
|
|
1265
|
+
const [currentSnap, setCurrentSnap] = React4.useState(initialSnap);
|
|
1266
|
+
const snapPoints = snapPointsProp && sheetHeight > 0 ? computeSnapPoints({ sheetHeight, snapPointsProp }) : [];
|
|
1267
|
+
const { windowHeight } = useDimensions();
|
|
1268
|
+
const closedY = sheetHeight > 0 ? sheetHeight : windowHeight;
|
|
1269
|
+
const y = react.useMotionValue(closedY);
|
|
1270
|
+
const yInverted = react.useTransform(y, (val) => Math.max(sheetHeight - val, 0));
|
|
1271
|
+
const indicatorRotation = react.useMotionValue(0);
|
|
1272
|
+
const shouldReduceMotion = react.useReducedMotion();
|
|
1273
|
+
const reduceMotion = Boolean(prefersReducedMotion || shouldReduceMotion);
|
|
1274
|
+
const animationOptions = {
|
|
1275
|
+
type: "tween",
|
|
1276
|
+
...reduceMotion ? REDUCED_MOTION_TWEEN_CONFIG : tweenConfig
|
|
1277
|
+
};
|
|
1278
|
+
const zIndex = react.useTransform(
|
|
1279
|
+
y,
|
|
1280
|
+
(val) => val + 2 >= closedY ? -1 : (style == null ? void 0 : style.zIndex) ?? 9999
|
|
1281
|
+
);
|
|
1282
|
+
const visibility = react.useTransform(
|
|
1283
|
+
y,
|
|
1284
|
+
(val) => val + 2 >= closedY ? "hidden" : "visible"
|
|
1285
|
+
);
|
|
1286
|
+
const updateSnap = useStableCallback((snapIndex) => {
|
|
1287
|
+
setCurrentSnap(snapIndex);
|
|
1288
|
+
onSnap == null ? void 0 : onSnap(snapIndex);
|
|
1289
|
+
});
|
|
1290
|
+
const getSnapPoint = useStableCallback((snapIndex) => {
|
|
1291
|
+
if (snapPointsProp && snapPoints) {
|
|
1292
|
+
if (snapIndex < 0 || snapIndex >= snapPoints.length) {
|
|
1293
|
+
console.warn(
|
|
1294
|
+
`Invalid snap index ${snapIndex}. Snap points are: [${snapPointsProp.join(", ")}] and their computed values are: [${snapPoints.map((point) => point.snapValue).join(", ")}]`
|
|
1295
|
+
);
|
|
1296
|
+
return null;
|
|
1297
|
+
}
|
|
1298
|
+
return snapPoints[snapIndex];
|
|
1299
|
+
}
|
|
1300
|
+
return null;
|
|
1301
|
+
});
|
|
1302
|
+
const snapTo = useStableCallback(async (snapIndex) => {
|
|
1303
|
+
if (!snapPointsProp) {
|
|
1304
|
+
console.warn("Snapping is not possible without `snapPoints` prop.");
|
|
1305
|
+
return;
|
|
1306
|
+
}
|
|
1307
|
+
const snapPoint = getSnapPoint(snapIndex);
|
|
1308
|
+
if (snapPoint === null) {
|
|
1309
|
+
console.warn(`Invalid snap index ${snapIndex}.`);
|
|
1310
|
+
return;
|
|
1311
|
+
}
|
|
1312
|
+
if (snapIndex === 0) {
|
|
1313
|
+
onClose();
|
|
1314
|
+
return;
|
|
1315
|
+
}
|
|
1316
|
+
await react.animate(y, snapPoint.snapValueY, {
|
|
1317
|
+
...animationOptions,
|
|
1318
|
+
onComplete: () => updateSnap(snapIndex)
|
|
1319
|
+
});
|
|
1320
|
+
});
|
|
1321
|
+
const keyboard = useKeyboardAvoidance({
|
|
1322
|
+
isEnabled: isOpen && avoidKeyboard,
|
|
1323
|
+
containerRef: sheetRef,
|
|
1324
|
+
onWillOpenKeyboard: async () => {
|
|
1325
|
+
const lastSnapPoint = snapPoints[snapPoints.length - 1];
|
|
1326
|
+
if (lastSnapPoint && lastSnapPoint.snapIndex !== currentSnap) {
|
|
1327
|
+
await react.animate(y, lastSnapPoint.snapValueY, animationOptions);
|
|
1328
|
+
updateSnap(lastSnapPoint.snapIndex);
|
|
1329
|
+
}
|
|
1330
|
+
},
|
|
1331
|
+
onDidOpenKeyboard: (focusedElement) => {
|
|
1332
|
+
const sheetElement = sheetRef.current;
|
|
1333
|
+
if (!sheetElement) return;
|
|
1334
|
+
const inputRect = focusedElement.getBoundingClientRect();
|
|
1335
|
+
const containerRect = sheetElement.getBoundingClientRect();
|
|
1336
|
+
const scroller = sheetElement.querySelector(
|
|
1337
|
+
".react-modal-sheet-content-scroller"
|
|
1338
|
+
);
|
|
1339
|
+
const scrollTarget = Math.max(
|
|
1340
|
+
inputRect.top - containerRect.top + scroller.scrollTop - inputRect.height,
|
|
1341
|
+
0
|
|
1342
|
+
);
|
|
1343
|
+
requestAnimationFrame(() => {
|
|
1344
|
+
scroller.scrollTo({ top: scrollTarget, behavior: "smooth" });
|
|
1345
|
+
});
|
|
1346
|
+
}
|
|
1347
|
+
});
|
|
1348
|
+
const disableDrag = keyboard.isKeyboardOpen || disableDragProp;
|
|
1349
|
+
const blurActiveInput = useStableCallback(() => {
|
|
1350
|
+
var _a;
|
|
1351
|
+
const focusedElement = document.activeElement;
|
|
1352
|
+
if (focusedElement && willOpenKeyboard(focusedElement) && ((_a = sheetRef.current) == null ? void 0 : _a.contains(focusedElement))) {
|
|
1353
|
+
focusedElement.blur();
|
|
1354
|
+
}
|
|
1355
|
+
});
|
|
1356
|
+
const onDragStart = useStableCallback((event, info) => {
|
|
1357
|
+
blurActiveInput();
|
|
1358
|
+
onDragStartProp == null ? void 0 : onDragStartProp(event, info);
|
|
1359
|
+
});
|
|
1360
|
+
const onDrag = useStableCallback((event, info) => {
|
|
1361
|
+
onDragProp == null ? void 0 : onDragProp(event, info);
|
|
1362
|
+
const currentY = y.get();
|
|
1363
|
+
const velocity = y.getVelocity();
|
|
1364
|
+
if (velocity > 0) indicatorRotation.set(10);
|
|
1365
|
+
if (velocity < 0) indicatorRotation.set(-10);
|
|
1366
|
+
y.set(Math.max(currentY + info.delta.y, 0));
|
|
1367
|
+
});
|
|
1368
|
+
const onDragEnd = useStableCallback((event, info) => {
|
|
1369
|
+
blurActiveInput();
|
|
1370
|
+
onDragEndProp == null ? void 0 : onDragEndProp(event, info);
|
|
1371
|
+
const currentY = y.get();
|
|
1372
|
+
let yTo = 0;
|
|
1373
|
+
const currentSnapPoint = currentSnap !== void 0 ? getSnapPoint(currentSnap) : null;
|
|
1374
|
+
if (currentSnapPoint) {
|
|
1375
|
+
const dragOffsetDirection = info.offset.y > 0 ? "down" : "up";
|
|
1376
|
+
const dragVelocityDirection = info.velocity.y > 0 ? "down" : "up";
|
|
1377
|
+
const isHighVelocity = Math.abs(info.velocity.y) > dragVelocityThreshold;
|
|
1378
|
+
let result;
|
|
1379
|
+
if (isHighVelocity) {
|
|
1380
|
+
result = handleHighVelocityDrag({
|
|
1381
|
+
snapPoints,
|
|
1382
|
+
dragDirection: dragVelocityDirection
|
|
1383
|
+
});
|
|
1384
|
+
} else {
|
|
1385
|
+
result = handleLowVelocityDrag({
|
|
1386
|
+
currentSnapPoint,
|
|
1387
|
+
currentY,
|
|
1388
|
+
dragDirection: dragOffsetDirection,
|
|
1389
|
+
snapPoints,
|
|
1390
|
+
velocity: info.velocity.y
|
|
1391
|
+
});
|
|
1392
|
+
}
|
|
1393
|
+
yTo = result.yTo;
|
|
1394
|
+
if (disableDismiss && yTo + 1 >= sheetHeight) {
|
|
1395
|
+
const bottomSnapPoint = snapPoints.find((s) => s.snapValue > 0);
|
|
1396
|
+
if (bottomSnapPoint) {
|
|
1397
|
+
yTo = bottomSnapPoint.snapValueY;
|
|
1398
|
+
updateSnap(bottomSnapPoint.snapIndex);
|
|
1399
|
+
} else {
|
|
1400
|
+
yTo = currentY;
|
|
1401
|
+
}
|
|
1402
|
+
} else if (result.snapIndex !== void 0) {
|
|
1403
|
+
updateSnap(result.snapIndex);
|
|
1404
|
+
}
|
|
1405
|
+
} else if (info.velocity.y > dragVelocityThreshold || currentY > sheetHeight * dragCloseThreshold) {
|
|
1406
|
+
if (disableDismiss) {
|
|
1407
|
+
yTo = 0;
|
|
1408
|
+
} else {
|
|
1409
|
+
yTo = closedY;
|
|
1410
|
+
}
|
|
1411
|
+
}
|
|
1412
|
+
react.animate(y, yTo, animationOptions);
|
|
1413
|
+
if (yTo + 1 >= sheetHeight && !disableDismiss) {
|
|
1414
|
+
onClose();
|
|
1415
|
+
}
|
|
1416
|
+
indicatorRotation.set(0);
|
|
1417
|
+
});
|
|
1418
|
+
React4.useImperativeHandle(ref, () => ({
|
|
1419
|
+
y,
|
|
1420
|
+
yInverted,
|
|
1421
|
+
height: sheetHeight,
|
|
1422
|
+
snapTo
|
|
1423
|
+
}));
|
|
1424
|
+
useModalEffect({
|
|
1425
|
+
y,
|
|
1426
|
+
detent,
|
|
1427
|
+
sheetHeight,
|
|
1428
|
+
snapPoints,
|
|
1429
|
+
rootId: modalEffectRootId,
|
|
1430
|
+
startThreshold: modalEffectThreshold
|
|
1431
|
+
});
|
|
1432
|
+
usePreventScroll({
|
|
1433
|
+
isDisabled: disableScrollLocking || !isOpen
|
|
1434
|
+
});
|
|
1435
|
+
const state = useSheetState({
|
|
1436
|
+
isOpen,
|
|
1437
|
+
onOpen: async () => {
|
|
1438
|
+
onOpenStart == null ? void 0 : onOpenStart();
|
|
1439
|
+
await waitForElement("react-modal-sheet-container");
|
|
1440
|
+
const initialSnapPoint = initialSnap !== void 0 ? getSnapPoint(initialSnap) : null;
|
|
1441
|
+
const yTo = (initialSnapPoint == null ? void 0 : initialSnapPoint.snapValueY) ?? 0;
|
|
1442
|
+
await react.animate(y, yTo, animationOptions);
|
|
1443
|
+
if (initialSnap !== void 0) {
|
|
1444
|
+
updateSnap(initialSnap);
|
|
1445
|
+
}
|
|
1446
|
+
onOpenEnd == null ? void 0 : onOpenEnd();
|
|
1447
|
+
},
|
|
1448
|
+
onClosing: async () => {
|
|
1449
|
+
onCloseStart == null ? void 0 : onCloseStart();
|
|
1450
|
+
await react.animate(y, closedY, animationOptions);
|
|
1451
|
+
onCloseEnd == null ? void 0 : onCloseEnd();
|
|
1452
|
+
}
|
|
1453
|
+
});
|
|
1454
|
+
const dragProps = {
|
|
1455
|
+
drag: "y",
|
|
1456
|
+
dragElastic: 0,
|
|
1457
|
+
dragMomentum: false,
|
|
1458
|
+
dragPropagation: false,
|
|
1459
|
+
onDrag,
|
|
1460
|
+
onDragStart,
|
|
1461
|
+
onDragEnd
|
|
1462
|
+
};
|
|
1463
|
+
const context = {
|
|
1464
|
+
currentSnap,
|
|
1465
|
+
detent,
|
|
1466
|
+
disableDrag,
|
|
1467
|
+
dragProps,
|
|
1468
|
+
indicatorRotation,
|
|
1469
|
+
avoidKeyboard,
|
|
1470
|
+
prefersReducedMotion,
|
|
1471
|
+
sheetBoundsRef,
|
|
1472
|
+
sheetRef,
|
|
1473
|
+
unstyled,
|
|
1474
|
+
y
|
|
1475
|
+
};
|
|
1476
|
+
const sheet = /* @__PURE__ */ React4__default.default.createElement(SheetContext.Provider, { value: context }, /* @__PURE__ */ React4__default.default.createElement(
|
|
1477
|
+
react.motion.div,
|
|
1478
|
+
{
|
|
1479
|
+
...rest,
|
|
1480
|
+
ref,
|
|
1481
|
+
"data-sheet-state": state,
|
|
1482
|
+
className: `react-modal-sheet-root ${className}`,
|
|
1483
|
+
style: {
|
|
1484
|
+
...applyStyles(styles.root, unstyled),
|
|
1485
|
+
zIndex,
|
|
1486
|
+
visibility,
|
|
1487
|
+
...style
|
|
1488
|
+
}
|
|
1489
|
+
},
|
|
1490
|
+
state !== "closed" ? children : null
|
|
1491
|
+
));
|
|
1492
|
+
if (IS_SSR) return sheet;
|
|
1493
|
+
return reactDom.createPortal(sheet, mountPoint ?? document.body);
|
|
1494
|
+
}
|
|
1495
|
+
);
|
|
1496
|
+
Sheet.displayName = "Sheet";
|
|
1497
|
+
|
|
1498
|
+
// src/index.tsx
|
|
1499
|
+
var Sheet2 = Object.assign(Sheet, {
|
|
1500
|
+
Container: SheetContainer,
|
|
1501
|
+
Header: SheetHeader,
|
|
1502
|
+
DragIndicator: SheetDragIndicator,
|
|
1503
|
+
Content: SheetContent,
|
|
1504
|
+
Backdrop: SheetBackdrop
|
|
1505
|
+
});
|
|
1506
|
+
|
|
1507
|
+
exports.Sheet = Sheet2;
|
|
1508
|
+
exports.useScrollPosition = useScrollPosition;
|
|
1509
|
+
exports.useVirtualKeyboard = useVirtualKeyboard;
|
|
1510
|
+
//# sourceMappingURL=index.js.map
|
|
1511
|
+
//# sourceMappingURL=index.js.map
|