react-modal-sheet 2.2.1 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs ADDED
@@ -0,0 +1,824 @@
1
+ import React2, { createContext, forwardRef, useRef, useEffect, useImperativeHandle, useMemo, Children, cloneElement, useLayoutEffect, useCallback, useState, useContext } from 'react';
2
+ import { createPortal } from 'react-dom';
3
+ import { useMotionValue, useReducedMotion, useTransform, animate, motion, AnimatePresence } from 'framer-motion';
4
+ import { useLayoutEffect as useLayoutEffect$1, isIOS, chain, getScrollParent } from '@react-aria/utils';
5
+
6
+ // src/sheet.tsx
7
+
8
+ // src/constants.ts
9
+ var MAX_HEIGHT = "calc(100% - env(safe-area-inset-top) - 34px)";
10
+ var IS_SSR = typeof window === "undefined";
11
+ var DEFAULT_TWEEN_CONFIG = {
12
+ ease: "easeOut",
13
+ duration: 0.2
14
+ };
15
+ var REDUCED_MOTION_TWEEN_CONFIG = {
16
+ ease: "linear",
17
+ duration: 0.01
18
+ };
19
+ var DRAG_CLOSE_THRESHOLD = 0.6;
20
+ var DRAG_VELOCITY_THRESHOLD = 500;
21
+
22
+ // src/utils.ts
23
+ function getClosest(nums, goal) {
24
+ let closest = nums[0];
25
+ let minDifference = Math.abs(nums[0] - goal);
26
+ for (let i = 1; i < nums.length; i++) {
27
+ const difference = Math.abs(nums[i] - goal);
28
+ if (difference < minDifference) {
29
+ closest = nums[i];
30
+ minDifference = difference;
31
+ }
32
+ }
33
+ return closest;
34
+ }
35
+ function applyRootStyles(rootId) {
36
+ const body = document.querySelector("body");
37
+ const root = document.querySelector(`#${rootId}`);
38
+ if (root) {
39
+ const p = 24;
40
+ const h = window.innerHeight;
41
+ const s = (h - p) / h;
42
+ body.style.backgroundColor = "#000";
43
+ root.style.overflow = "hidden";
44
+ root.style.willChange = "transform";
45
+ root.style.transition = "transform 200ms ease-in-out, border-radius 200ms linear";
46
+ root.style.transform = `translateY(calc(env(safe-area-inset-top) + ${p / 2}px)) scale(${s})`;
47
+ root.style.borderTopRightRadius = "10px";
48
+ root.style.borderTopLeftRadius = "10px";
49
+ }
50
+ }
51
+ function cleanupRootStyles(rootId) {
52
+ const body = document.querySelector("body");
53
+ const root = document.getElementById(rootId);
54
+ function onTransitionEnd() {
55
+ root.style.removeProperty("overflow");
56
+ root.style.removeProperty("will-change");
57
+ root.style.removeProperty("transition");
58
+ body.style.removeProperty("background-color");
59
+ root.removeEventListener("transitionend", onTransitionEnd);
60
+ }
61
+ if (root) {
62
+ root.style.removeProperty("border-top-right-radius");
63
+ root.style.removeProperty("border-top-left-radius");
64
+ root.style.removeProperty("transform");
65
+ root.addEventListener("transitionend", onTransitionEnd);
66
+ }
67
+ }
68
+ function inDescendingOrder(arr) {
69
+ for (let i = 0; i < arr.length; i++) {
70
+ if (arr[i + 1] > arr[i])
71
+ return false;
72
+ }
73
+ return true;
74
+ }
75
+ function validateSnapTo({
76
+ snapTo,
77
+ sheetHeight
78
+ }) {
79
+ if (snapTo < 0) {
80
+ console.warn(
81
+ `Snap point is out of bounds. Sheet height is ${sheetHeight} but snap point is ${sheetHeight + Math.abs(snapTo)}.`
82
+ );
83
+ }
84
+ return Math.max(Math.round(snapTo), 0);
85
+ }
86
+ function mergeRefs(refs) {
87
+ return (value) => {
88
+ refs.forEach((ref) => {
89
+ if (typeof ref === "function") {
90
+ ref(value);
91
+ } else if (ref) {
92
+ ref.current = value;
93
+ }
94
+ });
95
+ };
96
+ }
97
+ function isTouchDevice() {
98
+ if (typeof window === "undefined")
99
+ return false;
100
+ return "ontouchstart" in window || navigator.maxTouchPoints > 0;
101
+ }
102
+
103
+ // src/hooks.tsx
104
+ var useIsomorphicLayoutEffect = IS_SSR ? useEffect : useLayoutEffect;
105
+ function useModalEffect(isOpen, rootId) {
106
+ const prevOpen = usePrevious(isOpen);
107
+ useEffect(() => {
108
+ if (rootId && !prevOpen && isOpen) {
109
+ applyRootStyles(rootId);
110
+ } else if (rootId && !isOpen && prevOpen) {
111
+ cleanupRootStyles(rootId);
112
+ }
113
+ }, [isOpen, prevOpen]);
114
+ useEffect(() => {
115
+ return () => {
116
+ if (rootId && isOpen)
117
+ cleanupRootStyles(rootId);
118
+ };
119
+ }, [isOpen]);
120
+ }
121
+ function useEventCallbacks(isOpen, callbacks) {
122
+ const prevOpen = usePrevious(isOpen);
123
+ const didOpen = useRef(false);
124
+ const handleAnimationComplete = useCallback(() => {
125
+ var _a, _b, _c, _d;
126
+ if (!didOpen.current) {
127
+ (_b = (_a = callbacks.current).onOpenEnd) == null ? void 0 : _b.call(_a);
128
+ didOpen.current = true;
129
+ } else {
130
+ (_d = (_c = callbacks.current).onCloseEnd) == null ? void 0 : _d.call(_c);
131
+ didOpen.current = false;
132
+ }
133
+ }, [isOpen, prevOpen]);
134
+ useEffect(() => {
135
+ var _a, _b, _c, _d;
136
+ if (!prevOpen && isOpen) {
137
+ (_b = (_a = callbacks.current).onOpenStart) == null ? void 0 : _b.call(_a);
138
+ } else if (!isOpen && prevOpen) {
139
+ (_d = (_c = callbacks.current).onCloseStart) == null ? void 0 : _d.call(_c);
140
+ }
141
+ }, [isOpen, prevOpen]);
142
+ return { handleAnimationComplete };
143
+ }
144
+ function useDimensions() {
145
+ const [dimensions, setDimensions] = useState({ height: 0, width: 0 });
146
+ useIsomorphicLayoutEffect(() => {
147
+ const updateHeight = () => {
148
+ setDimensions({ height: window.innerHeight, width: window.innerWidth });
149
+ };
150
+ window.addEventListener("resize", updateHeight);
151
+ updateHeight();
152
+ return () => {
153
+ window.removeEventListener("resize", updateHeight);
154
+ };
155
+ }, []);
156
+ return dimensions;
157
+ }
158
+ function usePrevious(state) {
159
+ const ref = useRef();
160
+ useEffect(() => {
161
+ ref.current = state;
162
+ });
163
+ return ref.current;
164
+ }
165
+ function useEvent(handler) {
166
+ const handlerRef = useRef();
167
+ useIsomorphicLayoutEffect(() => {
168
+ handlerRef.current = handler;
169
+ });
170
+ return useCallback((...args) => {
171
+ const fn = handlerRef.current;
172
+ return fn == null ? void 0 : fn(...args);
173
+ }, []);
174
+ }
175
+ var constraints = { bottom: 0, top: 0, left: 0, right: 0 };
176
+ function useDragConstraints() {
177
+ const constraintsRef = useRef(null);
178
+ const onMeasureDragConstraints = useCallback(() => constraints, []);
179
+ return { constraintsRef, onMeasureDragConstraints };
180
+ }
181
+ var SheetContext = createContext(
182
+ void 0
183
+ );
184
+ var useSheetContext = () => {
185
+ const context = useContext(SheetContext);
186
+ if (!context)
187
+ throw Error("Sheet context error");
188
+ return context;
189
+ };
190
+ var SheetScrollerContext = createContext(void 0);
191
+ function SheetScrollerContextProvider({
192
+ children
193
+ }) {
194
+ const sheetContext = useSheetContext();
195
+ const [disableDrag, setDisableDrag] = useState(!!sheetContext.disableDrag);
196
+ function setDragEnabled() {
197
+ if (!sheetContext.disableDrag)
198
+ setDisableDrag(false);
199
+ }
200
+ function setDragDisabled() {
201
+ if (!disableDrag)
202
+ setDisableDrag(true);
203
+ }
204
+ return /* @__PURE__ */ React2.createElement(
205
+ SheetScrollerContext.Provider,
206
+ {
207
+ value: { disableDrag, setDragEnabled, setDragDisabled }
208
+ },
209
+ children
210
+ );
211
+ }
212
+ var useSheetScrollerContext = () => {
213
+ const context = useContext(SheetScrollerContext);
214
+ if (!context)
215
+ throw Error("Sheet scroller context error");
216
+ return context;
217
+ };
218
+ var visualViewport = typeof window !== "undefined" ? window.visualViewport : void 0;
219
+ var nonTextInputTypes = /* @__PURE__ */ new Set([
220
+ "checkbox",
221
+ "radio",
222
+ "range",
223
+ "color",
224
+ "file",
225
+ "image",
226
+ "button",
227
+ "submit",
228
+ "reset"
229
+ ]);
230
+ var preventScrollCount = 0;
231
+ var restore;
232
+ function usePreventScroll(options = {}) {
233
+ const { isDisabled } = options;
234
+ useLayoutEffect$1(() => {
235
+ if (isDisabled) {
236
+ return;
237
+ }
238
+ preventScrollCount++;
239
+ if (preventScrollCount === 1) {
240
+ if (isIOS()) {
241
+ restore = preventScrollMobileSafari();
242
+ } else {
243
+ restore = preventScrollStandard();
244
+ }
245
+ }
246
+ return () => {
247
+ preventScrollCount--;
248
+ if (preventScrollCount === 0) {
249
+ restore();
250
+ }
251
+ };
252
+ }, [isDisabled]);
253
+ }
254
+ function preventScrollStandard() {
255
+ return chain(
256
+ setStyle(
257
+ document.documentElement,
258
+ "paddingRight",
259
+ `${window.innerWidth - document.documentElement.clientWidth}px`
260
+ ),
261
+ setStyle(document.documentElement, "overflow", "hidden")
262
+ );
263
+ }
264
+ function preventScrollMobileSafari() {
265
+ let scrollable;
266
+ let lastY = 0;
267
+ const onTouchStart = (e) => {
268
+ scrollable = getScrollParent(e.target);
269
+ if (scrollable === document.documentElement && scrollable === document.body) {
270
+ return;
271
+ }
272
+ lastY = e.changedTouches[0].pageY;
273
+ };
274
+ const onTouchMove = (e) => {
275
+ if (scrollable === void 0) {
276
+ return;
277
+ }
278
+ if (scrollable === document.documentElement || scrollable === document.body) {
279
+ e.preventDefault();
280
+ return;
281
+ }
282
+ const y = e.changedTouches[0].pageY;
283
+ const scrollTop = scrollable.scrollTop;
284
+ const bottom = scrollable.scrollHeight - scrollable.clientHeight;
285
+ if (bottom === 0) {
286
+ return;
287
+ }
288
+ if (scrollTop <= 0 && y > lastY || scrollTop >= bottom && y < lastY) {
289
+ e.preventDefault();
290
+ }
291
+ lastY = y;
292
+ };
293
+ const onTouchEnd = (e) => {
294
+ const target = e.target;
295
+ if (willOpenKeyboard(target) && target !== document.activeElement) {
296
+ e.preventDefault();
297
+ target.style.transform = "translateY(-2000px)";
298
+ target.focus();
299
+ requestAnimationFrame(() => {
300
+ target.style.transform = "";
301
+ });
302
+ }
303
+ };
304
+ const onFocus = (e) => {
305
+ const target = e.target;
306
+ if (willOpenKeyboard(target)) {
307
+ target.style.transform = "translateY(-2000px)";
308
+ requestAnimationFrame(() => {
309
+ target.style.transform = "";
310
+ if (visualViewport) {
311
+ if (visualViewport.height < window.innerHeight) {
312
+ requestAnimationFrame(() => {
313
+ scrollIntoView(target);
314
+ });
315
+ } else {
316
+ visualViewport.addEventListener(
317
+ "resize",
318
+ () => {
319
+ scrollIntoView(target);
320
+ },
321
+ { once: true }
322
+ );
323
+ }
324
+ }
325
+ });
326
+ }
327
+ };
328
+ const onWindowScroll = () => {
329
+ window.scrollTo(0, 0);
330
+ };
331
+ const scrollX = window.pageXOffset;
332
+ const scrollY = window.pageYOffset;
333
+ const restoreStyles = chain(
334
+ setStyle(
335
+ document.documentElement,
336
+ "paddingRight",
337
+ `${window.innerWidth - document.documentElement.clientWidth}px`
338
+ ),
339
+ setStyle(document.documentElement, "overflow", "hidden"),
340
+ setStyle(document.body, "marginTop", `-${scrollY}px`)
341
+ );
342
+ window.scrollTo(0, 0);
343
+ const removeEvents = chain(
344
+ addEvent(document, "touchstart", onTouchStart, {
345
+ passive: false,
346
+ capture: true
347
+ }),
348
+ addEvent(document, "touchmove", onTouchMove, {
349
+ passive: false,
350
+ capture: true
351
+ }),
352
+ addEvent(document, "touchend", onTouchEnd, {
353
+ passive: false,
354
+ capture: true
355
+ }),
356
+ addEvent(document, "focus", onFocus, true),
357
+ addEvent(window, "scroll", onWindowScroll)
358
+ );
359
+ return () => {
360
+ restoreStyles();
361
+ removeEvents();
362
+ window.scrollTo(scrollX, scrollY);
363
+ };
364
+ }
365
+ function setStyle(element, style, value) {
366
+ const cur = element.style[style];
367
+ element.style[style] = value;
368
+ return () => {
369
+ element.style[style] = cur;
370
+ };
371
+ }
372
+ function addEvent(target, event, handler, options) {
373
+ target.addEventListener(event, handler, options);
374
+ return () => {
375
+ target.removeEventListener(event, handler, options);
376
+ };
377
+ }
378
+ function scrollIntoView(target) {
379
+ const root = document.scrollingElement || document.documentElement;
380
+ while (target && target !== root) {
381
+ const scrollable = getScrollParent(target);
382
+ if (scrollable !== document.documentElement && scrollable !== document.body && scrollable !== target) {
383
+ const scrollableTop = scrollable.getBoundingClientRect().top;
384
+ const targetTop = target.getBoundingClientRect().top;
385
+ if (targetTop > scrollableTop + target.clientHeight) {
386
+ scrollable.scrollTop += targetTop - scrollableTop;
387
+ }
388
+ }
389
+ target = scrollable.parentElement;
390
+ }
391
+ }
392
+ function willOpenKeyboard(target) {
393
+ return target instanceof HTMLInputElement && !nonTextInputTypes.has(target.type) || target instanceof HTMLTextAreaElement || target instanceof HTMLElement && target.isContentEditable;
394
+ }
395
+
396
+ // src/styles.ts
397
+ var styles = {
398
+ wrapper: {
399
+ position: "fixed",
400
+ top: 0,
401
+ bottom: 0,
402
+ left: 0,
403
+ right: 0,
404
+ overflow: "hidden",
405
+ pointerEvents: "none"
406
+ },
407
+ backdrop: {
408
+ zIndex: 1,
409
+ position: "fixed",
410
+ top: 0,
411
+ left: 0,
412
+ width: "100%",
413
+ height: "100%",
414
+ backgroundColor: "rgba(0, 0, 0, 0.2)",
415
+ touchAction: "none",
416
+ // Disable iOS body scrolling
417
+ border: "none"
418
+ },
419
+ container: {
420
+ zIndex: 2,
421
+ position: "absolute",
422
+ left: 0,
423
+ bottom: 0,
424
+ width: "100%",
425
+ backgroundColor: "#fff",
426
+ borderTopRightRadius: "8px",
427
+ borderTopLeftRadius: "8px",
428
+ boxShadow: "0px -2px 16px rgba(0, 0, 0, 0.3)",
429
+ display: "flex",
430
+ flexDirection: "column",
431
+ pointerEvents: "auto"
432
+ },
433
+ headerWrapper: {
434
+ width: "100%"
435
+ },
436
+ header: {
437
+ height: "40px",
438
+ width: "100%",
439
+ position: "relative",
440
+ display: "flex",
441
+ alignItems: "center",
442
+ justifyContent: "center"
443
+ },
444
+ indicator: {
445
+ width: "18px",
446
+ height: "4px",
447
+ borderRadius: "99px",
448
+ backgroundColor: "#ddd"
449
+ },
450
+ content: {
451
+ flexGrow: 1,
452
+ display: "flex",
453
+ flexDirection: "column",
454
+ minHeight: "0px",
455
+ position: "relative"
456
+ },
457
+ scroller: {
458
+ height: "100%",
459
+ overflowY: "auto"
460
+ }
461
+ };
462
+ var styles_default = styles;
463
+
464
+ // src/sheet.tsx
465
+ var Sheet = forwardRef(
466
+ ({
467
+ onOpenStart,
468
+ onOpenEnd,
469
+ onClose,
470
+ onCloseStart,
471
+ onCloseEnd,
472
+ onSnap,
473
+ children,
474
+ disableScrollLocking = false,
475
+ isOpen,
476
+ snapPoints,
477
+ rootId,
478
+ mountPoint,
479
+ style,
480
+ detent = "full-height",
481
+ initialSnap = 0,
482
+ disableDrag = false,
483
+ prefersReducedMotion = false,
484
+ tweenConfig = DEFAULT_TWEEN_CONFIG,
485
+ ...rest
486
+ }, ref) => {
487
+ const sheetRef = useRef(null);
488
+ const constraintsRef = useRef(null);
489
+ const indicatorRotation = useMotionValue(0);
490
+ const { height: windowHeight } = useDimensions();
491
+ const shouldReduceMotion = useReducedMotion();
492
+ const reduceMotion = Boolean(prefersReducedMotion || shouldReduceMotion);
493
+ const animationOptions = {
494
+ type: "tween",
495
+ ...reduceMotion ? REDUCED_MOTION_TWEEN_CONFIG : tweenConfig
496
+ };
497
+ const y = useMotionValue(0);
498
+ const zIndex = useTransform(
499
+ y,
500
+ (value) => value >= windowHeight ? -1 : 9999999
501
+ );
502
+ const visibility = useTransform(
503
+ y,
504
+ (value) => value >= windowHeight ? "hidden" : "visible"
505
+ );
506
+ const callbacks = useRef({
507
+ onOpenStart,
508
+ onOpenEnd,
509
+ onCloseStart,
510
+ onCloseEnd
511
+ });
512
+ useIsomorphicLayoutEffect(() => {
513
+ callbacks.current = {
514
+ onOpenStart,
515
+ onOpenEnd,
516
+ onCloseStart,
517
+ onCloseEnd
518
+ };
519
+ });
520
+ if (snapPoints) {
521
+ snapPoints = snapPoints.map((point) => {
522
+ if (point > 0 && point <= 1)
523
+ return Math.round(point * windowHeight);
524
+ return point < 0 ? windowHeight + point : point;
525
+ });
526
+ console.assert(
527
+ inDescendingOrder(snapPoints) || windowHeight === 0,
528
+ `Snap points need to be in descending order got: [${snapPoints.join(
529
+ ", "
530
+ )}]`
531
+ );
532
+ }
533
+ const onDrag = useEvent((_, { delta }) => {
534
+ const velocity = y.getVelocity();
535
+ if (velocity > 0)
536
+ indicatorRotation.set(10);
537
+ if (velocity < 0)
538
+ indicatorRotation.set(-10);
539
+ y.set(Math.max(y.get() + delta.y, 0));
540
+ });
541
+ const onDragEnd = useEvent((_, { velocity }) => {
542
+ if (velocity.y > DRAG_VELOCITY_THRESHOLD) {
543
+ onClose();
544
+ } else {
545
+ const sheetEl = sheetRef.current;
546
+ const sheetHeight = sheetEl.getBoundingClientRect().height;
547
+ const currentY = y.get();
548
+ let snapTo = 0;
549
+ if (snapPoints) {
550
+ const snapToValues = snapPoints.map(
551
+ (p) => sheetHeight - Math.min(p, sheetHeight)
552
+ );
553
+ if (detent === "content-height" && !snapToValues.includes(0)) {
554
+ snapToValues.unshift(0);
555
+ }
556
+ snapTo = getClosest(snapToValues, currentY);
557
+ } else if (currentY / sheetHeight > DRAG_CLOSE_THRESHOLD) {
558
+ snapTo = sheetHeight;
559
+ }
560
+ snapTo = validateSnapTo({ snapTo, sheetHeight });
561
+ void animate(y, snapTo, animationOptions);
562
+ if (snapPoints && onSnap) {
563
+ const snapValue = Math.abs(Math.round(snapPoints[0] - snapTo));
564
+ const snapIndex = snapPoints.indexOf(getClosest(snapPoints, snapValue));
565
+ onSnap(snapIndex);
566
+ }
567
+ const roundedSheetHeight = Math.round(sheetHeight);
568
+ const shouldClose = snapTo >= roundedSheetHeight;
569
+ if (shouldClose)
570
+ onClose();
571
+ }
572
+ indicatorRotation.set(0);
573
+ });
574
+ useEffect(() => {
575
+ if (!snapPoints || !onSnap)
576
+ return;
577
+ const snapIndex = isOpen ? initialSnap : snapPoints.length - 1;
578
+ onSnap(snapIndex);
579
+ }, [isOpen]);
580
+ useImperativeHandle(ref, () => ({
581
+ y,
582
+ snapTo: (snapIndex) => {
583
+ const sheetEl = sheetRef.current;
584
+ if ((snapPoints == null ? void 0 : snapPoints[snapIndex]) !== void 0 && sheetEl !== null) {
585
+ const sheetHeight = sheetEl.getBoundingClientRect().height;
586
+ const snapPoint = snapPoints[snapIndex];
587
+ const snapTo = validateSnapTo({
588
+ snapTo: sheetHeight - snapPoint,
589
+ sheetHeight
590
+ });
591
+ void animate(y, snapTo, animationOptions);
592
+ if (onSnap)
593
+ onSnap(snapIndex);
594
+ if (snapTo >= sheetHeight)
595
+ onClose();
596
+ }
597
+ }
598
+ }));
599
+ useModalEffect(isOpen, rootId);
600
+ usePreventScroll({ isDisabled: disableScrollLocking || !isOpen });
601
+ const dragProps = useMemo(() => {
602
+ const dragProps2 = {
603
+ drag: "y",
604
+ dragElastic: 0,
605
+ dragMomentum: false,
606
+ dragPropagation: false,
607
+ onDrag,
608
+ onDragEnd
609
+ };
610
+ return disableDrag ? void 0 : dragProps2;
611
+ }, [disableDrag, windowHeight]);
612
+ const context = {
613
+ y,
614
+ sheetRef,
615
+ isOpen,
616
+ initialSnap,
617
+ snapPoints,
618
+ detent,
619
+ indicatorRotation,
620
+ callbacks,
621
+ dragProps,
622
+ windowHeight,
623
+ animationOptions,
624
+ reduceMotion,
625
+ disableDrag
626
+ };
627
+ const sheet = /* @__PURE__ */ React2.createElement(SheetContext.Provider, { value: context }, /* @__PURE__ */ React2.createElement(
628
+ motion.div,
629
+ {
630
+ ...rest,
631
+ ref: mergeRefs([ref, constraintsRef]),
632
+ style: { ...styles_default.wrapper, zIndex, visibility, ...style }
633
+ },
634
+ /* @__PURE__ */ React2.createElement(AnimatePresence, null, isOpen ? /* @__PURE__ */ React2.createElement(SheetScrollerContextProvider, null, Children.map(
635
+ children,
636
+ (child, i) => cloneElement(child, { key: `sheet-child-${i}` })
637
+ )) : null)
638
+ ));
639
+ if (IS_SSR)
640
+ return sheet;
641
+ return createPortal(sheet, mountPoint ?? document.body);
642
+ }
643
+ );
644
+ Sheet.displayName = "Sheet";
645
+ var sheet_default = Sheet;
646
+ var SheetContainer = forwardRef(
647
+ ({ children, style = {}, className = "", ...rest }, ref) => {
648
+ const {
649
+ y,
650
+ isOpen,
651
+ callbacks,
652
+ snapPoints,
653
+ initialSnap = 0,
654
+ sheetRef,
655
+ windowHeight,
656
+ detent,
657
+ animationOptions,
658
+ reduceMotion
659
+ } = useSheetContext();
660
+ const { handleAnimationComplete } = useEventCallbacks(isOpen, callbacks);
661
+ const initialY = snapPoints ? snapPoints[0] - snapPoints[initialSnap] : 0;
662
+ const maxSnapHeight = snapPoints ? snapPoints[0] : null;
663
+ const height = maxSnapHeight !== null ? `min(${maxSnapHeight}px, ${MAX_HEIGHT})` : MAX_HEIGHT;
664
+ return /* @__PURE__ */ React2.createElement(
665
+ motion.div,
666
+ {
667
+ ...rest,
668
+ ref: mergeRefs([sheetRef, ref]),
669
+ className: `react-modal-sheet-container ${className}`,
670
+ style: {
671
+ ...styles_default.container,
672
+ ...style,
673
+ ...detent === "full-height" && { height },
674
+ ...detent === "content-height" && { maxHeight: height },
675
+ y
676
+ },
677
+ initial: reduceMotion ? false : { y: windowHeight },
678
+ animate: { y: initialY, transition: animationOptions },
679
+ exit: { y: windowHeight, transition: animationOptions },
680
+ onAnimationComplete: handleAnimationComplete
681
+ },
682
+ children
683
+ );
684
+ }
685
+ );
686
+ SheetContainer.displayName = "SheetContainer";
687
+ var SheetContainer_default = SheetContainer;
688
+ var SheetContent = forwardRef(
689
+ ({ children, style, disableDrag, className = "", ...rest }, ref) => {
690
+ const sheetContext = useSheetContext();
691
+ const sheetScrollerContext = useSheetScrollerContext();
692
+ const { constraintsRef, onMeasureDragConstraints } = useDragConstraints();
693
+ const dragProps = disableDrag || sheetScrollerContext.disableDrag ? void 0 : sheetContext.dragProps;
694
+ return /* @__PURE__ */ React2.createElement(
695
+ motion.div,
696
+ {
697
+ ...rest,
698
+ ref: mergeRefs([ref, constraintsRef]),
699
+ className: `react-modal-sheet-content ${className}`,
700
+ style: { ...styles_default.content, ...style },
701
+ ...dragProps,
702
+ dragConstraints: constraintsRef,
703
+ onMeasureDragConstraints
704
+ },
705
+ children
706
+ );
707
+ }
708
+ );
709
+ SheetContent.displayName = "SheetContent";
710
+ var SheetContent_default = SheetContent;
711
+ var SheetHeader = forwardRef(
712
+ ({ children, style, disableDrag, ...rest }, ref) => {
713
+ const { indicatorRotation, dragProps } = useSheetContext();
714
+ const { constraintsRef, onMeasureDragConstraints } = useDragConstraints();
715
+ const _dragProps = disableDrag ? void 0 : dragProps;
716
+ const indicator1Transform = useTransform(
717
+ indicatorRotation,
718
+ (r) => `translateX(2px) rotate(${r}deg)`
719
+ );
720
+ const indicator2Transform = useTransform(
721
+ indicatorRotation,
722
+ (r) => `translateX(-2px) rotate(${-1 * r}deg)`
723
+ );
724
+ return /* @__PURE__ */ React2.createElement(
725
+ motion.div,
726
+ {
727
+ ...rest,
728
+ ref: mergeRefs([ref, constraintsRef]),
729
+ style: { ...styles_default.headerWrapper, ...style },
730
+ ..._dragProps,
731
+ dragConstraints: constraintsRef,
732
+ onMeasureDragConstraints
733
+ },
734
+ children || /* @__PURE__ */ React2.createElement("div", { className: "react-modal-sheet-header", style: styles_default.header }, /* @__PURE__ */ React2.createElement(
735
+ motion.span,
736
+ {
737
+ className: "react-modal-sheet-drag-indicator",
738
+ style: { ...styles_default.indicator, transform: indicator1Transform }
739
+ }
740
+ ), /* @__PURE__ */ React2.createElement(
741
+ motion.span,
742
+ {
743
+ className: "react-modal-sheet-drag-indicator",
744
+ style: { ...styles_default.indicator, transform: indicator2Transform }
745
+ }
746
+ ))
747
+ );
748
+ }
749
+ );
750
+ SheetHeader.displayName = "SheetHeader";
751
+ var SheetHeader_default = SheetHeader;
752
+ var isClickable = (props) => !!props.onClick || !!props.onTap;
753
+ var SheetBackdrop = forwardRef(
754
+ ({ style = {}, className = "", ...rest }, ref) => {
755
+ const Comp = isClickable(rest) ? motion.button : motion.div;
756
+ const pointerEvents = isClickable(rest) ? "auto" : "none";
757
+ return /* @__PURE__ */ React2.createElement(
758
+ Comp,
759
+ {
760
+ ...rest,
761
+ ref,
762
+ className: `react-modal-sheet-backdrop ${className}`,
763
+ style: { ...styles_default.backdrop, ...style, pointerEvents },
764
+ initial: { opacity: 0 },
765
+ animate: { opacity: 1 },
766
+ exit: { opacity: 0 }
767
+ }
768
+ );
769
+ }
770
+ );
771
+ SheetBackdrop.displayName = "SheetBackdrop";
772
+ var SheetBackdrop_default = SheetBackdrop;
773
+ var SheetScroller = forwardRef(
774
+ ({ draggableAt = "top", children, style, className = "", ...rest }, ref) => {
775
+ const sheetScrollerContext = useSheetScrollerContext();
776
+ function determineDragState(element) {
777
+ const { scrollTop, scrollHeight, clientHeight } = element;
778
+ const isScrollable = scrollHeight > clientHeight;
779
+ if (!isScrollable)
780
+ return;
781
+ const isAtTop = scrollTop <= 0;
782
+ const isAtBottom = scrollHeight - scrollTop === clientHeight;
783
+ const shouldEnable = draggableAt === "top" && isAtTop || draggableAt === "bottom" && isAtBottom || draggableAt === "both" && (isAtTop || isAtBottom);
784
+ if (shouldEnable) {
785
+ sheetScrollerContext.setDragEnabled();
786
+ } else {
787
+ sheetScrollerContext.setDragDisabled();
788
+ }
789
+ }
790
+ function onScroll(e) {
791
+ determineDragState(e.currentTarget);
792
+ }
793
+ function onTouchStart(e) {
794
+ determineDragState(e.currentTarget);
795
+ }
796
+ const scrollProps = isTouchDevice() ? { onScroll, onTouchStart } : void 0;
797
+ return /* @__PURE__ */ React2.createElement(
798
+ "div",
799
+ {
800
+ ...rest,
801
+ ref,
802
+ className: `react-modal-sheet-scroller ${className}`,
803
+ style: { ...styles_default.scroller, ...style },
804
+ ...scrollProps
805
+ },
806
+ children
807
+ );
808
+ }
809
+ );
810
+ SheetScroller.displayName = "SheetScroller";
811
+ var SheetScroller_default = SheetScroller;
812
+
813
+ // src/index.tsx
814
+ var _SheetCompound = sheet_default;
815
+ _SheetCompound.Container = SheetContainer_default;
816
+ _SheetCompound.Header = SheetHeader_default;
817
+ _SheetCompound.Content = SheetContent_default;
818
+ _SheetCompound.Backdrop = SheetBackdrop_default;
819
+ _SheetCompound.Scroller = SheetScroller_default;
820
+ var Sheet2 = _SheetCompound;
821
+
822
+ export { Sheet2 as Sheet };
823
+ //# sourceMappingURL=out.js.map
824
+ //# sourceMappingURL=index.mjs.map