@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/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