react-modal-sheet 4.0.1 → 4.1.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,8 +1,9 @@
1
1
  'use strict';
2
2
 
3
- var React2 = require('react');
4
3
  var reactDom = require('react-dom');
4
+ var React2 = require('react');
5
5
  var react = require('motion/react');
6
+ var motion = require('motion');
6
7
 
7
8
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
8
9
 
@@ -24,182 +25,33 @@ var REDUCED_MOTION_TWEEN_CONFIG = {
24
25
  var DEFAULT_DRAG_CLOSE_THRESHOLD = 0.6;
25
26
  var DEFAULT_DRAG_VELOCITY_THRESHOLD = 500;
26
27
 
27
- // src/hooks.tsx
28
- var useIsomorphicLayoutEffect = IS_SSR ? React2.useEffect : React2.useLayoutEffect;
29
- function useModalEffect({
30
- y,
31
- rootId,
32
- sheetRef
33
- }) {
34
- const heightRef = React2.useRef(IS_SSR ? 0 : window.innerHeight);
35
- function setup() {
36
- const root = document.querySelector(`#${rootId}`);
37
- const body = document.querySelector("body");
38
- if (!root) return;
39
- body.style.backgroundColor = "#000";
40
- root.style.overflow = "hidden";
41
- root.style.transitionTimingFunction = "cubic-bezier(0.32, 0.72, 0, 1)";
42
- root.style.transitionProperty = "transform, border-radius";
43
- root.style.transitionDuration = "0.5s";
44
- root.style.transformOrigin = "center top";
45
- }
46
- function cleanup() {
47
- const root = document.querySelector(`#${rootId}`);
48
- const body = document.querySelector("body");
49
- if (!root) return;
50
- setTimeout(() => {
51
- body.style.removeProperty("background-color");
52
- root.style.removeProperty("overflow");
53
- root.style.removeProperty("transition-timing-function");
54
- root.style.removeProperty("transition-property");
55
- root.style.removeProperty("transition-duration");
56
- root.style.removeProperty("transform-origin");
57
- root.style.removeProperty("transform");
58
- root.style.removeProperty("border-top-right-radius");
59
- root.style.removeProperty("border-top-left-radius");
60
- }, 100);
28
+ // src/utils.ts
29
+ function getSheetHeight(sheetRef) {
30
+ const sheetEl = sheetRef.current;
31
+ if (!sheetEl) {
32
+ console.warn(
33
+ "Sheet height is not available because the sheet element is not mounted yet."
34
+ );
35
+ return 0;
61
36
  }
62
- useIsomorphicLayoutEffect(() => {
63
- return () => {
64
- if (rootId) cleanup();
65
- };
66
- }, []);
67
- React2.useEffect(() => {
68
- const root = document.querySelector(`#${rootId}`);
69
- if (!root) return;
70
- function onCompleted() {
71
- if (y.get() - 10 >= heightRef.current) cleanup();
72
- }
73
- const removeStartListener = y.on("animationStart", () => {
74
- var _a;
75
- heightRef.current = ((_a = sheetRef.current) == null ? void 0 : _a.offsetHeight) || window.innerHeight;
76
- setup();
77
- });
78
- const removeChangeListener = y.on("change", (value) => {
79
- if (root) {
80
- const progress = Math.max(0, 1 - value / heightRef.current);
81
- const pageWidth = window.innerWidth;
82
- const scale = (pageWidth - 16) / pageWidth;
83
- const ty = react.transform(progress, [0, 1], [0, 24]);
84
- const s = react.transform(progress, [0, 1], [1, scale]);
85
- const borderRadius = react.transform(progress, [0, 1], [0, 10]);
86
- const inset = "env(safe-area-inset-top)";
87
- root.style.transform = `scale(${s}) translate3d(0, calc(${inset} + ${ty}px), 0)`;
88
- root.style.borderTopRightRadius = `${borderRadius}px`;
89
- root.style.borderTopLeftRadius = `${borderRadius}px`;
90
- }
91
- });
92
- const removeCompleteListener = y.on("animationComplete", onCompleted);
93
- const removeCancelListener = y.on("animationCancel", onCompleted);
94
- return () => {
95
- removeStartListener();
96
- removeChangeListener();
97
- removeCompleteListener();
98
- removeCancelListener();
99
- };
100
- }, [y, rootId]);
37
+ return Math.round(sheetEl.getBoundingClientRect().height);
101
38
  }
102
- function useEventCallbacks(isOpen, callbacks) {
103
- const prevOpen = usePrevious(isOpen);
104
- const didOpen = React2.useRef(false);
105
- const handleAnimationComplete = React2.useCallback(() => {
106
- var _a, _b, _c, _d;
107
- if (!didOpen.current) {
108
- (_b = (_a = callbacks.current).onOpenEnd) == null ? void 0 : _b.call(_a);
109
- didOpen.current = true;
110
- } else {
111
- (_d = (_c = callbacks.current).onCloseEnd) == null ? void 0 : _d.call(_c);
112
- didOpen.current = false;
113
- }
114
- }, [isOpen, prevOpen]);
115
- React2.useEffect(() => {
116
- var _a, _b, _c, _d;
117
- if (!prevOpen && isOpen) {
118
- (_b = (_a = callbacks.current).onOpenStart) == null ? void 0 : _b.call(_a);
119
- } else if (!isOpen && prevOpen) {
120
- (_d = (_c = callbacks.current).onCloseStart) == null ? void 0 : _d.call(_c);
39
+ function getSnapPoints({
40
+ snapPointsProp,
41
+ sheetHeight
42
+ }) {
43
+ const snapPointValues = snapPointsProp.map((point) => {
44
+ if (point > 0 && point <= 1) {
45
+ return Math.round(point * sheetHeight);
121
46
  }
122
- }, [isOpen, prevOpen]);
123
- return { handleAnimationComplete };
124
- }
125
- function useDimensions() {
126
- const [dimensions, setDimensions] = React2.useState({
127
- height: !IS_SSR ? window.innerHeight : 0,
128
- width: !IS_SSR ? window.innerWidth : 0
47
+ return point < 0 ? sheetHeight + point : point;
129
48
  });
130
- useIsomorphicLayoutEffect(() => {
131
- function handler() {
132
- setDimensions({
133
- height: window.innerHeight,
134
- width: window.innerWidth
135
- });
136
- }
137
- handler();
138
- window.addEventListener("resize", handler);
139
- return () => {
140
- window.removeEventListener("resize", handler);
141
- };
142
- }, []);
143
- return dimensions;
144
- }
145
- function usePrevious(state) {
146
- const ref = React2.useRef(void 0);
147
- React2.useEffect(() => {
148
- ref.current = state;
149
- });
150
- return ref.current;
151
- }
152
- function useEffectEvent(handler) {
153
- const handlerRef = React2.useRef(void 0);
154
- useIsomorphicLayoutEffect(() => {
155
- handlerRef.current = handler;
156
- });
157
- return React2.useCallback((...args) => {
158
- const fn = handlerRef.current;
159
- return fn == null ? void 0 : fn(...args);
160
- }, []);
161
- }
162
- var constraints = { bottom: 0, top: 0, left: 0, right: 0 };
163
- function useDragConstraints() {
164
- const constraintsRef = React2.useRef(null);
165
- const onMeasureDragConstraints = React2.useCallback(() => constraints, []);
166
- return { constraintsRef, onMeasureDragConstraints };
167
- }
168
- var SheetContext = React2.createContext(
169
- void 0
170
- );
171
- var useSheetContext = () => {
172
- const context = React2.useContext(SheetContext);
173
- if (!context) throw new Error("Sheet context error");
174
- return context;
175
- };
176
- var SheetScrollerContext = React2.createContext(void 0);
177
- function SheetScrollerContextProvider({
178
- children
179
- }) {
180
- const sheetContext = useSheetContext();
181
- const [disableDrag, setDisableDrag] = React2.useState(!!sheetContext.disableDrag);
182
- function setDragEnabled() {
183
- if (!sheetContext.disableDrag) setDisableDrag(false);
184
- }
185
- function setDragDisabled() {
186
- if (!disableDrag) setDisableDrag(true);
187
- }
188
- return /* @__PURE__ */ React2__default.default.createElement(
189
- SheetScrollerContext.Provider,
190
- {
191
- value: { disableDrag, setDragEnabled, setDragDisabled }
192
- },
193
- children
49
+ console.assert(
50
+ inDescendingOrder(snapPointValues) || sheetHeight === 0,
51
+ `Snap points need to be in descending order got: [${snapPointsProp.join(", ")}]`
194
52
  );
53
+ return snapPointValues;
195
54
  }
196
- var useSheetScrollerContext = () => {
197
- const context = React2.useContext(SheetScrollerContext);
198
- if (!context) throw new Error("Sheet scroller context error");
199
- return context;
200
- };
201
-
202
- // src/utils.ts
203
55
  function getClosest(nums, goal) {
204
56
  let closest = nums[0];
205
57
  let minDifference = Math.abs(nums[0] - goal);
@@ -218,6 +70,21 @@ function inDescendingOrder(arr) {
218
70
  }
219
71
  return true;
220
72
  }
73
+ function getSnapY({
74
+ sheetHeight,
75
+ snapPoints,
76
+ snapIndex
77
+ }) {
78
+ const snapPoint = snapPoints[snapIndex];
79
+ if (snapPoint === void 0) {
80
+ console.warn(
81
+ `Invalid snap index ${snapIndex}. Snap points are: [${snapPoints.join(", ")}]`
82
+ );
83
+ return null;
84
+ }
85
+ const y = validateSnapTo({ snapTo: sheetHeight - snapPoint, sheetHeight });
86
+ return y;
87
+ }
221
88
  function validateSnapTo({
222
89
  snapTo,
223
90
  sheetHeight
@@ -241,7 +108,7 @@ function mergeRefs(refs) {
241
108
  };
242
109
  }
243
110
  function isTouchDevice() {
244
- if (typeof window === "undefined") return false;
111
+ if (IS_SSR) return false;
245
112
  return "ontouchstart" in window || navigator.maxTouchPoints > 0;
246
113
  }
247
114
  function testPlatform(re) {
@@ -267,14 +134,265 @@ var isIPhone = cached(function() {
267
134
  return testPlatform(/^iPhone/i);
268
135
  });
269
136
  var isIPad = cached(function() {
270
- return testPlatform(/^iPad/i) || // iPadOS 13 lies and says it's a Mac, but we can distinguish by detecting touch support.
271
- isMac() && navigator.maxTouchPoints > 1;
137
+ return testPlatform(/^iPad/i) || isMac() && navigator.maxTouchPoints > 1;
272
138
  });
273
139
  var isIOS = cached(function() {
274
140
  return isIPhone() || isIPad();
275
141
  });
142
+ var SheetContext = React2.createContext(
143
+ void 0
144
+ );
145
+ var useSheetContext = () => {
146
+ const context = React2.useContext(SheetContext);
147
+ if (!context) throw new Error("Sheet context error");
148
+ return context;
149
+ };
150
+ var SheetScrollerContext = React2.createContext(void 0);
151
+ function SheetScrollerContextProvider({
152
+ children
153
+ }) {
154
+ const sheetContext = useSheetContext();
155
+ const [disableDrag, setDisableDrag] = React2.useState(!!sheetContext.disableDrag);
156
+ function setDragEnabled() {
157
+ if (!sheetContext.disableDrag) setDisableDrag(false);
158
+ }
159
+ function setDragDisabled() {
160
+ if (!disableDrag) setDisableDrag(true);
161
+ }
162
+ return /* @__PURE__ */ React2__default.default.createElement(
163
+ SheetScrollerContext.Provider,
164
+ {
165
+ value: { disableDrag, setDragEnabled, setDragDisabled }
166
+ },
167
+ children
168
+ );
169
+ }
170
+ var useSheetScrollerContext = () => {
171
+ const context = React2.useContext(SheetScrollerContext);
172
+ if (!context) throw new Error("Sheet scroller context error");
173
+ return context;
174
+ };
276
175
 
277
- // src/use-prevent-scroll.ts
176
+ // src/styles.ts
177
+ var styles = {
178
+ wrapper: {
179
+ position: "fixed",
180
+ top: 0,
181
+ bottom: 0,
182
+ left: 0,
183
+ right: 0,
184
+ overflow: "hidden",
185
+ pointerEvents: "none"
186
+ },
187
+ backdrop: {
188
+ zIndex: 1,
189
+ position: "fixed",
190
+ top: 0,
191
+ left: 0,
192
+ width: "100%",
193
+ height: "100%",
194
+ backgroundColor: "rgba(0, 0, 0, 0.2)",
195
+ touchAction: "none",
196
+ border: "none",
197
+ WebkitTapHighlightColor: "transparent"
198
+ },
199
+ container: {
200
+ zIndex: 2,
201
+ position: "absolute",
202
+ left: 0,
203
+ bottom: 0,
204
+ width: "100%",
205
+ backgroundColor: "#fff",
206
+ borderTopRightRadius: "8px",
207
+ borderTopLeftRadius: "8px",
208
+ boxShadow: "0px -2px 16px rgba(0, 0, 0, 0.3)",
209
+ display: "flex",
210
+ flexDirection: "column",
211
+ pointerEvents: "auto"
212
+ },
213
+ headerWrapper: {
214
+ width: "100%"
215
+ },
216
+ header: {
217
+ height: "40px",
218
+ width: "100%",
219
+ position: "relative",
220
+ display: "flex",
221
+ alignItems: "center",
222
+ justifyContent: "center"
223
+ },
224
+ indicator: {
225
+ width: "18px",
226
+ height: "4px",
227
+ borderRadius: "99px",
228
+ backgroundColor: "#ddd"
229
+ },
230
+ content: {
231
+ flexGrow: 1,
232
+ display: "flex",
233
+ flexDirection: "column",
234
+ minHeight: "0px",
235
+ position: "relative"
236
+ },
237
+ scroller: {
238
+ height: "100%",
239
+ overflowY: "auto"
240
+ }
241
+ };
242
+ var useIsomorphicLayoutEffect = IS_SSR ? React2.useEffect : React2.useLayoutEffect;
243
+ function useSafeAreaInsets() {
244
+ const [insets] = React2.useState(() => {
245
+ const fallback = { top: 0, left: 0, right: 0, bottom: 0 };
246
+ if (IS_SSR) return fallback;
247
+ const root = document.querySelector(":root");
248
+ if (!root) return fallback;
249
+ root.style.setProperty("--rms-sat", "env(safe-area-inset-top)");
250
+ root.style.setProperty("--rms-sal", "env(safe-area-inset-left)");
251
+ root.style.setProperty("--rms-sar", "env(safe-area-inset-right)");
252
+ root.style.setProperty("--rms-sab", "env(safe-area-inset-bottom)");
253
+ const computedStyle = getComputedStyle(root);
254
+ const sat = getComputedValue(computedStyle, "--rms-sat");
255
+ const sal = getComputedValue(computedStyle, "--rms-sal");
256
+ const sar = getComputedValue(computedStyle, "--rms-sar");
257
+ const sab = getComputedValue(computedStyle, "--rms-sab");
258
+ root.style.removeProperty("--rms-sat");
259
+ root.style.removeProperty("--rms-sal");
260
+ root.style.removeProperty("--rms-sar");
261
+ root.style.removeProperty("--rms-sab");
262
+ return { top: sat, left: sal, right: sar, bottom: sab };
263
+ });
264
+ return insets;
265
+ }
266
+ function getComputedValue(computed, property) {
267
+ const strValue = computed.getPropertyValue(property).replace("px", "").trim();
268
+ return parseInt(strValue, 10) || 0;
269
+ }
270
+
271
+ // src/hooks/use-modal-effect.ts
272
+ function useModalEffect({
273
+ y,
274
+ rootId,
275
+ sheetRef,
276
+ snapPointsProp,
277
+ startThreshold
278
+ }) {
279
+ const insetTop = useSafeAreaInsets().top;
280
+ useIsomorphicLayoutEffect(() => {
281
+ return () => {
282
+ if (rootId) cleanupModalEffect(rootId);
283
+ };
284
+ }, []);
285
+ useIsomorphicLayoutEffect(() => {
286
+ if (!rootId) return;
287
+ const root = document.querySelector(`#${rootId}`);
288
+ if (!root) return;
289
+ let sheetHeight = 0;
290
+ const removeStartListener = y.on("animationStart", () => {
291
+ sheetHeight = getSheetHeight(sheetRef);
292
+ setupModalEffect(rootId);
293
+ });
294
+ const removeChangeListener = y.on("change", (yValue) => {
295
+ if (!root) return;
296
+ let progress = Math.max(0, 1 - yValue / sheetHeight);
297
+ const snapPoints = snapPointsProp ? getSnapPoints({ snapPointsProp, sheetHeight }) : void 0;
298
+ const snapThresholdPoint = snapPoints && snapPoints.length > 1 ? snapPoints[1] : void 0;
299
+ if (snapThresholdPoint !== void 0) {
300
+ const snapThresholdValue = sheetHeight - Math.min(snapThresholdPoint, sheetHeight);
301
+ if (yValue <= snapThresholdValue) {
302
+ progress = (snapThresholdValue - yValue) / snapThresholdValue;
303
+ } else {
304
+ progress = 0;
305
+ }
306
+ }
307
+ if (startThreshold !== void 0) {
308
+ const startThresholdValue = sheetHeight - Math.min(Math.floor(startThreshold * sheetHeight), sheetHeight);
309
+ if (yValue <= startThresholdValue) {
310
+ progress = (startThresholdValue - yValue) / startThresholdValue;
311
+ } else {
312
+ progress = 0;
313
+ }
314
+ }
315
+ progress = Math.max(0, Math.min(1, progress));
316
+ const pageWidth = window.innerWidth;
317
+ const ty = motion.transform(progress, [0, 1], [0, 24 + insetTop]);
318
+ const s = motion.transform(progress, [0, 1], [1, (pageWidth - 16) / pageWidth]);
319
+ const borderRadius = motion.transform(progress, [0, 1], [0, 10]);
320
+ root.style.transform = `scale(${s}) translate3d(0, ${ty}px, 0)`;
321
+ root.style.borderTopRightRadius = `${borderRadius}px`;
322
+ root.style.borderTopLeftRadius = `${borderRadius}px`;
323
+ });
324
+ function onCompleted() {
325
+ if (y.get() - 5 >= sheetHeight) {
326
+ cleanupModalEffect(rootId);
327
+ }
328
+ }
329
+ const removeCompleteListener = y.on("animationComplete", onCompleted);
330
+ const removeCancelListener = y.on("animationCancel", onCompleted);
331
+ return () => {
332
+ removeStartListener();
333
+ removeChangeListener();
334
+ removeCompleteListener();
335
+ removeCancelListener();
336
+ };
337
+ }, [y, rootId, insetTop, startThreshold, snapPointsProp]);
338
+ }
339
+ function setupModalEffect(rootId) {
340
+ const root = document.querySelector(`#${rootId}`);
341
+ const body = document.querySelector("body");
342
+ if (!root) return;
343
+ body.style.backgroundColor = "#000";
344
+ root.style.overflow = "hidden";
345
+ root.style.transitionTimingFunction = "cubic-bezier(0.32, 0.72, 0, 1)";
346
+ root.style.transitionProperty = "transform, border-radius";
347
+ root.style.transitionDuration = "0.5s";
348
+ root.style.transformOrigin = "center top";
349
+ }
350
+ function cleanupModalEffect(rootId) {
351
+ const root = document.querySelector(`#${rootId}`);
352
+ const body = document.querySelector("body");
353
+ if (!root) return;
354
+ body.style.removeProperty("background-color");
355
+ root.style.removeProperty("overflow");
356
+ root.style.removeProperty("transition-timing-function");
357
+ root.style.removeProperty("transition-property");
358
+ root.style.removeProperty("transition-duration");
359
+ root.style.removeProperty("transform-origin");
360
+ root.style.removeProperty("transform");
361
+ root.style.removeProperty("border-top-right-radius");
362
+ root.style.removeProperty("border-top-left-radius");
363
+ }
364
+ function useDimensions() {
365
+ const [dimensions, setDimensions] = React2.useState(() => ({
366
+ height: !IS_SSR ? window.innerHeight : 0,
367
+ width: !IS_SSR ? window.innerWidth : 0
368
+ }));
369
+ useIsomorphicLayoutEffect(() => {
370
+ function handler() {
371
+ setDimensions({
372
+ height: window.innerHeight,
373
+ width: window.innerWidth
374
+ });
375
+ }
376
+ handler();
377
+ window.addEventListener("resize", handler);
378
+ return () => {
379
+ window.removeEventListener("resize", handler);
380
+ };
381
+ }, []);
382
+ return dimensions;
383
+ }
384
+ function useStableCallback(handler) {
385
+ const handlerRef = React2.useRef(void 0);
386
+ useIsomorphicLayoutEffect(() => {
387
+ handlerRef.current = handler;
388
+ });
389
+ return React2.useCallback((...args) => {
390
+ const fn = handlerRef.current;
391
+ return fn == null ? void 0 : fn(...args);
392
+ }, []);
393
+ }
394
+
395
+ // src/hooks/use-prevent-scroll.ts
278
396
  var KEYBOARD_BUFFER = 24;
279
397
  function chain(...callbacks) {
280
398
  return (...args) => {
@@ -490,74 +608,6 @@ function willOpenKeyboard(target) {
490
608
  return target instanceof HTMLInputElement && !nonTextInputTypes.has(target.type) || target instanceof HTMLTextAreaElement || target instanceof HTMLElement && target.isContentEditable;
491
609
  }
492
610
 
493
- // src/styles.ts
494
- var styles = {
495
- wrapper: {
496
- position: "fixed",
497
- top: 0,
498
- bottom: 0,
499
- left: 0,
500
- right: 0,
501
- overflow: "hidden",
502
- pointerEvents: "none"
503
- },
504
- backdrop: {
505
- zIndex: 1,
506
- position: "fixed",
507
- top: 0,
508
- left: 0,
509
- width: "100%",
510
- height: "100%",
511
- backgroundColor: "rgba(0, 0, 0, 0.2)",
512
- touchAction: "none",
513
- // Disable iOS body scrolling
514
- border: "none",
515
- WebkitTapHighlightColor: "transparent"
516
- },
517
- container: {
518
- zIndex: 2,
519
- position: "absolute",
520
- left: 0,
521
- bottom: 0,
522
- width: "100%",
523
- backgroundColor: "#fff",
524
- borderTopRightRadius: "8px",
525
- borderTopLeftRadius: "8px",
526
- boxShadow: "0px -2px 16px rgba(0, 0, 0, 0.3)",
527
- display: "flex",
528
- flexDirection: "column",
529
- pointerEvents: "auto"
530
- },
531
- headerWrapper: {
532
- width: "100%"
533
- },
534
- header: {
535
- height: "40px",
536
- width: "100%",
537
- position: "relative",
538
- display: "flex",
539
- alignItems: "center",
540
- justifyContent: "center"
541
- },
542
- indicator: {
543
- width: "18px",
544
- height: "4px",
545
- borderRadius: "99px",
546
- backgroundColor: "#ddd"
547
- },
548
- content: {
549
- flexGrow: 1,
550
- display: "flex",
551
- flexDirection: "column",
552
- minHeight: "0px",
553
- position: "relative"
554
- },
555
- scroller: {
556
- height: "100%",
557
- overflowY: "auto"
558
- }
559
- };
560
-
561
611
  // src/sheet.tsx
562
612
  var Sheet = React2.forwardRef(
563
613
  ({
@@ -572,6 +622,7 @@ var Sheet = React2.forwardRef(
572
622
  isOpen,
573
623
  snapPoints: snapPointsProp,
574
624
  rootId,
625
+ modalEffectRootId,
575
626
  mountPoint,
576
627
  style,
577
628
  detent = "full-height",
@@ -581,6 +632,7 @@ var Sheet = React2.forwardRef(
581
632
  tweenConfig = DEFAULT_TWEEN_CONFIG,
582
633
  dragVelocityThreshold = DEFAULT_DRAG_VELOCITY_THRESHOLD,
583
634
  dragCloseThreshold = DEFAULT_DRAG_CLOSE_THRESHOLD,
635
+ modalEffectThreshold,
584
636
  ...rest
585
637
  }, ref) => {
586
638
  const sheetRef = React2.useRef(null);
@@ -615,26 +667,13 @@ var Sheet = React2.forwardRef(
615
667
  onCloseEnd
616
668
  };
617
669
  });
618
- let snapPoints = snapPointsProp;
619
- if (snapPoints) {
620
- snapPoints = snapPoints.map((point) => {
621
- if (point > 0 && point <= 1) return Math.round(point * windowHeight);
622
- return point < 0 ? windowHeight + point : point;
623
- });
624
- console.assert(
625
- inDescendingOrder(snapPoints) || windowHeight === 0,
626
- `Snap points need to be in descending order got: [${snapPoints.join(
627
- ", "
628
- )}]`
629
- );
630
- }
631
- const onDrag = useEffectEvent((_, { delta }) => {
670
+ const onDrag = useStableCallback((_, { delta }) => {
632
671
  const velocity = y.getVelocity();
633
672
  if (velocity > 0) indicatorRotation.set(10);
634
673
  if (velocity < 0) indicatorRotation.set(-10);
635
674
  y.set(Math.max(y.get() + delta.y, 0));
636
675
  });
637
- const onDragStart = useEffectEvent(() => {
676
+ const onDragStart = useStableCallback(() => {
638
677
  const focusedElement = document.activeElement;
639
678
  if (!focusedElement || !sheetRef.current) return;
640
679
  const isInput = focusedElement.tagName === "INPUT" || focusedElement.tagName === "TEXTAREA";
@@ -642,67 +681,91 @@ var Sheet = React2.forwardRef(
642
681
  focusedElement.blur();
643
682
  }
644
683
  });
645
- const onDragEnd = useEffectEvent((_, { velocity }) => {
684
+ const onDragEnd = useStableCallback((_, { velocity }) => {
646
685
  if (velocity.y > dragVelocityThreshold) {
647
686
  onClose();
648
687
  } else {
649
- const sheetHeight = sheetRef.current.getBoundingClientRect().height;
688
+ const sheetHeight = getSheetHeight(sheetRef);
650
689
  const currentY = y.get();
651
- let snapTo = 0;
690
+ let yTo = 0;
691
+ const snapPoints = snapPointsProp ? getSnapPoints({ snapPointsProp, sheetHeight }) : void 0;
652
692
  if (snapPoints) {
653
- const snapToValues = snapPoints.map(
693
+ const snapInverse = snapPoints.map(
654
694
  (p) => sheetHeight - Math.min(p, sheetHeight)
655
695
  );
656
- if (detent === "content-height" && !snapToValues.includes(0)) {
657
- snapToValues.unshift(0);
696
+ if (detent === "content-height" && !snapInverse.includes(0)) {
697
+ snapInverse.unshift(0);
658
698
  }
659
- snapTo = getClosest(snapToValues, currentY);
699
+ const snapTo = getClosest(snapInverse, currentY);
700
+ const snapIndex = snapInverse.indexOf(snapTo);
701
+ yTo = validateSnapTo({
702
+ snapTo: getClosest(snapInverse, currentY),
703
+ sheetHeight
704
+ });
705
+ onSnap == null ? void 0 : onSnap(snapIndex);
660
706
  } else if (currentY / sheetHeight > dragCloseThreshold) {
661
- snapTo = sheetHeight;
707
+ yTo = sheetHeight;
662
708
  }
663
- snapTo = validateSnapTo({ snapTo, sheetHeight });
664
- react.animate(y, snapTo, animationOptions);
665
- if (snapPoints && onSnap) {
666
- const snapValue = Math.abs(Math.round(snapPoints[0] - snapTo));
667
- const snapIndex = snapPoints.indexOf(
668
- getClosest(snapPoints, snapValue)
669
- );
670
- onSnap(snapIndex);
709
+ react.animate(y, yTo, animationOptions);
710
+ if (yTo + 1 >= sheetHeight) {
711
+ onClose();
671
712
  }
672
- const roundedSheetHeight = Math.round(sheetHeight);
673
- const shouldClose = snapTo + 2 >= roundedSheetHeight;
674
- if (shouldClose) onClose();
675
713
  }
676
714
  indicatorRotation.set(0);
677
715
  });
678
716
  React2.useEffect(() => {
679
- if (!snapPoints || !onSnap) return;
680
- const snapIndex = isOpen ? initialSnap : snapPoints.length - 1;
681
- onSnap(snapIndex);
717
+ if (snapPointsProp && onSnap) {
718
+ onSnap(isOpen ? initialSnap : snapPointsProp.length - 1);
719
+ }
720
+ }, [isOpen]);
721
+ React2.useEffect(() => {
722
+ if (!isOpen) return;
723
+ let yTo = 0;
724
+ if (snapPointsProp) {
725
+ const snapIndex = initialSnap;
726
+ const sheetHeight = getSheetHeight(sheetRef);
727
+ const snapPoints = getSnapPoints({ snapPointsProp, sheetHeight });
728
+ const ySnap = getSnapY({ sheetHeight, snapPoints, snapIndex });
729
+ if (ySnap !== null) {
730
+ yTo = ySnap;
731
+ }
732
+ }
733
+ react.animate(y, yTo, animationOptions);
682
734
  }, [isOpen]);
683
735
  React2.useImperativeHandle(ref, () => ({
684
736
  y,
685
737
  snapTo: (snapIndex) => {
686
- const sheetEl = sheetRef.current;
687
- if ((snapPoints == null ? void 0 : snapPoints[snapIndex]) !== void 0 && sheetEl) {
688
- const sheetHeight = sheetEl.getBoundingClientRect().height;
689
- const snapPoint = snapPoints[snapIndex];
690
- const snapTo = validateSnapTo({
691
- snapTo: sheetHeight - snapPoint,
692
- sheetHeight
693
- });
694
- react.animate(y, snapTo, animationOptions);
695
- if (onSnap) onSnap(snapIndex);
696
- if (snapTo >= sheetHeight) onClose();
738
+ if (!snapPointsProp) {
739
+ console.warn("Snapping is not possible without `snapPoints` prop.");
740
+ return;
741
+ }
742
+ const sheetHeight = getSheetHeight(sheetRef);
743
+ const snapPoints = getSnapPoints({ snapPointsProp, sheetHeight });
744
+ const ySnap = getSnapY({ sheetHeight, snapPoints, snapIndex });
745
+ if (ySnap !== null) {
746
+ react.animate(y, ySnap, animationOptions);
747
+ onSnap == null ? void 0 : onSnap(snapIndex);
748
+ if (ySnap + 1 >= sheetHeight) {
749
+ onClose();
750
+ }
697
751
  }
698
752
  }
699
753
  }));
754
+ if (rootId) {
755
+ console.warn(
756
+ "The `rootId` prop is deprecated and will be removed in the next major version. Use `modalEffectRootId` instead."
757
+ );
758
+ }
700
759
  useModalEffect({
701
760
  y,
702
- rootId,
703
- sheetRef
761
+ sheetRef,
762
+ snapPointsProp,
763
+ rootId: rootId || modalEffectRootId,
764
+ startThreshold: modalEffectThreshold
765
+ });
766
+ usePreventScroll({
767
+ isDisabled: disableScrollLocking || !isOpen
704
768
  });
705
- usePreventScroll({ isDisabled: disableScrollLocking || !isOpen });
706
769
  const dragProps = React2.useMemo(() => {
707
770
  const dragProps2 = {
708
771
  drag: "y",
@@ -720,7 +783,6 @@ var Sheet = React2.forwardRef(
720
783
  sheetRef,
721
784
  isOpen,
722
785
  initialSnap,
723
- snapPoints,
724
786
  detent,
725
787
  indicatorRotation,
726
788
  callbacks,
@@ -747,24 +809,50 @@ var Sheet = React2.forwardRef(
747
809
  }
748
810
  );
749
811
  Sheet.displayName = "Sheet";
812
+ function useEventCallbacks(isOpen, callbacks) {
813
+ const prevOpen = usePrevious(isOpen);
814
+ const didOpen = React2.useRef(false);
815
+ const handleAnimationComplete = React2.useCallback(() => {
816
+ var _a, _b, _c, _d;
817
+ if (!didOpen.current) {
818
+ (_b = (_a = callbacks.current).onOpenEnd) == null ? void 0 : _b.call(_a);
819
+ didOpen.current = true;
820
+ } else {
821
+ (_d = (_c = callbacks.current).onCloseEnd) == null ? void 0 : _d.call(_c);
822
+ didOpen.current = false;
823
+ }
824
+ }, [isOpen, prevOpen]);
825
+ React2.useEffect(() => {
826
+ var _a, _b, _c, _d;
827
+ if (!prevOpen && isOpen) {
828
+ (_b = (_a = callbacks.current).onOpenStart) == null ? void 0 : _b.call(_a);
829
+ } else if (!isOpen && prevOpen) {
830
+ (_d = (_c = callbacks.current).onCloseStart) == null ? void 0 : _d.call(_c);
831
+ }
832
+ }, [isOpen, prevOpen]);
833
+ return { handleAnimationComplete };
834
+ }
835
+ function usePrevious(state) {
836
+ const ref = React2.useRef(void 0);
837
+ React2.useEffect(() => {
838
+ ref.current = state;
839
+ });
840
+ return ref.current;
841
+ }
842
+
843
+ // src/SheetContainer.tsx
750
844
  var SheetContainer = React2.forwardRef(
751
- ({ children, style = {}, className = "", ...rest }, ref) => {
845
+ ({ children, style, className = "", ...rest }, ref) => {
752
846
  const {
753
847
  y,
754
848
  isOpen,
755
849
  callbacks,
756
- snapPoints,
757
- initialSnap = 0,
758
850
  sheetRef,
759
851
  windowHeight,
760
852
  detent,
761
- animationOptions,
762
- reduceMotion
853
+ animationOptions
763
854
  } = useSheetContext();
764
855
  const { handleAnimationComplete } = useEventCallbacks(isOpen, callbacks);
765
- const initialY = snapPoints ? snapPoints[0] - snapPoints[initialSnap] : 0;
766
- const maxSnapHeight = snapPoints ? snapPoints[0] : null;
767
- const height = maxSnapHeight !== null ? `min(${maxSnapHeight}px, ${MAX_HEIGHT})` : MAX_HEIGHT;
768
856
  return /* @__PURE__ */ React2__default.default.createElement(
769
857
  react.motion.div,
770
858
  {
@@ -774,12 +862,11 @@ var SheetContainer = React2.forwardRef(
774
862
  style: {
775
863
  ...styles.container,
776
864
  ...style,
777
- ...detent === "full-height" && { height },
778
- ...detent === "content-height" && { maxHeight: height },
865
+ ...detent === "full-height" && { height: MAX_HEIGHT },
866
+ ...detent === "content-height" && { maxHeight: MAX_HEIGHT },
779
867
  y
780
868
  },
781
- initial: reduceMotion ? false : { y: windowHeight },
782
- animate: { y: initialY, transition: animationOptions },
869
+ initial: { y: windowHeight },
783
870
  exit: { y: windowHeight, transition: animationOptions },
784
871
  onAnimationComplete: handleAnimationComplete
785
872
  },
@@ -788,6 +875,14 @@ var SheetContainer = React2.forwardRef(
788
875
  }
789
876
  );
790
877
  SheetContainer.displayName = "SheetContainer";
878
+ var constraints = { bottom: 0, top: 0, left: 0, right: 0 };
879
+ function useDragConstraints() {
880
+ const constraintsRef = React2.useRef(null);
881
+ const onMeasureDragConstraints = React2.useCallback(() => constraints, []);
882
+ return { constraintsRef, onMeasureDragConstraints };
883
+ }
884
+
885
+ // src/SheetContent.tsx
791
886
  var SheetContent = React2.forwardRef(
792
887
  ({ children, style, disableDrag, className = "", ...rest }, ref) => {
793
888
  const sheetContext = useSheetContext();
@@ -821,7 +916,7 @@ var SheetHeader = React2.forwardRef(
821
916
  );
822
917
  const indicator2Transform = react.useTransform(
823
918
  indicatorRotation,
824
- (r) => `translateX(-2px) rotate(${-1 * r}deg)`
919
+ (r) => `translateX(-2px) rotate(${ -1 * r}deg)`
825
920
  );
826
921
  return /* @__PURE__ */ React2__default.default.createElement(
827
922
  react.motion.div,