react-modal-sheet 5.4.1 → 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.js CHANGED
@@ -1,129 +1,29 @@
1
1
  'use strict';
2
2
 
3
+ var React = require('react');
3
4
  var react = require('motion/react');
4
- var React4 = require('react');
5
5
  var reactDom = require('react-dom');
6
6
  var useMeasure = require('react-use-measure');
7
7
  var motion = require('motion');
8
8
 
9
9
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
10
10
 
11
- var React4__default = /*#__PURE__*/_interopDefault(React4);
11
+ var React__default = /*#__PURE__*/_interopDefault(React);
12
12
  var useMeasure__default = /*#__PURE__*/_interopDefault(useMeasure);
13
13
 
14
- // src/SheetBackdrop.tsx
15
- var SheetContext = React4.createContext(
16
- void 0
17
- );
18
- function useSheetContext() {
19
- const context = React4.useContext(SheetContext);
14
+ // src/context.tsx
15
+ var InternalSheetContext = React.createContext(void 0);
16
+ var ExposedSheetContext = React.createContext(void 0);
17
+ function useInternalContext() {
18
+ const context = React.useContext(InternalSheetContext);
19
+ if (!context) throw new Error("Sheet context error");
20
+ return context;
21
+ }
22
+ function useExposedContext() {
23
+ const context = React.useContext(ExposedSheetContext);
20
24
  if (!context) throw new Error("Sheet context error");
21
25
  return context;
22
26
  }
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
27
 
128
28
  // src/constants.ts
129
29
  var DEFAULT_HEIGHT = "calc(100% - env(safe-area-inset-top) - 34px)";
@@ -137,7 +37,30 @@ var REDUCED_MOTION_TWEEN_CONFIG = {
137
37
  duration: 0.01
138
38
  };
139
39
  var DEFAULT_DRAG_CLOSE_THRESHOLD = 0.6;
140
- var DEFAULT_DRAG_VELOCITY_THRESHOLD = 500;
40
+ var DEFAULT_DRAG_VELOCITY_THRESHOLD = 1200;
41
+ var useIsomorphicLayoutEffect = IS_SSR ? React.useEffect : React.useLayoutEffect;
42
+
43
+ // src/hooks/use-dimensions.ts
44
+ function useDimensions() {
45
+ const [dimensions, setDimensions] = React.useState(() => ({
46
+ windowHeight: !IS_SSR ? window.innerHeight : 0,
47
+ windowWidth: !IS_SSR ? window.innerWidth : 0
48
+ }));
49
+ useIsomorphicLayoutEffect(() => {
50
+ function handler() {
51
+ setDimensions({
52
+ windowHeight: window.innerHeight,
53
+ windowWidth: window.innerWidth
54
+ });
55
+ }
56
+ handler();
57
+ window.addEventListener("resize", handler);
58
+ return () => {
59
+ window.removeEventListener("resize", handler);
60
+ };
61
+ }, []);
62
+ return dimensions;
63
+ }
141
64
 
142
65
  // src/utils.ts
143
66
  function applyStyles(styles2, unstyled) {
@@ -220,356 +143,46 @@ function willOpenKeyboard(target) {
220
143
  function isHTTPS() {
221
144
  return typeof window !== "undefined" && window.isSecureContext;
222
145
  }
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 };
146
+ function clamp(value, min, max) {
147
+ return Math.min(Math.max(value, min), max);
299
148
  }
300
- var useIsomorphicLayoutEffect = IS_SSR ? React4.useEffect : React4.useLayoutEffect;
301
-
302
- // src/hooks/use-stable-callback.ts
303
149
  function useStableCallback(handler) {
304
- const handlerRef = React4.useRef(void 0);
150
+ const handlerRef = React.useRef(void 0);
305
151
  useIsomorphicLayoutEffect(() => {
306
152
  handlerRef.current = handler;
307
153
  });
308
- return React4.useCallback((...args) => {
154
+ return React.useCallback((...args) => {
309
155
  const fn = handlerRef.current;
310
156
  return fn == null ? void 0 : fn(...args);
311
157
  }, []);
312
158
  }
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);
159
+ var virtualKeyboardOverlayUsers = 0;
160
+ var initialVirtualKeyboardOverlaysContent = null;
161
+ function useVirtualKeyboard(options = {}) {
162
+ const {
163
+ containerRef,
164
+ isEnabled = true,
165
+ debounceDelay = 100,
166
+ visualViewportThreshold = 100
167
+ } = options;
168
+ const [state, setState] = React.useState({
169
+ isVisible: false,
170
+ height: 0
346
171
  });
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
172
+ const focusedElementRef = React.useRef(null);
173
+ const debounceTimer = React.useRef(null);
174
+ React.useEffect(() => {
175
+ const vv = window.visualViewport;
176
+ const vk = getVirtualKeyboardApi();
177
+ function setKeyboardInsetHeightEnv(height) {
178
+ const element = (containerRef == null ? void 0 : containerRef.current) || document.documentElement;
179
+ if (vk) {
180
+ element.style.setProperty(
181
+ "--keyboard-inset-height",
182
+ `env(keyboard-inset-height, ${height}px)`
357
183
  );
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`);
184
+ } else {
185
+ element.style.setProperty("--keyboard-inset-height", `${height}px`);
573
186
  }
574
187
  }
575
188
  function setKeyboardState(nextState) {
@@ -691,7 +304,7 @@ function useKeyboardAvoidance({
691
304
  onWillOpenKeyboard,
692
305
  onDidOpenKeyboard
693
306
  }) {
694
- const [focusedElement, setFocusedElement] = React4.useState(
307
+ const [focusedElement, setFocusedElement] = React.useState(
695
308
  null
696
309
  );
697
310
  const keyboard = useVirtualKeyboard({
@@ -712,7 +325,7 @@ function useKeyboardAvoidance({
712
325
  setFocusedElement(null);
713
326
  }
714
327
  });
715
- React4.useEffect(() => {
328
+ React.useEffect(() => {
716
329
  if (!isEnabled) return;
717
330
  document.addEventListener("focusin", handleFocusIn);
718
331
  document.addEventListener("focusout", handleFocusOut);
@@ -721,7 +334,7 @@ function useKeyboardAvoidance({
721
334
  document.removeEventListener("focusout", handleFocusOut);
722
335
  };
723
336
  }, [isEnabled]);
724
- React4.useEffect(() => {
337
+ React.useEffect(() => {
725
338
  const containerElement = containerRef.current;
726
339
  if (!isEnabled || !focusedElement || !containerElement || !keyboard.isKeyboardOpen) {
727
340
  return;
@@ -733,7 +346,7 @@ function useKeyboardAvoidance({
733
346
  return keyboard;
734
347
  }
735
348
  function useSafeAreaInsets() {
736
- const [insets] = React4.useState(() => {
349
+ const [insets] = React.useState(() => {
737
350
  const fallback = { top: 0, left: 0, right: 0, bottom: 0 };
738
351
  if (IS_SSR) return fallback;
739
352
  const root = document.querySelector(":root");
@@ -775,77 +388,82 @@ function useModalEffect({
775
388
  console.warn('Using "full" detent with modal effect is not supported.');
776
389
  rootId = void 0;
777
390
  }
778
- useIsomorphicLayoutEffect(() => {
779
- return () => {
780
- if (rootId) cleanupModalEffect(rootId);
781
- };
782
- }, []);
783
- useIsomorphicLayoutEffect(() => {
391
+ const handleYChange = useStableCallback((yValue) => {
784
392
  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
- }
393
+ let progress = Math.max(0, 1 - yValue / sheetHeight);
394
+ const snapThresholdPoint = snapPoints.length > 1 ? snapPoints[snapPoints.length - 2] : void 0;
395
+ if (snapThresholdPoint !== void 0) {
396
+ const snapThresholdValue = snapThresholdPoint.snapValueY;
397
+ if (yValue <= snapThresholdValue) {
398
+ progress = (snapThresholdValue - yValue) / snapThresholdValue;
399
+ } else {
400
+ progress = 0;
801
401
  }
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
- }
402
+ }
403
+ if (startThreshold !== void 0) {
404
+ const startThresholdValue = sheetHeight - Math.min(Math.floor(startThreshold * sheetHeight), sheetHeight);
405
+ if (yValue <= startThresholdValue) {
406
+ progress = (startThresholdValue - yValue) / startThresholdValue;
407
+ } else {
408
+ progress = 0;
809
409
  }
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`;
410
+ }
411
+ progress = Math.max(0, Math.min(1, progress));
412
+ const pageWidth = window.innerWidth;
413
+ updateModalEffect(rootId, {
414
+ scale: motion.transform(progress, [0, 1], [1, (pageWidth - 16) / pageWidth]),
415
+ translateY: motion.transform(progress, [0, 1], [0, 24 + insetTop]),
416
+ borderRadius: motion.transform(progress, [0, 1], [0, 10])
818
417
  });
819
- function onCompleted() {
820
- if (y.get() - 5 >= sheetHeight) {
821
- cleanupModalEffect(rootId);
822
- }
418
+ });
419
+ const handleYComplete = useStableCallback(() => {
420
+ if (!rootId) return;
421
+ if (y.get() + 5 >= sheetHeight) {
422
+ cleanupModalEffect(rootId);
823
423
  }
824
- const removeCompleteListener = y.on("animationComplete", onCompleted);
825
- const removeCancelListener = y.on("animationCancel", onCompleted);
424
+ });
425
+ React.useEffect(() => {
426
+ if (!rootId) return;
427
+ const removeStartListener = y.on("animationStart", () => {
428
+ if (rootId) setupModalEffect(rootId);
429
+ });
430
+ const removeChangeListener = y.on("change", (yValue) => {
431
+ handleYChange(yValue);
432
+ });
433
+ const removeCompleteListener = y.on("animationComplete", handleYComplete);
434
+ const removeCancelListener = y.on("animationCancel", handleYComplete);
826
435
  return () => {
436
+ if (rootId) cleanupModalEffect(rootId);
827
437
  removeStartListener();
828
438
  removeChangeListener();
829
439
  removeCompleteListener();
830
440
  removeCancelListener();
831
441
  };
832
- }, [y, rootId, insetTop, startThreshold, sheetHeight]);
442
+ }, [rootId]);
833
443
  }
834
444
  function setupModalEffect(rootId) {
835
445
  const root = document.querySelector(`#${rootId}`);
836
- const body = document.querySelector("body");
837
446
  if (!root) return;
838
- body.style.backgroundColor = "#000";
447
+ const body = document.querySelector("body");
448
+ body.style.backgroundColor = "#000000";
839
449
  root.style.overflow = "hidden";
840
450
  root.style.transitionTimingFunction = "cubic-bezier(0.32, 0.72, 0, 1)";
841
451
  root.style.transitionProperty = "transform, border-radius";
842
452
  root.style.transitionDuration = "0.5s";
843
453
  root.style.transformOrigin = "center top";
844
454
  }
455
+ function updateModalEffect(rootId, opts) {
456
+ const root = document.querySelector(`#${rootId}`);
457
+ if (!root) return;
458
+ const { scale, translateY, borderRadius } = opts;
459
+ root.style.transform = `scale(${scale}) translate3d(0, ${translateY}px, 0)`;
460
+ root.style.borderTopRightRadius = `${borderRadius}px`;
461
+ root.style.borderTopLeftRadius = `${borderRadius}px`;
462
+ }
845
463
  function cleanupModalEffect(rootId) {
846
464
  const root = document.querySelector(`#${rootId}`);
847
- const body = document.querySelector("body");
848
465
  if (!root) return;
466
+ const body = document.querySelector("body");
849
467
  body.style.removeProperty("background-color");
850
468
  root.style.removeProperty("overflow");
851
469
  root.style.removeProperty("transition-timing-function");
@@ -1065,19 +683,19 @@ function useSheetState({
1065
683
  onOpen: _onOpen,
1066
684
  onClosing: _onClosing
1067
685
  }) {
1068
- const [state, setState] = React4.useState(isOpen ? "opening" : "closed");
686
+ const [state, setState] = React.useState(isOpen ? "opening" : "closed");
1069
687
  const onClosed = useStableCallback(() => _onClosed == null ? void 0 : _onClosed());
1070
688
  const onOpening = useStableCallback(() => _onOpening == null ? void 0 : _onOpening());
1071
689
  const onOpen = useStableCallback(() => _onOpen == null ? void 0 : _onOpen());
1072
690
  const onClosing = useStableCallback(() => _onClosing == null ? void 0 : _onClosing());
1073
- React4.useEffect(() => {
691
+ React.useEffect(() => {
1074
692
  if (isOpen && state === "closed") {
1075
693
  setState("opening");
1076
694
  } else if (!isOpen && (state === "open" || state === "opening")) {
1077
695
  setState("closing");
1078
696
  }
1079
697
  }, [isOpen, state]);
1080
- React4.useEffect(() => {
698
+ React.useEffect(() => {
1081
699
  async function handle() {
1082
700
  switch (state) {
1083
701
  case "closed":
@@ -1157,78 +775,251 @@ function computeSnapPoints({
1157
775
  // Y value is inverted as `y = 0` means sheet is at the top
1158
776
  }));
1159
777
  }
1160
- function findClosestSnapPoint({
1161
- snapPoints,
1162
- currentY
1163
- }) {
778
+ function sign(value) {
779
+ if (value > 0) return 1;
780
+ if (value < 0) return -1;
781
+ return 0;
782
+ }
783
+ function toAscendingYSnaps(snapPoints) {
784
+ return snapPoints.slice().sort((a, b) => a.snapValueY - b.snapValueY);
785
+ }
786
+ function findNearestSnapByY(snapPoints, y) {
1164
787
  return snapPoints.reduce(
1165
- (closest, snap) => Math.abs(snap.snapValueY - currentY) < Math.abs(closest.snapValueY - currentY) ? snap : closest
788
+ (closest, snap) => Math.abs(snap.snapValueY - y) < Math.abs(closest.snapValueY - y) ? snap : closest
1166
789
  );
1167
790
  }
1168
- function findNextSnapPointInDirection({
791
+ function findNearestSnapAboveY(snapPoints, y) {
792
+ const snaps = toAscendingYSnaps(snapPoints);
793
+ for (let i = snaps.length - 1; i >= 0; i--) {
794
+ if (snaps[i].snapValueY < y) {
795
+ return snaps[i];
796
+ }
797
+ }
798
+ return null;
799
+ }
800
+ function findNearestSnapBelowY(snapPoints, y) {
801
+ const snaps = toAscendingYSnaps(snapPoints);
802
+ for (let i = 0; i < snaps.length; i++) {
803
+ if (snaps[i].snapValueY > y) {
804
+ return snaps[i];
805
+ }
806
+ }
807
+ return null;
808
+ }
809
+ function resolveDirection({
810
+ offsetY,
811
+ velocityY,
812
+ deltaY
813
+ }) {
814
+ if (Math.abs(velocityY) > 50) return sign(velocityY);
815
+ const offsetDirection = sign(offsetY);
816
+ if (offsetDirection !== 0) return offsetDirection;
817
+ return sign(deltaY);
818
+ }
819
+ function classifyDragEnd({
1169
820
  y,
821
+ info,
822
+ sheetHeight,
1170
823
  snapPoints,
1171
- dragDirection
824
+ dragVelocityThreshold,
825
+ dragCloseThreshold
1172
826
  }) {
1173
- if (dragDirection === "down") {
1174
- return snapPoints.slice().reverse().find((s) => s.snapValueY > y);
1175
- } else {
1176
- return snapPoints.find((s) => s.snapValueY < y);
827
+ const flickVelocity = dragVelocityThreshold;
828
+ const strongFlickVelocity = Math.max(2e3, flickVelocity);
829
+ const distanceCommitRatio = 0.35;
830
+ const predictionTime = 0.2;
831
+ const minDragDistance = 20;
832
+ const snapStickiness = 24;
833
+ const minY = 0;
834
+ const maxY = sheetHeight;
835
+ const offsetY = info.offset.y;
836
+ const deltaY = info.delta.y;
837
+ const velocityY = info.velocity.y;
838
+ const predictedY = clamp(y + velocityY * predictionTime, minY, maxY);
839
+ const absVelocity = Math.abs(velocityY);
840
+ const direction = resolveDirection({ offsetY, velocityY, deltaY });
841
+ const isStrongFlick = absVelocity > strongFlickVelocity;
842
+ const isFlick = absVelocity > flickVelocity;
843
+ if (snapPoints.length === 0) {
844
+ if (isFlick) {
845
+ return {
846
+ yTo: direction < 0 ? minY : maxY,
847
+ snapIndex: void 0
848
+ };
849
+ }
850
+ const midpoint = minY + (maxY - minY) * dragCloseThreshold;
851
+ return {
852
+ yTo: predictedY < midpoint ? minY : maxY,
853
+ snapIndex: void 0
854
+ };
1177
855
  }
1178
- }
1179
- function handleHighVelocityDrag({
1180
- dragDirection,
1181
- snapPoints
1182
- }) {
1183
- const bottomSnapPoint = snapPoints[0];
1184
- const topSnapPoint = snapPoints[snapPoints.length - 1];
1185
- if (dragDirection === "down") {
856
+ const nearestSnap = findNearestSnapByY(snapPoints, y);
857
+ if (!isFlick && Math.abs(offsetY) < minDragDistance) {
1186
858
  return {
1187
- yTo: bottomSnapPoint.snapValueY,
1188
- snapIndex: bottomSnapPoint.snapIndex
859
+ yTo: nearestSnap.snapValueY,
860
+ snapIndex: nearestSnap.snapIndex
1189
861
  };
1190
862
  }
1191
- return {
1192
- yTo: topSnapPoint.snapValueY,
1193
- snapIndex: topSnapPoint.snapIndex
1194
- };
1195
- }
1196
- function handleLowVelocityDrag({
1197
- currentSnapPoint,
1198
- currentY,
1199
- dragDirection,
1200
- snapPoints,
1201
- velocity
1202
- }) {
1203
- const closestSnapRelativeToCurrentY = findClosestSnapPoint({
1204
- snapPoints,
1205
- currentY
1206
- });
1207
- if (Math.abs(velocity) < 20) {
863
+ if (!isFlick && Math.abs(y - nearestSnap.snapValueY) < snapStickiness) {
1208
864
  return {
1209
- yTo: closestSnapRelativeToCurrentY.snapValueY,
1210
- snapIndex: closestSnapRelativeToCurrentY.snapIndex
865
+ yTo: nearestSnap.snapValueY,
866
+ snapIndex: nearestSnap.snapIndex
1211
867
  };
1212
868
  }
1213
- const nextSnapInDirectionRelativeToCurrentY = findNextSnapPointInDirection({
1214
- y: currentY,
1215
- snapPoints,
1216
- dragDirection
1217
- });
1218
- if (nextSnapInDirectionRelativeToCurrentY) {
869
+ if (isStrongFlick) {
870
+ const target = direction < 0 ? minY : maxY;
871
+ const targetSnap = findNearestSnapByY(snapPoints, target);
872
+ return {
873
+ yTo: target,
874
+ snapIndex: targetSnap.snapIndex
875
+ };
876
+ }
877
+ if (isFlick) {
878
+ const nextSnap2 = direction < 0 ? findNearestSnapAboveY(snapPoints, y) : findNearestSnapBelowY(snapPoints, y);
879
+ const fallbackY = direction < 0 ? minY : maxY;
880
+ const fallbackSnap = findNearestSnapByY(snapPoints, fallbackY);
881
+ const targetSnap = nextSnap2 ?? fallbackSnap;
882
+ return {
883
+ yTo: targetSnap.snapValueY,
884
+ snapIndex: targetSnap.snapIndex
885
+ };
886
+ }
887
+ const firstSnap = toAscendingYSnaps(snapPoints)[0];
888
+ const lastSnap = toAscendingYSnaps(snapPoints)[snapPoints.length - 1];
889
+ const prevSnap = findNearestSnapAboveY(snapPoints, y) ?? firstSnap;
890
+ const nextSnap = findNearestSnapBelowY(snapPoints, y) ?? lastSnap;
891
+ if (prevSnap.snapIndex === nextSnap.snapIndex) {
1219
892
  return {
1220
- yTo: nextSnapInDirectionRelativeToCurrentY.snapValueY,
1221
- snapIndex: nextSnapInDirectionRelativeToCurrentY.snapIndex
893
+ yTo: prevSnap.snapValueY,
894
+ snapIndex: prevSnap.snapIndex
1222
895
  };
1223
896
  }
897
+ const range = nextSnap.snapValueY - prevSnap.snapValueY;
898
+ const progress = range > 0 ? (y - prevSnap.snapValueY) / range : 0;
899
+ let selectedSnap = nearestSnap;
900
+ if (direction > 0) {
901
+ selectedSnap = progress > distanceCommitRatio ? nextSnap : prevSnap;
902
+ } else if (direction < 0) {
903
+ selectedSnap = progress < 1 - distanceCommitRatio ? prevSnap : nextSnap;
904
+ }
905
+ if (absVelocity > 200) {
906
+ const predictedSnap = findNearestSnapByY(snapPoints, predictedY);
907
+ if (Math.abs(predictedSnap.snapValueY - y) < Math.abs(selectedSnap.snapValueY - y)) {
908
+ selectedSnap = predictedSnap;
909
+ }
910
+ }
1224
911
  return {
1225
- yTo: currentSnapPoint.snapValueY,
1226
- snapIndex: currentSnapPoint.snapIndex
912
+ yTo: selectedSnap.snapValueY,
913
+ snapIndex: selectedSnap.snapIndex
1227
914
  };
1228
915
  }
1229
916
 
917
+ // src/styles.ts
918
+ var styles = {
919
+ root: {
920
+ base: {
921
+ position: "fixed",
922
+ top: 0,
923
+ bottom: 0,
924
+ left: 0,
925
+ right: 0,
926
+ overflow: "hidden",
927
+ pointerEvents: "none"
928
+ },
929
+ decorative: {}
930
+ },
931
+ backdrop: {
932
+ base: {
933
+ zIndex: 1,
934
+ position: "fixed",
935
+ top: 0,
936
+ left: 0,
937
+ width: "100%",
938
+ height: "100%",
939
+ touchAction: "none",
940
+ userSelect: "none"
941
+ },
942
+ decorative: {
943
+ backgroundColor: "rgba(0, 0, 0, 0.2)",
944
+ border: "none",
945
+ WebkitTapHighlightColor: "transparent"
946
+ }
947
+ },
948
+ container: {
949
+ base: {
950
+ zIndex: 2,
951
+ position: "absolute",
952
+ left: 0,
953
+ bottom: 0,
954
+ width: "100%",
955
+ pointerEvents: "auto",
956
+ display: "flex",
957
+ flexDirection: "column"
958
+ },
959
+ decorative: {
960
+ backgroundColor: "#fff",
961
+ borderTopRightRadius: "8px",
962
+ borderTopLeftRadius: "8px",
963
+ boxShadow: "0px -2px 16px rgba(0, 0, 0, 0.3)"
964
+ }
965
+ },
966
+ headerWrapper: {
967
+ base: {
968
+ width: "100%"
969
+ },
970
+ decorative: {}
971
+ },
972
+ header: {
973
+ base: {
974
+ width: "100%",
975
+ position: "relative"
976
+ },
977
+ decorative: {
978
+ height: "40px",
979
+ display: "flex",
980
+ alignItems: "center",
981
+ justifyContent: "center"
982
+ }
983
+ },
984
+ indicatorWrapper: {
985
+ base: {
986
+ display: "flex"
987
+ },
988
+ decorative: {}
989
+ },
990
+ indicator: {
991
+ base: {
992
+ display: "inline-block"
993
+ },
994
+ decorative: {
995
+ width: "18px",
996
+ height: "4px",
997
+ borderRadius: "99px",
998
+ backgroundColor: "#ddd"
999
+ }
1000
+ },
1001
+ content: {
1002
+ base: {
1003
+ minHeight: "0px",
1004
+ position: "relative",
1005
+ flexGrow: 1,
1006
+ display: "flex",
1007
+ flexDirection: "column"
1008
+ },
1009
+ decorative: {}
1010
+ },
1011
+ scroller: {
1012
+ base: {
1013
+ height: "100%",
1014
+ overflowY: "auto",
1015
+ overscrollBehaviorY: "none"
1016
+ },
1017
+ decorative: {}
1018
+ }
1019
+ };
1020
+
1230
1021
  // src/sheet.tsx
1231
- var Sheet = React4.forwardRef(
1022
+ var Sheet = React.forwardRef(
1232
1023
  ({
1233
1024
  avoidKeyboard = true,
1234
1025
  children,
@@ -1261,14 +1052,14 @@ var Sheet = React4.forwardRef(
1261
1052
  ...rest
1262
1053
  }, ref) => {
1263
1054
  const [sheetBoundsRef, sheetBounds] = useMeasure__default.default();
1264
- const sheetRef = React4.useRef(null);
1055
+ const sheetRef = React.useRef(null);
1265
1056
  const sheetHeight = Math.round(sheetBounds.height);
1266
- const [currentSnap, setCurrentSnap] = React4.useState(initialSnap);
1267
- const snapPoints = snapPointsProp && sheetHeight > 0 ? computeSnapPoints({ sheetHeight, snapPointsProp }) : [];
1057
+ const [currentSnap, setCurrentSnap] = React.useState(initialSnap);
1268
1058
  const { windowHeight } = useDimensions();
1269
1059
  const closedY = sheetHeight > 0 ? sheetHeight : windowHeight;
1270
1060
  const y = react.useMotionValue(closedY);
1271
1061
  const yInverted = react.useTransform(y, (val) => Math.max(sheetHeight - val, 0));
1062
+ const yProgress = react.useTransform(y, (val) => clamp(1 - val / closedY, 0, 1));
1272
1063
  const indicatorRotation = react.useMotionValue(0);
1273
1064
  const shouldReduceMotion = react.useReducedMotion();
1274
1065
  const reduceMotion = Boolean(prefersReducedMotion || shouldReduceMotion);
@@ -1284,6 +1075,9 @@ var Sheet = React4.forwardRef(
1284
1075
  y,
1285
1076
  (val) => val + 2 >= closedY ? "hidden" : "visible"
1286
1077
  );
1078
+ const snapPoints = React.useMemo(() => {
1079
+ return snapPointsProp && sheetHeight > 0 ? computeSnapPoints({ sheetHeight, snapPointsProp }) : [];
1080
+ }, [sheetHeight, snapPointsProp == null ? void 0 : snapPointsProp.join("")]);
1287
1081
  const updateSnap = useStableCallback((snapIndex) => {
1288
1082
  setCurrentSnap(snapIndex);
1289
1083
  onSnap == null ? void 0 : onSnap(snapIndex);
@@ -1370,45 +1164,25 @@ var Sheet = React4.forwardRef(
1370
1164
  blurActiveInput();
1371
1165
  onDragEndProp == null ? void 0 : onDragEndProp(event, info);
1372
1166
  const currentY = y.get();
1373
- let yTo = 0;
1374
- const currentSnapPoint = currentSnap !== void 0 ? getSnapPoint(currentSnap) : null;
1375
- if (currentSnapPoint) {
1376
- const dragOffsetDirection = info.offset.y > 0 ? "down" : "up";
1377
- const dragVelocityDirection = info.velocity.y > 0 ? "down" : "up";
1378
- const isHighVelocity = Math.abs(info.velocity.y) > dragVelocityThreshold;
1379
- let result;
1380
- if (isHighVelocity) {
1381
- result = handleHighVelocityDrag({
1382
- snapPoints,
1383
- dragDirection: dragVelocityDirection
1384
- });
1385
- } else {
1386
- result = handleLowVelocityDrag({
1387
- currentSnapPoint,
1388
- currentY,
1389
- dragDirection: dragOffsetDirection,
1390
- snapPoints,
1391
- velocity: info.velocity.y
1392
- });
1393
- }
1394
- yTo = result.yTo;
1395
- if (disableDismiss && yTo + 1 >= sheetHeight) {
1396
- const bottomSnapPoint = snapPoints.find((s) => s.snapValue > 0);
1397
- if (bottomSnapPoint) {
1398
- yTo = bottomSnapPoint.snapValueY;
1399
- updateSnap(bottomSnapPoint.snapIndex);
1400
- } else {
1401
- yTo = currentY;
1402
- }
1403
- } else if (result.snapIndex !== void 0) {
1404
- updateSnap(result.snapIndex);
1405
- }
1406
- } else if (info.velocity.y > dragVelocityThreshold || currentY > sheetHeight * dragCloseThreshold) {
1407
- if (disableDismiss) {
1408
- yTo = 0;
1167
+ const result = classifyDragEnd({
1168
+ y: currentY,
1169
+ info,
1170
+ sheetHeight,
1171
+ dragCloseThreshold,
1172
+ snapPoints,
1173
+ dragVelocityThreshold
1174
+ });
1175
+ let yTo = result.yTo;
1176
+ if (disableDismiss && yTo + 1 >= sheetHeight) {
1177
+ const bottomSnapPoint = snapPoints.find((s) => s.snapValue > 0);
1178
+ if (bottomSnapPoint) {
1179
+ yTo = bottomSnapPoint.snapValueY;
1180
+ updateSnap(bottomSnapPoint.snapIndex);
1409
1181
  } else {
1410
- yTo = closedY;
1182
+ yTo = currentY;
1411
1183
  }
1184
+ } else if (result.snapIndex !== void 0) {
1185
+ updateSnap(result.snapIndex);
1412
1186
  }
1413
1187
  react.animate(y, yTo, animationOptions);
1414
1188
  if (yTo + 1 >= sheetHeight && !disableDismiss) {
@@ -1416,12 +1190,6 @@ var Sheet = React4.forwardRef(
1416
1190
  }
1417
1191
  indicatorRotation.set(0);
1418
1192
  });
1419
- React4.useImperativeHandle(ref, () => ({
1420
- y,
1421
- yInverted,
1422
- height: sheetHeight,
1423
- snapTo
1424
- }));
1425
1193
  useModalEffect({
1426
1194
  y,
1427
1195
  detent,
@@ -1441,9 +1209,7 @@ var Sheet = React4.forwardRef(
1441
1209
  const initialSnapPoint = initialSnap !== void 0 ? getSnapPoint(initialSnap) : null;
1442
1210
  const yTo = (initialSnapPoint == null ? void 0 : initialSnapPoint.snapValueY) ?? 0;
1443
1211
  await react.animate(y, yTo, animationOptions);
1444
- if (initialSnap !== void 0) {
1445
- updateSnap(initialSnap);
1446
- }
1212
+ if (initialSnap !== void 0) updateSnap(initialSnap);
1447
1213
  onOpenEnd == null ? void 0 : onOpenEnd();
1448
1214
  },
1449
1215
  onClosing: async () => {
@@ -1461,7 +1227,7 @@ var Sheet = React4.forwardRef(
1461
1227
  onDragStart,
1462
1228
  onDragEnd
1463
1229
  };
1464
- const context = {
1230
+ const internalContext = {
1465
1231
  currentSnap,
1466
1232
  detent,
1467
1233
  disableDrag,
@@ -1472,9 +1238,20 @@ var Sheet = React4.forwardRef(
1472
1238
  sheetBoundsRef,
1473
1239
  sheetRef,
1474
1240
  unstyled,
1241
+ yProgress,
1475
1242
  y
1476
1243
  };
1477
- const sheet = /* @__PURE__ */ React4__default.default.createElement(SheetContext.Provider, { value: context }, /* @__PURE__ */ React4__default.default.createElement(
1244
+ const exposedContext = {
1245
+ height: sheetHeight,
1246
+ currentSnap,
1247
+ snapPoints,
1248
+ snapTo,
1249
+ y,
1250
+ yInverted,
1251
+ yProgress
1252
+ };
1253
+ React.useImperativeHandle(ref, () => exposedContext);
1254
+ const sheet = /* @__PURE__ */ React__default.default.createElement(InternalSheetContext.Provider, { value: internalContext }, /* @__PURE__ */ React__default.default.createElement(
1478
1255
  react.motion.div,
1479
1256
  {
1480
1257
  ...rest,
@@ -1488,13 +1265,293 @@ var Sheet = React4.forwardRef(
1488
1265
  ...style
1489
1266
  }
1490
1267
  },
1491
- state !== "closed" ? children : null
1268
+ /* @__PURE__ */ React__default.default.createElement(ExposedSheetContext.Provider, { value: exposedContext }, state !== "closed" ? children : null)
1492
1269
  ));
1493
1270
  if (IS_SSR) return sheet;
1494
1271
  return reactDom.createPortal(sheet, mountPoint ?? document.body);
1495
1272
  }
1496
1273
  );
1497
1274
  Sheet.displayName = "Sheet";
1275
+ var isClickable = (props) => !!props.onClick || !!props.onTap;
1276
+ var SheetBackdrop = React.forwardRef(
1277
+ ({ style, className = "", unstyled, ...rest }, ref) => {
1278
+ const sheetContext = useInternalContext();
1279
+ const clickable = isClickable(rest);
1280
+ const Comp = clickable ? react.motion.button : react.motion.div;
1281
+ const pointerEvents = clickable ? "auto" : "none";
1282
+ const isUnstyled = unstyled ?? sheetContext.unstyled;
1283
+ const backdropStyle = {
1284
+ opacity: sheetContext.yProgress,
1285
+ ...applyStyles(styles.backdrop, isUnstyled),
1286
+ ...style,
1287
+ pointerEvents
1288
+ };
1289
+ return /* @__PURE__ */ React__default.default.createElement(
1290
+ Comp,
1291
+ {
1292
+ ...rest,
1293
+ ref,
1294
+ className: `react-modal-sheet-backdrop ${className}`,
1295
+ style: backdropStyle
1296
+ }
1297
+ );
1298
+ }
1299
+ );
1300
+ SheetBackdrop.displayName = "SheetBackdrop";
1301
+ var SheetContainer = React.forwardRef(
1302
+ ({ children, style, className = "", unstyled, ...rest }, ref) => {
1303
+ const sheetContext = useInternalContext();
1304
+ const isUnstyled = unstyled ?? sheetContext.unstyled;
1305
+ const containerStyle = {
1306
+ ...applyStyles(styles.container, isUnstyled),
1307
+ ...style,
1308
+ y: sheetContext.y
1309
+ };
1310
+ if (sheetContext.detent === "default") {
1311
+ containerStyle.height = DEFAULT_HEIGHT;
1312
+ }
1313
+ if (sheetContext.detent === "full") {
1314
+ containerStyle.height = "100%";
1315
+ containerStyle.maxHeight = "100%";
1316
+ }
1317
+ if (sheetContext.detent === "content") {
1318
+ containerStyle.height = "auto";
1319
+ containerStyle.maxHeight = DEFAULT_HEIGHT;
1320
+ }
1321
+ return /* @__PURE__ */ React__default.default.createElement(
1322
+ react.motion.div,
1323
+ {
1324
+ ...rest,
1325
+ ref: mergeRefs([
1326
+ ref,
1327
+ sheetContext.sheetRef,
1328
+ sheetContext.sheetBoundsRef
1329
+ ]),
1330
+ className: `react-modal-sheet-container ${className}`,
1331
+ style: containerStyle
1332
+ },
1333
+ children
1334
+ );
1335
+ }
1336
+ );
1337
+ SheetContainer.displayName = "SheetContainer";
1338
+ var constraints = { bottom: 0, top: 0, left: 0, right: 0 };
1339
+ function useDragConstraints() {
1340
+ const ref = React.useRef(null);
1341
+ const onMeasure = React.useCallback(() => constraints, []);
1342
+ return { ref, onMeasure };
1343
+ }
1344
+ function useScrollPosition(options = {}) {
1345
+ const { debounceDelay = 32, isEnabled = true } = options;
1346
+ const scrollTimeoutRef = React.useRef(null);
1347
+ const [element, setElement] = React.useState(null);
1348
+ const [scrollPosition, setScrollPosition] = React.useState(void 0);
1349
+ const scrollRef = React.useMemo(
1350
+ () => (element2) => {
1351
+ setElement(element2);
1352
+ },
1353
+ []
1354
+ );
1355
+ const determineScrollPosition = useStableCallback((element2) => {
1356
+ const { scrollTop, scrollHeight, clientHeight } = element2;
1357
+ const isScrollable2 = scrollHeight > clientHeight;
1358
+ if (!isScrollable2) {
1359
+ if (scrollPosition) setScrollPosition(void 0);
1360
+ return;
1361
+ }
1362
+ const isAtTop = scrollTop <= 0;
1363
+ const isAtBottom = Math.ceil(scrollHeight) - Math.ceil(scrollTop) === Math.ceil(clientHeight);
1364
+ let position;
1365
+ if (isAtTop) {
1366
+ position = "top";
1367
+ } else if (isAtBottom) {
1368
+ position = "bottom";
1369
+ } else {
1370
+ position = "middle";
1371
+ }
1372
+ element2.style.touchAction = isAtTop ? "pan-down" : "";
1373
+ if (position === scrollPosition) return;
1374
+ setScrollPosition(position);
1375
+ });
1376
+ const onScroll = useStableCallback((event) => {
1377
+ if (event.currentTarget instanceof HTMLElement) {
1378
+ const el = event.currentTarget;
1379
+ if (scrollTimeoutRef.current) clearTimeout(scrollTimeoutRef.current);
1380
+ if (debounceDelay === 0) {
1381
+ determineScrollPosition(el);
1382
+ } else {
1383
+ scrollTimeoutRef.current = setTimeout(
1384
+ () => determineScrollPosition(el),
1385
+ debounceDelay
1386
+ );
1387
+ }
1388
+ }
1389
+ });
1390
+ const onTouchStart = useStableCallback((event) => {
1391
+ if (event.currentTarget instanceof HTMLElement) {
1392
+ const element2 = event.currentTarget;
1393
+ requestAnimationFrame(() => {
1394
+ determineScrollPosition(element2);
1395
+ });
1396
+ }
1397
+ });
1398
+ React.useEffect(() => {
1399
+ if (!element || !isEnabled) return;
1400
+ determineScrollPosition(element);
1401
+ element.addEventListener("scroll", onScroll);
1402
+ element.addEventListener("touchstart", onTouchStart);
1403
+ return () => {
1404
+ if (scrollTimeoutRef.current) clearTimeout(scrollTimeoutRef.current);
1405
+ element.removeEventListener("scroll", onScroll);
1406
+ element.removeEventListener("touchstart", onTouchStart);
1407
+ };
1408
+ }, [element, isEnabled]);
1409
+ return {
1410
+ scrollRef,
1411
+ scrollPosition
1412
+ };
1413
+ }
1414
+
1415
+ // src/sheet-content.tsx
1416
+ var SheetContent = React.forwardRef(
1417
+ ({
1418
+ disableScroll: disableScrollProp,
1419
+ disableDrag: disableDragProp,
1420
+ children,
1421
+ style: styleProp,
1422
+ className = "",
1423
+ scrollClassName = "",
1424
+ scrollStyle: scrollStyleProp,
1425
+ scrollRef: scrollRefProp = null,
1426
+ unstyled,
1427
+ ...rest
1428
+ }, ref) => {
1429
+ const sheetContext = useInternalContext();
1430
+ const dragConstraints = useDragConstraints();
1431
+ const scroll = useScrollPosition({
1432
+ isEnabled: typeof disableScrollProp === "function" ? true : !disableScrollProp
1433
+ });
1434
+ const disableScroll = typeof disableScrollProp === "function" ? disableScrollProp({
1435
+ scrollPosition: scroll.scrollPosition,
1436
+ currentSnap: sheetContext.currentSnap
1437
+ }) : Boolean(disableScrollProp);
1438
+ const disableDragDueToScroll = !disableScroll && scroll.scrollPosition && scroll.scrollPosition !== "top";
1439
+ const disableDragDueToProp = typeof disableDragProp === "function" ? disableDragProp({
1440
+ scrollPosition: scroll.scrollPosition,
1441
+ currentSnap: sheetContext.currentSnap
1442
+ }) : Boolean(disableDragProp);
1443
+ const disableDrag = disableDragDueToProp || disableDragDueToScroll || sheetContext.disableDrag;
1444
+ const dragProps = disableDrag || sheetContext.disableDrag ? void 0 : sheetContext.dragProps;
1445
+ const isUnstyled = unstyled ?? sheetContext.unstyled;
1446
+ const contentStyle = {
1447
+ ...applyStyles(styles.content, isUnstyled),
1448
+ ...styleProp
1449
+ };
1450
+ const scrollStyle = applyStyles(styles.scroller, isUnstyled);
1451
+ if (sheetContext.avoidKeyboard) {
1452
+ scrollStyle.paddingBottom = isHTTPS() ? "env(keyboard-inset-height, var(--keyboard-inset-height, 0px))" : "var(--keyboard-inset-height, 0px)";
1453
+ }
1454
+ if (disableScroll) {
1455
+ scrollStyle.overflowY = "clip";
1456
+ }
1457
+ if (dragProps !== void 0) {
1458
+ scrollStyle.touchAction = "pan-down";
1459
+ }
1460
+ return /* @__PURE__ */ React__default.default.createElement(
1461
+ react.motion.div,
1462
+ {
1463
+ ...rest,
1464
+ ref: mergeRefs([ref, dragConstraints.ref]),
1465
+ className: `react-modal-sheet-content ${className}`,
1466
+ style: contentStyle,
1467
+ ...dragProps,
1468
+ dragConstraints: dragConstraints.ref,
1469
+ onMeasureDragConstraints: dragConstraints.onMeasure
1470
+ },
1471
+ /* @__PURE__ */ React__default.default.createElement(
1472
+ react.motion.div,
1473
+ {
1474
+ ref: mergeRefs([scroll.scrollRef, scrollRefProp]),
1475
+ style: { ...scrollStyle, ...scrollStyleProp },
1476
+ className: `react-modal-sheet-content-scroller ${scrollClassName}`
1477
+ },
1478
+ children
1479
+ )
1480
+ );
1481
+ }
1482
+ );
1483
+ SheetContent.displayName = "SheetContent";
1484
+ function SheetDragIndicator({
1485
+ style,
1486
+ className = "",
1487
+ unstyled,
1488
+ ...rest
1489
+ }) {
1490
+ const sheetContext = useInternalContext();
1491
+ const indicator1Transform = react.useTransform(
1492
+ sheetContext.indicatorRotation,
1493
+ (r) => `translateX(2px) rotate(${r}deg)`
1494
+ );
1495
+ const indicator2Transform = react.useTransform(
1496
+ sheetContext.indicatorRotation,
1497
+ (r) => `translateX(-2px) rotate(${ -1 * r}deg)`
1498
+ );
1499
+ const isUnstyled = unstyled ?? sheetContext.unstyled;
1500
+ const indicatorWrapperStyle = {
1501
+ ...applyStyles(styles.indicatorWrapper, isUnstyled),
1502
+ ...style
1503
+ };
1504
+ const indicatorStyle = applyStyles(styles.indicator, isUnstyled);
1505
+ return /* @__PURE__ */ React__default.default.createElement(
1506
+ "div",
1507
+ {
1508
+ className: `react-modal-sheet-drag-indicator-container ${className}`,
1509
+ style: indicatorWrapperStyle,
1510
+ ...rest
1511
+ },
1512
+ /* @__PURE__ */ React__default.default.createElement(
1513
+ react.motion.span,
1514
+ {
1515
+ className: "react-modal-sheet-drag-indicator",
1516
+ style: { ...indicatorStyle, transform: indicator1Transform }
1517
+ }
1518
+ ),
1519
+ /* @__PURE__ */ React__default.default.createElement(
1520
+ react.motion.span,
1521
+ {
1522
+ className: "react-modal-sheet-drag-indicator",
1523
+ style: { ...indicatorStyle, transform: indicator2Transform }
1524
+ }
1525
+ )
1526
+ );
1527
+ }
1528
+ var SheetHeader = React.forwardRef(
1529
+ ({ children, style, disableDrag, unstyled, className = "", ...rest }, ref) => {
1530
+ const sheetContext = useInternalContext();
1531
+ const dragConstraints = useDragConstraints();
1532
+ const dragProps = disableDrag || sheetContext.disableDrag ? void 0 : sheetContext.dragProps;
1533
+ const isUnstyled = unstyled ?? sheetContext.unstyled;
1534
+ const headerWrapperStyle = {
1535
+ ...applyStyles(styles.headerWrapper, isUnstyled),
1536
+ ...style
1537
+ };
1538
+ const headerStyle = applyStyles(styles.header, isUnstyled);
1539
+ return /* @__PURE__ */ React__default.default.createElement(
1540
+ react.motion.div,
1541
+ {
1542
+ ...rest,
1543
+ ref: mergeRefs([ref, dragConstraints.ref]),
1544
+ style: headerWrapperStyle,
1545
+ className: `react-modal-sheet-header-container ${className}`,
1546
+ ...dragProps,
1547
+ dragConstraints: dragConstraints.ref,
1548
+ onMeasureDragConstraints: dragConstraints.onMeasure
1549
+ },
1550
+ children || /* @__PURE__ */ React__default.default.createElement("div", { className: "react-modal-sheet-header", style: headerStyle }, /* @__PURE__ */ React__default.default.createElement(SheetDragIndicator, null))
1551
+ );
1552
+ }
1553
+ );
1554
+ SheetHeader.displayName = "SheetHeader";
1498
1555
 
1499
1556
  // src/index.tsx
1500
1557
  var Sheet2 = Object.assign(Sheet, {
@@ -1502,7 +1559,8 @@ var Sheet2 = Object.assign(Sheet, {
1502
1559
  Header: SheetHeader,
1503
1560
  DragIndicator: SheetDragIndicator,
1504
1561
  Content: SheetContent,
1505
- Backdrop: SheetBackdrop
1562
+ Backdrop: SheetBackdrop,
1563
+ useContext: useExposedContext
1506
1564
  });
1507
1565
 
1508
1566
  exports.Sheet = Sheet2;