react-modal-sheet 5.5.0 → 5.6.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 CHANGED
@@ -1,122 +1,22 @@
1
- import { motion, useMotionValue, useTransform, useReducedMotion, animate } from 'motion/react';
2
- import React4, { createContext, forwardRef, useRef, useState, useImperativeHandle, useContext, useCallback, useMemo, useEffect, useLayoutEffect } from 'react';
1
+ import React, { createContext, forwardRef, useRef, useState, useMemo, useImperativeHandle, useEffect, useLayoutEffect, useCallback, useContext } from 'react';
2
+ import { useMotionValue, useTransform, useReducedMotion, motion, animate } from 'motion/react';
3
3
  import { createPortal } from 'react-dom';
4
4
  import useMeasure from 'react-use-measure';
5
5
  import { transform } from 'motion';
6
6
 
7
- // src/SheetBackdrop.tsx
8
- var SheetContext = createContext(
9
- void 0
10
- );
11
- function useSheetContext() {
12
- const context = useContext(SheetContext);
7
+ // src/context.tsx
8
+ var InternalSheetContext = createContext(void 0);
9
+ var ExposedSheetContext = createContext(void 0);
10
+ function useInternalContext() {
11
+ const context = useContext(InternalSheetContext);
12
+ if (!context) throw new Error("Sheet context error");
13
+ return context;
14
+ }
15
+ function useExposedContext() {
16
+ const context = useContext(ExposedSheetContext);
13
17
  if (!context) throw new Error("Sheet context error");
14
18
  return context;
15
19
  }
16
-
17
- // src/styles.ts
18
- var styles = {
19
- root: {
20
- base: {
21
- position: "fixed",
22
- top: 0,
23
- bottom: 0,
24
- left: 0,
25
- right: 0,
26
- overflow: "hidden",
27
- pointerEvents: "none"
28
- },
29
- decorative: {}
30
- },
31
- backdrop: {
32
- base: {
33
- zIndex: 1,
34
- position: "fixed",
35
- top: 0,
36
- left: 0,
37
- width: "100%",
38
- height: "100%",
39
- touchAction: "none",
40
- userSelect: "none"
41
- },
42
- decorative: {
43
- backgroundColor: "rgba(0, 0, 0, 0.2)",
44
- border: "none",
45
- WebkitTapHighlightColor: "transparent"
46
- }
47
- },
48
- container: {
49
- base: {
50
- zIndex: 2,
51
- position: "absolute",
52
- left: 0,
53
- bottom: 0,
54
- width: "100%",
55
- pointerEvents: "auto",
56
- display: "flex",
57
- flexDirection: "column"
58
- },
59
- decorative: {
60
- backgroundColor: "#fff",
61
- borderTopRightRadius: "8px",
62
- borderTopLeftRadius: "8px",
63
- boxShadow: "0px -2px 16px rgba(0, 0, 0, 0.3)"
64
- }
65
- },
66
- headerWrapper: {
67
- base: {
68
- width: "100%"
69
- },
70
- decorative: {}
71
- },
72
- header: {
73
- base: {
74
- width: "100%",
75
- position: "relative"
76
- },
77
- decorative: {
78
- height: "40px",
79
- display: "flex",
80
- alignItems: "center",
81
- justifyContent: "center"
82
- }
83
- },
84
- indicatorWrapper: {
85
- base: {
86
- display: "flex"
87
- },
88
- decorative: {}
89
- },
90
- indicator: {
91
- base: {
92
- display: "inline-block"
93
- },
94
- decorative: {
95
- width: "18px",
96
- height: "4px",
97
- borderRadius: "99px",
98
- backgroundColor: "#ddd"
99
- }
100
- },
101
- content: {
102
- base: {
103
- minHeight: "0px",
104
- position: "relative",
105
- flexGrow: 1,
106
- display: "flex",
107
- flexDirection: "column"
108
- },
109
- decorative: {}
110
- },
111
- scroller: {
112
- base: {
113
- height: "100%",
114
- overflowY: "auto",
115
- overscrollBehaviorY: "none"
116
- },
117
- decorative: {}
118
- }
119
- };
120
20
 
121
21
  // src/constants.ts
122
22
  var DEFAULT_HEIGHT = "calc(100% - env(safe-area-inset-top) - 34px)";
@@ -131,6 +31,29 @@ var REDUCED_MOTION_TWEEN_CONFIG = {
131
31
  };
132
32
  var DEFAULT_DRAG_CLOSE_THRESHOLD = 0.6;
133
33
  var DEFAULT_DRAG_VELOCITY_THRESHOLD = 1200;
34
+ var useIsomorphicLayoutEffect = IS_SSR ? useEffect : useLayoutEffect;
35
+
36
+ // src/hooks/use-dimensions.ts
37
+ function useDimensions() {
38
+ const [dimensions, setDimensions] = useState(() => ({
39
+ windowHeight: !IS_SSR ? window.innerHeight : 0,
40
+ windowWidth: !IS_SSR ? window.innerWidth : 0
41
+ }));
42
+ useIsomorphicLayoutEffect(() => {
43
+ function handler() {
44
+ setDimensions({
45
+ windowHeight: window.innerHeight,
46
+ windowWidth: window.innerWidth
47
+ });
48
+ }
49
+ handler();
50
+ window.addEventListener("resize", handler);
51
+ return () => {
52
+ window.removeEventListener("resize", handler);
53
+ };
54
+ }, []);
55
+ return dimensions;
56
+ }
134
57
 
135
58
  // src/utils.ts
136
59
  function applyStyles(styles2, unstyled) {
@@ -213,86 +136,9 @@ function willOpenKeyboard(target) {
213
136
  function isHTTPS() {
214
137
  return typeof window !== "undefined" && window.isSecureContext;
215
138
  }
216
-
217
- // src/SheetBackdrop.tsx
218
- var isClickable = (props) => !!props.onClick || !!props.onTap;
219
- var SheetBackdrop = forwardRef(
220
- ({ style, className = "", unstyled, ...rest }, ref) => {
221
- const sheetContext = useSheetContext();
222
- const clickable = isClickable(rest);
223
- const Comp = clickable ? motion.button : motion.div;
224
- const pointerEvents = clickable ? "auto" : "none";
225
- const isUnstyled = unstyled ?? sheetContext.unstyled;
226
- const backdropStyle = {
227
- ...applyStyles(styles.backdrop, isUnstyled),
228
- ...style,
229
- pointerEvents
230
- };
231
- const animationProps = sheetContext.prefersReducedMotion ? {} : {
232
- initial: { opacity: 0 },
233
- animate: { opacity: 1 },
234
- exit: { opacity: 0 },
235
- transition: { duration: 1 }
236
- };
237
- return /* @__PURE__ */ React4.createElement(
238
- Comp,
239
- {
240
- ...animationProps,
241
- ...rest,
242
- ref,
243
- className: `react-modal-sheet-backdrop ${className}`,
244
- style: backdropStyle
245
- }
246
- );
247
- }
248
- );
249
- SheetBackdrop.displayName = "SheetBackdrop";
250
- var SheetContainer = forwardRef(
251
- ({ children, style, className = "", unstyled, ...rest }, ref) => {
252
- const sheetContext = useSheetContext();
253
- const isUnstyled = unstyled ?? sheetContext.unstyled;
254
- const containerStyle = {
255
- ...applyStyles(styles.container, isUnstyled),
256
- ...style,
257
- y: sheetContext.y
258
- };
259
- if (sheetContext.detent === "default") {
260
- containerStyle.height = DEFAULT_HEIGHT;
261
- }
262
- if (sheetContext.detent === "full") {
263
- containerStyle.height = "100%";
264
- containerStyle.maxHeight = "100%";
265
- }
266
- if (sheetContext.detent === "content") {
267
- containerStyle.height = "auto";
268
- containerStyle.maxHeight = DEFAULT_HEIGHT;
269
- }
270
- return /* @__PURE__ */ React4.createElement(
271
- motion.div,
272
- {
273
- ...rest,
274
- ref: mergeRefs([
275
- ref,
276
- sheetContext.sheetRef,
277
- sheetContext.sheetBoundsRef
278
- ]),
279
- className: `react-modal-sheet-container ${className}`,
280
- style: containerStyle
281
- },
282
- children
283
- );
284
- }
285
- );
286
- SheetContainer.displayName = "SheetContainer";
287
- var constraints = { bottom: 0, top: 0, left: 0, right: 0 };
288
- function useDragConstraints() {
289
- const ref = useRef(null);
290
- const onMeasure = useCallback(() => constraints, []);
291
- return { ref, onMeasure };
139
+ function clamp(value, min, max) {
140
+ return Math.min(Math.max(value, min), max);
292
141
  }
293
- var useIsomorphicLayoutEffect = IS_SSR ? useEffect : useLayoutEffect;
294
-
295
- // src/hooks/use-stable-callback.ts
296
142
  function useStableCallback(handler) {
297
143
  const handlerRef = useRef(void 0);
298
144
  useIsomorphicLayoutEffect(() => {
@@ -303,251 +149,18 @@ function useStableCallback(handler) {
303
149
  return fn == null ? void 0 : fn(...args);
304
150
  }, []);
305
151
  }
306
-
307
- // src/hooks/use-scroll-position.ts
308
- function useScrollPosition(options = {}) {
309
- const { debounceDelay = 32, isEnabled = true } = options;
310
- const scrollTimeoutRef = useRef(null);
311
- const [element, setElement] = useState(null);
312
- const [scrollPosition, setScrollPosition] = useState(void 0);
313
- const scrollRef = useMemo(
314
- () => (element2) => {
315
- setElement(element2);
316
- },
317
- []
318
- );
319
- const determineScrollPosition = useStableCallback((element2) => {
320
- const { scrollTop, scrollHeight, clientHeight } = element2;
321
- const isScrollable2 = scrollHeight > clientHeight;
322
- if (!isScrollable2) {
323
- if (scrollPosition) setScrollPosition(void 0);
324
- return;
325
- }
326
- const isAtTop = scrollTop <= 0;
327
- const isAtBottom = Math.ceil(scrollHeight) - Math.ceil(scrollTop) === Math.ceil(clientHeight);
328
- let position;
329
- if (isAtTop) {
330
- position = "top";
331
- } else if (isAtBottom) {
332
- position = "bottom";
333
- } else {
334
- position = "middle";
335
- }
336
- element2.style.touchAction = isAtTop ? "pan-down" : "";
337
- if (position === scrollPosition) return;
338
- setScrollPosition(position);
339
- });
340
- const onScroll = useStableCallback((event) => {
341
- if (event.currentTarget instanceof HTMLElement) {
342
- const el = event.currentTarget;
343
- if (scrollTimeoutRef.current) clearTimeout(scrollTimeoutRef.current);
344
- if (debounceDelay === 0) {
345
- determineScrollPosition(el);
346
- } else {
347
- scrollTimeoutRef.current = setTimeout(
348
- () => determineScrollPosition(el),
349
- debounceDelay
350
- );
351
- }
352
- }
353
- });
354
- const onTouchStart = useStableCallback((event) => {
355
- if (event.currentTarget instanceof HTMLElement) {
356
- const element2 = event.currentTarget;
357
- requestAnimationFrame(() => {
358
- determineScrollPosition(element2);
359
- });
360
- }
361
- });
362
- useEffect(() => {
363
- if (!element || !isEnabled) return;
364
- determineScrollPosition(element);
365
- element.addEventListener("scroll", onScroll);
366
- element.addEventListener("touchstart", onTouchStart);
367
- return () => {
368
- if (scrollTimeoutRef.current) clearTimeout(scrollTimeoutRef.current);
369
- element.removeEventListener("scroll", onScroll);
370
- element.removeEventListener("touchstart", onTouchStart);
371
- };
372
- }, [element, isEnabled]);
373
- return {
374
- scrollRef,
375
- scrollPosition
376
- };
377
- }
378
-
379
- // src/SheetContent.tsx
380
- var SheetContent = forwardRef(
381
- ({
382
- disableScroll: disableScrollProp,
383
- disableDrag: disableDragProp,
384
- children,
385
- style: styleProp,
386
- className = "",
387
- scrollClassName = "",
388
- scrollStyle: scrollStyleProp,
389
- scrollRef: scrollRefProp = null,
390
- unstyled,
391
- ...rest
392
- }, ref) => {
393
- const sheetContext = useSheetContext();
394
- const dragConstraints = useDragConstraints();
395
- const scroll = useScrollPosition({
396
- isEnabled: typeof disableScrollProp === "function" ? true : !disableScrollProp
397
- });
398
- const disableScroll = typeof disableScrollProp === "function" ? disableScrollProp({
399
- scrollPosition: scroll.scrollPosition,
400
- currentSnap: sheetContext.currentSnap
401
- }) : Boolean(disableScrollProp);
402
- const disableDragDueToScroll = !disableScroll && scroll.scrollPosition && scroll.scrollPosition !== "top";
403
- const disableDragDueToProp = typeof disableDragProp === "function" ? disableDragProp({
404
- scrollPosition: scroll.scrollPosition,
405
- currentSnap: sheetContext.currentSnap
406
- }) : Boolean(disableDragProp);
407
- const disableDrag = disableDragDueToProp || disableDragDueToScroll || sheetContext.disableDrag;
408
- const dragProps = disableDrag || sheetContext.disableDrag ? void 0 : sheetContext.dragProps;
409
- const isUnstyled = unstyled ?? sheetContext.unstyled;
410
- const contentStyle = {
411
- ...applyStyles(styles.content, isUnstyled),
412
- ...styleProp
413
- };
414
- const scrollStyle = applyStyles(styles.scroller, isUnstyled);
415
- if (sheetContext.avoidKeyboard) {
416
- scrollStyle.paddingBottom = isHTTPS() ? "env(keyboard-inset-height, var(--keyboard-inset-height, 0px))" : "var(--keyboard-inset-height, 0px)";
417
- }
418
- if (disableScroll) {
419
- scrollStyle.overflowY = "hidden";
420
- }
421
- if (dragProps !== void 0) {
422
- scrollStyle.touchAction = "pan-down";
423
- }
424
- return /* @__PURE__ */ React4.createElement(
425
- motion.div,
426
- {
427
- ...rest,
428
- ref: mergeRefs([ref, dragConstraints.ref]),
429
- className: `react-modal-sheet-content ${className}`,
430
- style: contentStyle,
431
- ...dragProps,
432
- dragConstraints: dragConstraints.ref,
433
- onMeasureDragConstraints: dragConstraints.onMeasure
434
- },
435
- /* @__PURE__ */ React4.createElement(
436
- motion.div,
437
- {
438
- ref: mergeRefs([scroll.scrollRef, scrollRefProp]),
439
- style: { ...scrollStyle, ...scrollStyleProp },
440
- className: `react-modal-sheet-content-scroller ${scrollClassName}`
441
- },
442
- children
443
- )
444
- );
445
- }
446
- );
447
- SheetContent.displayName = "SheetContent";
448
- function SheetDragIndicator({
449
- style,
450
- className = "",
451
- unstyled,
452
- ...rest
453
- }) {
454
- const sheetContext = useSheetContext();
455
- const indicator1Transform = useTransform(
456
- sheetContext.indicatorRotation,
457
- (r) => `translateX(2px) rotate(${r}deg)`
458
- );
459
- const indicator2Transform = useTransform(
460
- sheetContext.indicatorRotation,
461
- (r) => `translateX(-2px) rotate(${ -1 * r}deg)`
462
- );
463
- const isUnstyled = unstyled ?? sheetContext.unstyled;
464
- const indicatorWrapperStyle = {
465
- ...applyStyles(styles.indicatorWrapper, isUnstyled),
466
- ...style
467
- };
468
- const indicatorStyle = applyStyles(styles.indicator, isUnstyled);
469
- return /* @__PURE__ */ React4.createElement(
470
- "div",
471
- {
472
- className: `react-modal-sheet-drag-indicator-container ${className}`,
473
- style: indicatorWrapperStyle,
474
- ...rest
475
- },
476
- /* @__PURE__ */ React4.createElement(
477
- motion.span,
478
- {
479
- className: "react-modal-sheet-drag-indicator",
480
- style: { ...indicatorStyle, transform: indicator1Transform }
481
- }
482
- ),
483
- /* @__PURE__ */ React4.createElement(
484
- motion.span,
485
- {
486
- className: "react-modal-sheet-drag-indicator",
487
- style: { ...indicatorStyle, transform: indicator2Transform }
488
- }
489
- )
490
- );
491
- }
492
- var SheetHeader = forwardRef(
493
- ({ children, style, disableDrag, unstyled, className = "", ...rest }, ref) => {
494
- const sheetContext = useSheetContext();
495
- const dragConstraints = useDragConstraints();
496
- const dragProps = disableDrag || sheetContext.disableDrag ? void 0 : sheetContext.dragProps;
497
- const isUnstyled = unstyled ?? sheetContext.unstyled;
498
- const headerWrapperStyle = {
499
- ...applyStyles(styles.headerWrapper, isUnstyled),
500
- ...style
501
- };
502
- const headerStyle = applyStyles(styles.header, isUnstyled);
503
- return /* @__PURE__ */ React4.createElement(
504
- motion.div,
505
- {
506
- ...rest,
507
- ref: mergeRefs([ref, dragConstraints.ref]),
508
- style: headerWrapperStyle,
509
- className: `react-modal-sheet-header-container ${className}`,
510
- ...dragProps,
511
- dragConstraints: dragConstraints.ref,
512
- onMeasureDragConstraints: dragConstraints.onMeasure
513
- },
514
- children || /* @__PURE__ */ React4.createElement("div", { className: "react-modal-sheet-header", style: headerStyle }, /* @__PURE__ */ React4.createElement(SheetDragIndicator, null))
515
- );
516
- }
517
- );
518
- SheetHeader.displayName = "SheetHeader";
519
- function useDimensions() {
520
- const [dimensions, setDimensions] = useState(() => ({
521
- windowHeight: !IS_SSR ? window.innerHeight : 0,
522
- windowWidth: !IS_SSR ? window.innerWidth : 0
523
- }));
524
- useIsomorphicLayoutEffect(() => {
525
- function handler() {
526
- setDimensions({
527
- windowHeight: window.innerHeight,
528
- windowWidth: window.innerWidth
529
- });
530
- }
531
- handler();
532
- window.addEventListener("resize", handler);
533
- return () => {
534
- window.removeEventListener("resize", handler);
535
- };
536
- }, []);
537
- return dimensions;
538
- }
539
- var virtualKeyboardOverlayUsers = 0;
540
- var initialVirtualKeyboardOverlaysContent = null;
541
- function useVirtualKeyboard(options = {}) {
542
- const {
543
- containerRef,
544
- isEnabled = true,
545
- debounceDelay = 100,
546
- visualViewportThreshold = 100
547
- } = options;
548
- const [state, setState] = useState({
549
- isVisible: false,
550
- height: 0
152
+ var virtualKeyboardOverlayUsers = 0;
153
+ var initialVirtualKeyboardOverlaysContent = null;
154
+ function useVirtualKeyboard(options = {}) {
155
+ const {
156
+ containerRef,
157
+ isEnabled = true,
158
+ debounceDelay = 100,
159
+ visualViewportThreshold = 100
160
+ } = options;
161
+ const [state, setState] = useState({
162
+ isVisible: false,
163
+ height: 0
551
164
  });
552
165
  const focusedElementRef = useRef(null);
553
166
  const debounceTimer = useRef(null);
@@ -768,77 +381,82 @@ function useModalEffect({
768
381
  console.warn('Using "full" detent with modal effect is not supported.');
769
382
  rootId = void 0;
770
383
  }
771
- useIsomorphicLayoutEffect(() => {
772
- return () => {
773
- if (rootId) cleanupModalEffect(rootId);
774
- };
775
- }, []);
776
- useIsomorphicLayoutEffect(() => {
384
+ const handleYChange = useStableCallback((yValue) => {
777
385
  if (!rootId) return;
778
- const root = document.querySelector(`#${rootId}`);
779
- if (!root) return;
780
- const removeStartListener = y.on("animationStart", () => {
781
- setupModalEffect(rootId);
782
- });
783
- const removeChangeListener = y.on("change", (yValue) => {
784
- if (!root) return;
785
- let progress = Math.max(0, 1 - yValue / sheetHeight);
786
- const snapThresholdPoint = snapPoints.length > 1 ? snapPoints[snapPoints.length - 2] : void 0;
787
- if (snapThresholdPoint !== void 0) {
788
- const snapThresholdValue = snapThresholdPoint.snapValueY;
789
- if (yValue <= snapThresholdValue) {
790
- progress = (snapThresholdValue - yValue) / snapThresholdValue;
791
- } else {
792
- progress = 0;
793
- }
386
+ let progress = Math.max(0, 1 - yValue / sheetHeight);
387
+ const snapThresholdPoint = snapPoints.length > 1 ? snapPoints[snapPoints.length - 2] : void 0;
388
+ if (snapThresholdPoint !== void 0) {
389
+ const snapThresholdValue = snapThresholdPoint.snapValueY;
390
+ if (yValue <= snapThresholdValue) {
391
+ progress = (snapThresholdValue - yValue) / snapThresholdValue;
392
+ } else {
393
+ progress = 0;
794
394
  }
795
- if (startThreshold !== void 0) {
796
- const startThresholdValue = sheetHeight - Math.min(Math.floor(startThreshold * sheetHeight), sheetHeight);
797
- if (yValue <= startThresholdValue) {
798
- progress = (startThresholdValue - yValue) / startThresholdValue;
799
- } else {
800
- progress = 0;
801
- }
395
+ }
396
+ if (startThreshold !== void 0) {
397
+ const startThresholdValue = sheetHeight - Math.min(Math.floor(startThreshold * sheetHeight), sheetHeight);
398
+ if (yValue <= startThresholdValue) {
399
+ progress = (startThresholdValue - yValue) / startThresholdValue;
400
+ } else {
401
+ progress = 0;
802
402
  }
803
- progress = Math.max(0, Math.min(1, progress));
804
- const pageWidth = window.innerWidth;
805
- const ty = transform(progress, [0, 1], [0, 24 + insetTop]);
806
- const s = transform(progress, [0, 1], [1, (pageWidth - 16) / pageWidth]);
807
- const borderRadius = transform(progress, [0, 1], [0, 10]);
808
- root.style.transform = `scale(${s}) translate3d(0, ${ty}px, 0)`;
809
- root.style.borderTopRightRadius = `${borderRadius}px`;
810
- root.style.borderTopLeftRadius = `${borderRadius}px`;
403
+ }
404
+ progress = Math.max(0, Math.min(1, progress));
405
+ const pageWidth = window.innerWidth;
406
+ updateModalEffect(rootId, {
407
+ scale: transform(progress, [0, 1], [1, (pageWidth - 16) / pageWidth]),
408
+ translateY: transform(progress, [0, 1], [0, 24 + insetTop]),
409
+ borderRadius: transform(progress, [0, 1], [0, 10])
811
410
  });
812
- function onCompleted() {
813
- if (y.get() - 5 >= sheetHeight) {
814
- cleanupModalEffect(rootId);
815
- }
411
+ });
412
+ const handleYComplete = useStableCallback(() => {
413
+ if (!rootId) return;
414
+ if (y.get() + 5 >= sheetHeight) {
415
+ cleanupModalEffect(rootId);
816
416
  }
817
- const removeCompleteListener = y.on("animationComplete", onCompleted);
818
- const removeCancelListener = y.on("animationCancel", onCompleted);
417
+ });
418
+ useEffect(() => {
419
+ if (!rootId) return;
420
+ const removeStartListener = y.on("animationStart", () => {
421
+ if (rootId) setupModalEffect(rootId);
422
+ });
423
+ const removeChangeListener = y.on("change", (yValue) => {
424
+ handleYChange(yValue);
425
+ });
426
+ const removeCompleteListener = y.on("animationComplete", handleYComplete);
427
+ const removeCancelListener = y.on("animationCancel", handleYComplete);
819
428
  return () => {
429
+ if (rootId) cleanupModalEffect(rootId);
820
430
  removeStartListener();
821
431
  removeChangeListener();
822
432
  removeCompleteListener();
823
433
  removeCancelListener();
824
434
  };
825
- }, [y, rootId, insetTop, startThreshold, sheetHeight]);
435
+ }, [rootId]);
826
436
  }
827
437
  function setupModalEffect(rootId) {
828
438
  const root = document.querySelector(`#${rootId}`);
829
- const body = document.querySelector("body");
830
439
  if (!root) return;
831
- body.style.backgroundColor = "#000";
440
+ const body = document.querySelector("body");
441
+ body.style.backgroundColor = "#000000";
832
442
  root.style.overflow = "hidden";
833
443
  root.style.transitionTimingFunction = "cubic-bezier(0.32, 0.72, 0, 1)";
834
444
  root.style.transitionProperty = "transform, border-radius";
835
445
  root.style.transitionDuration = "0.5s";
836
446
  root.style.transformOrigin = "center top";
837
447
  }
448
+ function updateModalEffect(rootId, opts) {
449
+ const root = document.querySelector(`#${rootId}`);
450
+ if (!root) return;
451
+ const { scale, translateY, borderRadius } = opts;
452
+ root.style.transform = `scale(${scale}) translate3d(0, ${translateY}px, 0)`;
453
+ root.style.borderTopRightRadius = `${borderRadius}px`;
454
+ root.style.borderTopLeftRadius = `${borderRadius}px`;
455
+ }
838
456
  function cleanupModalEffect(rootId) {
839
457
  const root = document.querySelector(`#${rootId}`);
840
- const body = document.querySelector("body");
841
458
  if (!root) return;
459
+ const body = document.querySelector("body");
842
460
  body.style.removeProperty("background-color");
843
461
  root.style.removeProperty("overflow");
844
462
  root.style.removeProperty("transition-timing-function");
@@ -1150,9 +768,6 @@ function computeSnapPoints({
1150
768
  // Y value is inverted as `y = 0` means sheet is at the top
1151
769
  }));
1152
770
  }
1153
- function clamp(value, min, max) {
1154
- return Math.min(Math.max(value, min), max);
1155
- }
1156
771
  function sign(value) {
1157
772
  if (value > 0) return 1;
1158
773
  if (value < 0) return -1;
@@ -1292,48 +907,152 @@ function classifyDragEnd({
1292
907
  };
1293
908
  }
1294
909
 
1295
- // src/sheet.tsx
1296
- var Sheet = forwardRef(
1297
- ({
1298
- avoidKeyboard = true,
1299
- children,
1300
- className = "",
1301
- detent = "default",
1302
- disableDismiss = false,
1303
- disableDrag: disableDragProp = false,
1304
- disableScrollLocking = false,
1305
- dragCloseThreshold = DEFAULT_DRAG_CLOSE_THRESHOLD,
1306
- dragVelocityThreshold = DEFAULT_DRAG_VELOCITY_THRESHOLD,
1307
- initialSnap,
1308
- isOpen,
1309
- modalEffectRootId,
1310
- modalEffectThreshold,
1311
- mountPoint,
1312
- prefersReducedMotion = false,
1313
- snapPoints: snapPointsProp,
1314
- style,
1315
- tweenConfig = DEFAULT_TWEEN_CONFIG,
1316
- unstyled = false,
1317
- onOpenStart,
1318
- onOpenEnd,
1319
- onClose,
1320
- onCloseStart,
1321
- onCloseEnd,
1322
- onSnap,
1323
- onDrag: onDragProp,
1324
- onDragStart: onDragStartProp,
1325
- onDragEnd: onDragEndProp,
910
+ // src/styles.ts
911
+ var styles = {
912
+ root: {
913
+ base: {
914
+ position: "fixed",
915
+ top: 0,
916
+ bottom: 0,
917
+ left: 0,
918
+ right: 0,
919
+ overflow: "hidden",
920
+ pointerEvents: "none"
921
+ },
922
+ decorative: {}
923
+ },
924
+ backdrop: {
925
+ base: {
926
+ zIndex: 1,
927
+ position: "fixed",
928
+ top: 0,
929
+ left: 0,
930
+ width: "100%",
931
+ height: "100%",
932
+ touchAction: "none",
933
+ userSelect: "none"
934
+ },
935
+ decorative: {
936
+ backgroundColor: "rgba(0, 0, 0, 0.2)",
937
+ border: "none",
938
+ WebkitTapHighlightColor: "transparent"
939
+ }
940
+ },
941
+ container: {
942
+ base: {
943
+ zIndex: 2,
944
+ position: "absolute",
945
+ left: 0,
946
+ bottom: 0,
947
+ width: "100%",
948
+ pointerEvents: "auto",
949
+ display: "flex",
950
+ flexDirection: "column"
951
+ },
952
+ decorative: {
953
+ backgroundColor: "#fff",
954
+ borderTopRightRadius: "8px",
955
+ borderTopLeftRadius: "8px",
956
+ boxShadow: "0px -2px 16px rgba(0, 0, 0, 0.3)"
957
+ }
958
+ },
959
+ headerWrapper: {
960
+ base: {
961
+ width: "100%"
962
+ },
963
+ decorative: {}
964
+ },
965
+ header: {
966
+ base: {
967
+ width: "100%",
968
+ position: "relative"
969
+ },
970
+ decorative: {
971
+ height: "40px",
972
+ display: "flex",
973
+ alignItems: "center",
974
+ justifyContent: "center"
975
+ }
976
+ },
977
+ indicatorWrapper: {
978
+ base: {
979
+ display: "flex"
980
+ },
981
+ decorative: {}
982
+ },
983
+ indicator: {
984
+ base: {
985
+ display: "inline-block"
986
+ },
987
+ decorative: {
988
+ width: "18px",
989
+ height: "4px",
990
+ borderRadius: "99px",
991
+ backgroundColor: "#ddd"
992
+ }
993
+ },
994
+ content: {
995
+ base: {
996
+ minHeight: "0px",
997
+ position: "relative",
998
+ flexGrow: 1,
999
+ display: "flex",
1000
+ flexDirection: "column"
1001
+ },
1002
+ decorative: {}
1003
+ },
1004
+ scroller: {
1005
+ base: {
1006
+ height: "100%",
1007
+ overflowY: "auto",
1008
+ overscrollBehaviorY: "none"
1009
+ },
1010
+ decorative: {}
1011
+ }
1012
+ };
1013
+
1014
+ // src/sheet.tsx
1015
+ var Sheet = forwardRef(
1016
+ ({
1017
+ avoidKeyboard = true,
1018
+ children,
1019
+ className = "",
1020
+ detent = "default",
1021
+ disableDismiss = false,
1022
+ disableDrag: disableDragProp = false,
1023
+ disableScrollLocking = false,
1024
+ dragCloseThreshold = DEFAULT_DRAG_CLOSE_THRESHOLD,
1025
+ dragVelocityThreshold = DEFAULT_DRAG_VELOCITY_THRESHOLD,
1026
+ initialSnap,
1027
+ isOpen,
1028
+ modalEffectRootId,
1029
+ modalEffectThreshold,
1030
+ mountPoint,
1031
+ prefersReducedMotion = false,
1032
+ snapPoints: snapPointsProp,
1033
+ style,
1034
+ tweenConfig = DEFAULT_TWEEN_CONFIG,
1035
+ unstyled = false,
1036
+ onOpenStart,
1037
+ onOpenEnd,
1038
+ onClose,
1039
+ onCloseStart,
1040
+ onCloseEnd,
1041
+ onSnap,
1042
+ onDrag: onDragProp,
1043
+ onDragStart: onDragStartProp,
1044
+ onDragEnd: onDragEndProp,
1326
1045
  ...rest
1327
1046
  }, ref) => {
1328
1047
  const [sheetBoundsRef, sheetBounds] = useMeasure();
1329
1048
  const sheetRef = useRef(null);
1330
1049
  const sheetHeight = Math.round(sheetBounds.height);
1331
1050
  const [currentSnap, setCurrentSnap] = useState(initialSnap);
1332
- const snapPoints = snapPointsProp && sheetHeight > 0 ? computeSnapPoints({ sheetHeight, snapPointsProp }) : [];
1333
1051
  const { windowHeight } = useDimensions();
1334
1052
  const closedY = sheetHeight > 0 ? sheetHeight : windowHeight;
1335
1053
  const y = useMotionValue(closedY);
1336
1054
  const yInverted = useTransform(y, (val) => Math.max(sheetHeight - val, 0));
1055
+ const yProgress = useTransform(y, (val) => clamp(1 - val / closedY, 0, 1));
1337
1056
  const indicatorRotation = useMotionValue(0);
1338
1057
  const shouldReduceMotion = useReducedMotion();
1339
1058
  const reduceMotion = Boolean(prefersReducedMotion || shouldReduceMotion);
@@ -1349,6 +1068,9 @@ var Sheet = forwardRef(
1349
1068
  y,
1350
1069
  (val) => val + 2 >= closedY ? "hidden" : "visible"
1351
1070
  );
1071
+ const snapPoints = useMemo(() => {
1072
+ return snapPointsProp && sheetHeight > 0 ? computeSnapPoints({ sheetHeight, snapPointsProp }) : [];
1073
+ }, [sheetHeight, snapPointsProp == null ? void 0 : snapPointsProp.join("")]);
1352
1074
  const updateSnap = useStableCallback((snapIndex) => {
1353
1075
  setCurrentSnap(snapIndex);
1354
1076
  onSnap == null ? void 0 : onSnap(snapIndex);
@@ -1461,12 +1183,6 @@ var Sheet = forwardRef(
1461
1183
  }
1462
1184
  indicatorRotation.set(0);
1463
1185
  });
1464
- useImperativeHandle(ref, () => ({
1465
- y,
1466
- yInverted,
1467
- height: sheetHeight,
1468
- snapTo
1469
- }));
1470
1186
  useModalEffect({
1471
1187
  y,
1472
1188
  detent,
@@ -1486,9 +1202,7 @@ var Sheet = forwardRef(
1486
1202
  const initialSnapPoint = initialSnap !== void 0 ? getSnapPoint(initialSnap) : null;
1487
1203
  const yTo = (initialSnapPoint == null ? void 0 : initialSnapPoint.snapValueY) ?? 0;
1488
1204
  await animate(y, yTo, animationOptions);
1489
- if (initialSnap !== void 0) {
1490
- updateSnap(initialSnap);
1491
- }
1205
+ if (initialSnap !== void 0) updateSnap(initialSnap);
1492
1206
  onOpenEnd == null ? void 0 : onOpenEnd();
1493
1207
  },
1494
1208
  onClosing: async () => {
@@ -1506,7 +1220,7 @@ var Sheet = forwardRef(
1506
1220
  onDragStart,
1507
1221
  onDragEnd
1508
1222
  };
1509
- const context = {
1223
+ const internalContext = {
1510
1224
  currentSnap,
1511
1225
  detent,
1512
1226
  disableDrag,
@@ -1517,9 +1231,20 @@ var Sheet = forwardRef(
1517
1231
  sheetBoundsRef,
1518
1232
  sheetRef,
1519
1233
  unstyled,
1234
+ yProgress,
1520
1235
  y
1521
1236
  };
1522
- const sheet = /* @__PURE__ */ React4.createElement(SheetContext.Provider, { value: context }, /* @__PURE__ */ React4.createElement(
1237
+ const exposedContext = {
1238
+ height: sheetHeight,
1239
+ currentSnap,
1240
+ snapPoints,
1241
+ snapTo,
1242
+ y,
1243
+ yInverted,
1244
+ yProgress
1245
+ };
1246
+ useImperativeHandle(ref, () => exposedContext);
1247
+ const sheet = /* @__PURE__ */ React.createElement(InternalSheetContext.Provider, { value: internalContext }, /* @__PURE__ */ React.createElement(
1523
1248
  motion.div,
1524
1249
  {
1525
1250
  ...rest,
@@ -1533,13 +1258,293 @@ var Sheet = forwardRef(
1533
1258
  ...style
1534
1259
  }
1535
1260
  },
1536
- state !== "closed" ? children : null
1261
+ /* @__PURE__ */ React.createElement(ExposedSheetContext.Provider, { value: exposedContext }, state !== "closed" ? children : null)
1537
1262
  ));
1538
1263
  if (IS_SSR) return sheet;
1539
1264
  return createPortal(sheet, mountPoint ?? document.body);
1540
1265
  }
1541
1266
  );
1542
1267
  Sheet.displayName = "Sheet";
1268
+ var isClickable = (props) => !!props.onClick || !!props.onTap;
1269
+ var SheetBackdrop = forwardRef(
1270
+ ({ style, className = "", unstyled, ...rest }, ref) => {
1271
+ const sheetContext = useInternalContext();
1272
+ const clickable = isClickable(rest);
1273
+ const Comp = clickable ? motion.button : motion.div;
1274
+ const pointerEvents = clickable ? "auto" : "none";
1275
+ const isUnstyled = unstyled ?? sheetContext.unstyled;
1276
+ const backdropStyle = {
1277
+ opacity: sheetContext.yProgress,
1278
+ ...applyStyles(styles.backdrop, isUnstyled),
1279
+ ...style,
1280
+ pointerEvents
1281
+ };
1282
+ return /* @__PURE__ */ React.createElement(
1283
+ Comp,
1284
+ {
1285
+ ...rest,
1286
+ ref,
1287
+ className: `react-modal-sheet-backdrop ${className}`,
1288
+ style: backdropStyle
1289
+ }
1290
+ );
1291
+ }
1292
+ );
1293
+ SheetBackdrop.displayName = "SheetBackdrop";
1294
+ var SheetContainer = forwardRef(
1295
+ ({ children, style, className = "", unstyled, ...rest }, ref) => {
1296
+ const sheetContext = useInternalContext();
1297
+ const isUnstyled = unstyled ?? sheetContext.unstyled;
1298
+ const containerStyle = {
1299
+ ...applyStyles(styles.container, isUnstyled),
1300
+ ...style,
1301
+ y: sheetContext.y
1302
+ };
1303
+ if (sheetContext.detent === "default") {
1304
+ containerStyle.height = DEFAULT_HEIGHT;
1305
+ }
1306
+ if (sheetContext.detent === "full") {
1307
+ containerStyle.height = "100%";
1308
+ containerStyle.maxHeight = "100%";
1309
+ }
1310
+ if (sheetContext.detent === "content") {
1311
+ containerStyle.height = "auto";
1312
+ containerStyle.maxHeight = DEFAULT_HEIGHT;
1313
+ }
1314
+ return /* @__PURE__ */ React.createElement(
1315
+ motion.div,
1316
+ {
1317
+ ...rest,
1318
+ ref: mergeRefs([
1319
+ ref,
1320
+ sheetContext.sheetRef,
1321
+ sheetContext.sheetBoundsRef
1322
+ ]),
1323
+ className: `react-modal-sheet-container ${className}`,
1324
+ style: containerStyle
1325
+ },
1326
+ children
1327
+ );
1328
+ }
1329
+ );
1330
+ SheetContainer.displayName = "SheetContainer";
1331
+ var constraints = { bottom: 0, top: 0, left: 0, right: 0 };
1332
+ function useDragConstraints() {
1333
+ const ref = useRef(null);
1334
+ const onMeasure = useCallback(() => constraints, []);
1335
+ return { ref, onMeasure };
1336
+ }
1337
+ function useScrollPosition(options = {}) {
1338
+ const { debounceDelay = 32, isEnabled = true } = options;
1339
+ const scrollTimeoutRef = useRef(null);
1340
+ const [element, setElement] = useState(null);
1341
+ const [scrollPosition, setScrollPosition] = useState(void 0);
1342
+ const scrollRef = useMemo(
1343
+ () => (element2) => {
1344
+ setElement(element2);
1345
+ },
1346
+ []
1347
+ );
1348
+ const determineScrollPosition = useStableCallback((element2) => {
1349
+ const { scrollTop, scrollHeight, clientHeight } = element2;
1350
+ const isScrollable2 = scrollHeight > clientHeight;
1351
+ if (!isScrollable2) {
1352
+ if (scrollPosition) setScrollPosition(void 0);
1353
+ return;
1354
+ }
1355
+ const isAtTop = scrollTop <= 0;
1356
+ const isAtBottom = Math.ceil(scrollHeight) - Math.ceil(scrollTop) === Math.ceil(clientHeight);
1357
+ let position;
1358
+ if (isAtTop) {
1359
+ position = "top";
1360
+ } else if (isAtBottom) {
1361
+ position = "bottom";
1362
+ } else {
1363
+ position = "middle";
1364
+ }
1365
+ element2.style.touchAction = isAtTop ? "pan-down" : "";
1366
+ if (position === scrollPosition) return;
1367
+ setScrollPosition(position);
1368
+ });
1369
+ const onScroll = useStableCallback((event) => {
1370
+ if (event.currentTarget instanceof HTMLElement) {
1371
+ const el = event.currentTarget;
1372
+ if (scrollTimeoutRef.current) clearTimeout(scrollTimeoutRef.current);
1373
+ if (debounceDelay === 0) {
1374
+ determineScrollPosition(el);
1375
+ } else {
1376
+ scrollTimeoutRef.current = setTimeout(
1377
+ () => determineScrollPosition(el),
1378
+ debounceDelay
1379
+ );
1380
+ }
1381
+ }
1382
+ });
1383
+ const onTouchStart = useStableCallback((event) => {
1384
+ if (event.currentTarget instanceof HTMLElement) {
1385
+ const element2 = event.currentTarget;
1386
+ requestAnimationFrame(() => {
1387
+ determineScrollPosition(element2);
1388
+ });
1389
+ }
1390
+ });
1391
+ useEffect(() => {
1392
+ if (!element || !isEnabled) return;
1393
+ determineScrollPosition(element);
1394
+ element.addEventListener("scroll", onScroll);
1395
+ element.addEventListener("touchstart", onTouchStart);
1396
+ return () => {
1397
+ if (scrollTimeoutRef.current) clearTimeout(scrollTimeoutRef.current);
1398
+ element.removeEventListener("scroll", onScroll);
1399
+ element.removeEventListener("touchstart", onTouchStart);
1400
+ };
1401
+ }, [element, isEnabled]);
1402
+ return {
1403
+ scrollRef,
1404
+ scrollPosition
1405
+ };
1406
+ }
1407
+
1408
+ // src/sheet-content.tsx
1409
+ var SheetContent = forwardRef(
1410
+ ({
1411
+ disableScroll: disableScrollProp,
1412
+ disableDrag: disableDragProp,
1413
+ children,
1414
+ style: styleProp,
1415
+ className = "",
1416
+ scrollClassName = "",
1417
+ scrollStyle: scrollStyleProp,
1418
+ scrollRef: scrollRefProp = null,
1419
+ unstyled,
1420
+ ...rest
1421
+ }, ref) => {
1422
+ const sheetContext = useInternalContext();
1423
+ const dragConstraints = useDragConstraints();
1424
+ const scroll = useScrollPosition({
1425
+ isEnabled: typeof disableScrollProp === "function" ? true : !disableScrollProp
1426
+ });
1427
+ const disableScroll = typeof disableScrollProp === "function" ? disableScrollProp({
1428
+ scrollPosition: scroll.scrollPosition,
1429
+ currentSnap: sheetContext.currentSnap
1430
+ }) : Boolean(disableScrollProp);
1431
+ const disableDragDueToScroll = !disableScroll && scroll.scrollPosition && scroll.scrollPosition !== "top";
1432
+ const disableDragDueToProp = typeof disableDragProp === "function" ? disableDragProp({
1433
+ scrollPosition: scroll.scrollPosition,
1434
+ currentSnap: sheetContext.currentSnap
1435
+ }) : Boolean(disableDragProp);
1436
+ const disableDrag = disableDragDueToProp || disableDragDueToScroll || sheetContext.disableDrag;
1437
+ const dragProps = disableDrag || sheetContext.disableDrag ? void 0 : sheetContext.dragProps;
1438
+ const isUnstyled = unstyled ?? sheetContext.unstyled;
1439
+ const contentStyle = {
1440
+ ...applyStyles(styles.content, isUnstyled),
1441
+ ...styleProp
1442
+ };
1443
+ const scrollStyle = applyStyles(styles.scroller, isUnstyled);
1444
+ if (sheetContext.avoidKeyboard) {
1445
+ scrollStyle.paddingBottom = isHTTPS() ? "env(keyboard-inset-height, var(--keyboard-inset-height, 0px))" : "var(--keyboard-inset-height, 0px)";
1446
+ }
1447
+ if (disableScroll) {
1448
+ scrollStyle.overflowY = "clip";
1449
+ }
1450
+ if (dragProps !== void 0) {
1451
+ scrollStyle.touchAction = "pan-down";
1452
+ }
1453
+ return /* @__PURE__ */ React.createElement(
1454
+ motion.div,
1455
+ {
1456
+ ...rest,
1457
+ ref: mergeRefs([ref, dragConstraints.ref]),
1458
+ className: `react-modal-sheet-content ${className}`,
1459
+ style: contentStyle,
1460
+ ...dragProps,
1461
+ dragConstraints: dragConstraints.ref,
1462
+ onMeasureDragConstraints: dragConstraints.onMeasure
1463
+ },
1464
+ /* @__PURE__ */ React.createElement(
1465
+ motion.div,
1466
+ {
1467
+ ref: mergeRefs([scroll.scrollRef, scrollRefProp]),
1468
+ style: { ...scrollStyle, ...scrollStyleProp },
1469
+ className: `react-modal-sheet-content-scroller ${scrollClassName}`
1470
+ },
1471
+ children
1472
+ )
1473
+ );
1474
+ }
1475
+ );
1476
+ SheetContent.displayName = "SheetContent";
1477
+ function SheetDragIndicator({
1478
+ style,
1479
+ className = "",
1480
+ unstyled,
1481
+ ...rest
1482
+ }) {
1483
+ const sheetContext = useInternalContext();
1484
+ const indicator1Transform = useTransform(
1485
+ sheetContext.indicatorRotation,
1486
+ (r) => `translateX(2px) rotate(${r}deg)`
1487
+ );
1488
+ const indicator2Transform = useTransform(
1489
+ sheetContext.indicatorRotation,
1490
+ (r) => `translateX(-2px) rotate(${ -1 * r}deg)`
1491
+ );
1492
+ const isUnstyled = unstyled ?? sheetContext.unstyled;
1493
+ const indicatorWrapperStyle = {
1494
+ ...applyStyles(styles.indicatorWrapper, isUnstyled),
1495
+ ...style
1496
+ };
1497
+ const indicatorStyle = applyStyles(styles.indicator, isUnstyled);
1498
+ return /* @__PURE__ */ React.createElement(
1499
+ "div",
1500
+ {
1501
+ className: `react-modal-sheet-drag-indicator-container ${className}`,
1502
+ style: indicatorWrapperStyle,
1503
+ ...rest
1504
+ },
1505
+ /* @__PURE__ */ React.createElement(
1506
+ motion.span,
1507
+ {
1508
+ className: "react-modal-sheet-drag-indicator",
1509
+ style: { ...indicatorStyle, transform: indicator1Transform }
1510
+ }
1511
+ ),
1512
+ /* @__PURE__ */ React.createElement(
1513
+ motion.span,
1514
+ {
1515
+ className: "react-modal-sheet-drag-indicator",
1516
+ style: { ...indicatorStyle, transform: indicator2Transform }
1517
+ }
1518
+ )
1519
+ );
1520
+ }
1521
+ var SheetHeader = forwardRef(
1522
+ ({ children, style, disableDrag, unstyled, className = "", ...rest }, ref) => {
1523
+ const sheetContext = useInternalContext();
1524
+ const dragConstraints = useDragConstraints();
1525
+ const dragProps = disableDrag || sheetContext.disableDrag ? void 0 : sheetContext.dragProps;
1526
+ const isUnstyled = unstyled ?? sheetContext.unstyled;
1527
+ const headerWrapperStyle = {
1528
+ ...applyStyles(styles.headerWrapper, isUnstyled),
1529
+ ...style
1530
+ };
1531
+ const headerStyle = applyStyles(styles.header, isUnstyled);
1532
+ return /* @__PURE__ */ React.createElement(
1533
+ motion.div,
1534
+ {
1535
+ ...rest,
1536
+ ref: mergeRefs([ref, dragConstraints.ref]),
1537
+ style: headerWrapperStyle,
1538
+ className: `react-modal-sheet-header-container ${className}`,
1539
+ ...dragProps,
1540
+ dragConstraints: dragConstraints.ref,
1541
+ onMeasureDragConstraints: dragConstraints.onMeasure
1542
+ },
1543
+ children || /* @__PURE__ */ React.createElement("div", { className: "react-modal-sheet-header", style: headerStyle }, /* @__PURE__ */ React.createElement(SheetDragIndicator, null))
1544
+ );
1545
+ }
1546
+ );
1547
+ SheetHeader.displayName = "SheetHeader";
1543
1548
 
1544
1549
  // src/index.tsx
1545
1550
  var Sheet2 = Object.assign(Sheet, {
@@ -1547,7 +1552,8 @@ var Sheet2 = Object.assign(Sheet, {
1547
1552
  Header: SheetHeader,
1548
1553
  DragIndicator: SheetDragIndicator,
1549
1554
  Content: SheetContent,
1550
- Backdrop: SheetBackdrop
1555
+ Backdrop: SheetBackdrop,
1556
+ useContext: useExposedContext
1551
1557
  });
1552
1558
 
1553
1559
  export { Sheet2 as Sheet, useScrollPosition, useVirtualKeyboard };