base-vaul 0.0.2 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/{style.css → dist/index.css} +32 -30
- package/dist/index.d.mts +168 -131
- package/dist/index.mjs +1158 -1528
- package/package.json +9 -12
- package/dist/index.d.ts +0 -148
- package/dist/index.js +0 -1635
package/dist/index.mjs
CHANGED
|
@@ -1,1635 +1,1265 @@
|
|
|
1
|
-
|
|
2
|
-
function __insertCSS(code) {
|
|
3
|
-
if (!code || typeof document == 'undefined') return
|
|
4
|
-
let head = document.head || document.getElementsByTagName('head')[0]
|
|
5
|
-
let style = document.createElement('style')
|
|
6
|
-
style.type = 'text/css'
|
|
7
|
-
head.appendChild(style)
|
|
8
|
-
;style.styleSheet ? (style.styleSheet.cssText = code) : style.appendChild(document.createTextNode(code))
|
|
9
|
-
}
|
|
1
|
+
"use client";
|
|
10
2
|
|
|
11
|
-
import {
|
|
12
|
-
import
|
|
13
|
-
import
|
|
14
|
-
import
|
|
3
|
+
import { Dialog } from "@base-ui/react";
|
|
4
|
+
import * as React$1 from "react";
|
|
5
|
+
import React, { useEffect, useLayoutEffect, useMemo } from "react";
|
|
6
|
+
import { jsx } from "react/jsx-runtime";
|
|
15
7
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
current: true
|
|
49
|
-
},
|
|
50
|
-
shouldScaleBackground: false,
|
|
51
|
-
setBackgroundColorOnScale: true,
|
|
52
|
-
noBodyStyles: false,
|
|
53
|
-
container: null,
|
|
54
|
-
autoFocus: false
|
|
8
|
+
import './index.css';
|
|
9
|
+
//#region src/context.ts
|
|
10
|
+
const DrawerContext = React.createContext({
|
|
11
|
+
drawerRef: { current: null },
|
|
12
|
+
overlayRef: { current: null },
|
|
13
|
+
onPress: () => {},
|
|
14
|
+
onRelease: () => {},
|
|
15
|
+
onDrag: () => {},
|
|
16
|
+
onNestedDrag: () => {},
|
|
17
|
+
onNestedOpenChange: () => {},
|
|
18
|
+
onNestedRelease: () => {},
|
|
19
|
+
openProp: void 0,
|
|
20
|
+
dismissible: false,
|
|
21
|
+
isOpen: false,
|
|
22
|
+
isDragging: false,
|
|
23
|
+
keyboardIsOpen: { current: false },
|
|
24
|
+
snapPointsOffset: null,
|
|
25
|
+
snapPoints: null,
|
|
26
|
+
handleOnly: false,
|
|
27
|
+
modal: false,
|
|
28
|
+
shouldFade: false,
|
|
29
|
+
activeSnapPoint: null,
|
|
30
|
+
onOpenChange: () => {},
|
|
31
|
+
setActiveSnapPoint: () => {},
|
|
32
|
+
closeDrawer: () => {},
|
|
33
|
+
direction: "bottom",
|
|
34
|
+
shouldAnimate: { current: true },
|
|
35
|
+
shouldScaleBackground: false,
|
|
36
|
+
setBackgroundColorOnScale: true,
|
|
37
|
+
noBodyStyles: false,
|
|
38
|
+
container: null,
|
|
39
|
+
autoFocus: false
|
|
55
40
|
});
|
|
56
|
-
const useDrawerContext = ()=>{
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
}
|
|
61
|
-
return context;
|
|
41
|
+
const useDrawerContext = () => {
|
|
42
|
+
const context = React.useContext(DrawerContext);
|
|
43
|
+
if (!context) throw new Error("useDrawerContext must be used within a Drawer.Root");
|
|
44
|
+
return context;
|
|
62
45
|
};
|
|
63
46
|
|
|
64
|
-
|
|
65
|
-
|
|
47
|
+
//#endregion
|
|
48
|
+
//#region src/browser.ts
|
|
66
49
|
function isMobileFirefox() {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
/FxiOS/.test(userAgent) // iOS Firefox
|
|
70
|
-
);
|
|
50
|
+
const userAgent = navigator.userAgent;
|
|
51
|
+
return typeof window !== "undefined" && (/Firefox/.test(userAgent) && /Mobile/.test(userAgent) || /FxiOS/.test(userAgent));
|
|
71
52
|
}
|
|
72
53
|
function isMac() {
|
|
73
|
-
|
|
54
|
+
return testPlatform(/^Mac/);
|
|
74
55
|
}
|
|
75
56
|
function isIPhone() {
|
|
76
|
-
|
|
57
|
+
return testPlatform(/^iPhone/);
|
|
77
58
|
}
|
|
78
59
|
function isSafari() {
|
|
79
|
-
|
|
60
|
+
return /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
|
|
80
61
|
}
|
|
81
62
|
function isIPad() {
|
|
82
|
-
|
|
83
|
-
isMac() && navigator.maxTouchPoints > 1;
|
|
63
|
+
return testPlatform(/^iPad/) || isMac() && navigator.maxTouchPoints > 1;
|
|
84
64
|
}
|
|
85
65
|
function isIOS() {
|
|
86
|
-
|
|
66
|
+
return isIPhone() || isIPad();
|
|
87
67
|
}
|
|
88
68
|
function testPlatform(re) {
|
|
89
|
-
|
|
69
|
+
return typeof window !== "undefined" && window.navigator != null ? re.test(window.navigator.platform) : void 0;
|
|
90
70
|
}
|
|
91
71
|
|
|
92
|
-
|
|
93
|
-
|
|
72
|
+
//#endregion
|
|
73
|
+
//#region src/use-prevent-scroll.ts
|
|
94
74
|
const KEYBOARD_BUFFER = 24;
|
|
95
75
|
const useIsomorphicLayoutEffect = typeof window !== "undefined" ? useLayoutEffect : useEffect;
|
|
96
76
|
function chain$1(...callbacks) {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
callback(...args);
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
};
|
|
77
|
+
return (...args) => {
|
|
78
|
+
for (const callback of callbacks) if (typeof callback === "function") callback(...args);
|
|
79
|
+
};
|
|
104
80
|
}
|
|
105
|
-
// @ts-ignore
|
|
106
81
|
const visualViewport = typeof document !== "undefined" && window.visualViewport;
|
|
107
82
|
function isScrollable(node) {
|
|
108
|
-
|
|
109
|
-
|
|
83
|
+
const style = window.getComputedStyle(node);
|
|
84
|
+
return /(auto|scroll)/.test(style.overflow + style.overflowX + style.overflowY);
|
|
110
85
|
}
|
|
111
86
|
function getScrollParent(node) {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
while(node && !isScrollable(node)){
|
|
116
|
-
node = node.parentElement;
|
|
117
|
-
}
|
|
118
|
-
return node || document.scrollingElement || document.documentElement;
|
|
87
|
+
if (isScrollable(node)) node = node.parentElement;
|
|
88
|
+
while (node && !isScrollable(node)) node = node.parentElement;
|
|
89
|
+
return node || document.scrollingElement || document.documentElement;
|
|
119
90
|
}
|
|
120
|
-
// HTML input types that do not cause the software keyboard to appear.
|
|
121
91
|
const nonTextInputTypes = new Set([
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
92
|
+
"checkbox",
|
|
93
|
+
"radio",
|
|
94
|
+
"range",
|
|
95
|
+
"color",
|
|
96
|
+
"file",
|
|
97
|
+
"image",
|
|
98
|
+
"button",
|
|
99
|
+
"submit",
|
|
100
|
+
"reset"
|
|
131
101
|
]);
|
|
132
|
-
// The number of active usePreventScroll calls. Used to determine whether to revert back to the original page style/scroll position
|
|
133
102
|
let preventScrollCount = 0;
|
|
134
103
|
let restore;
|
|
135
104
|
/**
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
if (preventScrollCount === 0) {
|
|
154
|
-
restore?.();
|
|
155
|
-
}
|
|
156
|
-
};
|
|
157
|
-
}, [
|
|
158
|
-
isDisabled
|
|
159
|
-
]);
|
|
105
|
+
* Prevents scrolling on the document body on mount, and
|
|
106
|
+
* restores it on unmount. Also ensures that content does not
|
|
107
|
+
* shift due to the scrollbars disappearing.
|
|
108
|
+
*/
|
|
109
|
+
function usePreventScroll(options = {}) {
|
|
110
|
+
const { isDisabled } = options;
|
|
111
|
+
useIsomorphicLayoutEffect(() => {
|
|
112
|
+
if (isDisabled) return;
|
|
113
|
+
preventScrollCount++;
|
|
114
|
+
if (preventScrollCount === 1) {
|
|
115
|
+
if (isIOS()) restore = preventScrollMobileSafari();
|
|
116
|
+
}
|
|
117
|
+
return () => {
|
|
118
|
+
preventScrollCount--;
|
|
119
|
+
if (preventScrollCount === 0) restore?.();
|
|
120
|
+
};
|
|
121
|
+
}, [isDisabled]);
|
|
160
122
|
}
|
|
161
|
-
// Mobile Safari is a whole different beast. Even with overflow: hidden,
|
|
162
|
-
// it still scrolls the page in many situations:
|
|
163
|
-
//
|
|
164
|
-
// 1. When the bottom toolbar and address bar are collapsed, page scrolling is always allowed.
|
|
165
|
-
// 2. When the keyboard is visible, the viewport does not resize. Instead, the keyboard covers part of
|
|
166
|
-
// it, so it becomes scrollable.
|
|
167
|
-
// 3. When tapping on an input, the page always scrolls so that the input is centered in the visual viewport.
|
|
168
|
-
// This may cause even fixed position elements to scroll off the screen.
|
|
169
|
-
// 4. When using the next/previous buttons in the keyboard to navigate between inputs, the whole page always
|
|
170
|
-
// scrolls, even if the input is inside a nested scrollable element that could be scrolled instead.
|
|
171
|
-
//
|
|
172
|
-
// In order to work around these cases, and prevent scrolling without jankiness, we do a few things:
|
|
173
|
-
//
|
|
174
|
-
// 1. Prevent default on `touchmove` events that are not in a scrollable element. This prevents touch scrolling
|
|
175
|
-
// on the window.
|
|
176
|
-
// 2. Prevent default on `touchmove` events inside a scrollable element when the scroll position is at the
|
|
177
|
-
// top or bottom. This avoids the whole page scrolling instead, but does prevent overscrolling.
|
|
178
|
-
// 3. Prevent default on `touchend` events on input elements and handle focusing the element ourselves.
|
|
179
|
-
// 4. When focusing an input, apply a transform to trick Safari into thinking the input is at the top
|
|
180
|
-
// of the page, which prevents it from scrolling the page. After the input is focused, scroll the element
|
|
181
|
-
// into view ourselves, without scrolling the whole page.
|
|
182
|
-
// 5. Offset the body by the scroll position using a negative margin and scroll to the top. This should appear the
|
|
183
|
-
// same visually, but makes the actual scroll position always zero. This is required to make all of the
|
|
184
|
-
// above work or Safari will still try to scroll the page when focusing an input.
|
|
185
|
-
// 6. As a last resort, handle window scroll events, and scroll back to the top. This can happen when attempting
|
|
186
|
-
// to navigate to an input with the next/previous buttons that's outside a modal.
|
|
187
123
|
function preventScrollMobileSafari() {
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
} else {
|
|
254
|
-
// Otherwise, wait for the visual viewport to resize before scrolling so we can
|
|
255
|
-
// measure the correct position to scroll to.
|
|
256
|
-
visualViewport.addEventListener("resize", ()=>scrollIntoView(target), {
|
|
257
|
-
once: true
|
|
258
|
-
});
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
});
|
|
262
|
-
}
|
|
263
|
-
};
|
|
264
|
-
const onWindowScroll = ()=>{
|
|
265
|
-
// Last resort. If the window scrolled, scroll it back to the top.
|
|
266
|
-
// It should always be at the top because the body will have a negative margin (see below).
|
|
267
|
-
window.scrollTo(0, 0);
|
|
268
|
-
};
|
|
269
|
-
// Record the original scroll position so we can restore it.
|
|
270
|
-
// Then apply a negative margin to the body to offset it by the scroll position. This will
|
|
271
|
-
// enable us to scroll the window to the top, which is required for the rest of this to work.
|
|
272
|
-
const scrollX = window.pageXOffset;
|
|
273
|
-
const scrollY = window.pageYOffset;
|
|
274
|
-
const restoreStyles = chain$1(setStyle(document.documentElement, "paddingRight", `${window.innerWidth - document.documentElement.clientWidth}px`));
|
|
275
|
-
// Scroll to the top. The negative margin on the body will make this appear the same.
|
|
276
|
-
window.scrollTo(0, 0);
|
|
277
|
-
const removeEvents = chain$1(addEvent(document, "touchstart", onTouchStart, {
|
|
278
|
-
passive: false,
|
|
279
|
-
capture: true
|
|
280
|
-
}), addEvent(document, "touchmove", onTouchMove, {
|
|
281
|
-
passive: false,
|
|
282
|
-
capture: true
|
|
283
|
-
}), addEvent(document, "touchend", onTouchEnd, {
|
|
284
|
-
passive: false,
|
|
285
|
-
capture: true
|
|
286
|
-
}), addEvent(document, "focus", onFocus, true), addEvent(window, "scroll", onWindowScroll));
|
|
287
|
-
return ()=>{
|
|
288
|
-
// Restore styles and scroll the page back to where it was.
|
|
289
|
-
restoreStyles();
|
|
290
|
-
removeEvents();
|
|
291
|
-
window.scrollTo(scrollX, scrollY);
|
|
292
|
-
};
|
|
124
|
+
let scrollable;
|
|
125
|
+
let lastY = 0;
|
|
126
|
+
const onTouchStart = (e) => {
|
|
127
|
+
scrollable = getScrollParent(e.target);
|
|
128
|
+
if (scrollable === document.documentElement && scrollable === document.body) return;
|
|
129
|
+
lastY = e.changedTouches[0].pageY;
|
|
130
|
+
};
|
|
131
|
+
const onTouchMove = (e) => {
|
|
132
|
+
if (!scrollable || scrollable === document.documentElement || scrollable === document.body) {
|
|
133
|
+
e.preventDefault();
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
const y = e.changedTouches[0].pageY;
|
|
137
|
+
const scrollTop = scrollable.scrollTop;
|
|
138
|
+
const bottom = scrollable.scrollHeight - scrollable.clientHeight;
|
|
139
|
+
if (bottom === 0) return;
|
|
140
|
+
if (scrollTop <= 0 && y > lastY || scrollTop >= bottom && y < lastY) e.preventDefault();
|
|
141
|
+
lastY = y;
|
|
142
|
+
};
|
|
143
|
+
const onTouchEnd = (e) => {
|
|
144
|
+
const target = e.target;
|
|
145
|
+
if (isInput(target) && target !== document.activeElement) {
|
|
146
|
+
e.preventDefault();
|
|
147
|
+
target.style.transform = "translateY(-2000px)";
|
|
148
|
+
target.focus();
|
|
149
|
+
requestAnimationFrame(() => {
|
|
150
|
+
target.style.transform = "";
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
const onFocus = (e) => {
|
|
155
|
+
const target = e.target;
|
|
156
|
+
if (isInput(target)) {
|
|
157
|
+
target.style.transform = "translateY(-2000px)";
|
|
158
|
+
requestAnimationFrame(() => {
|
|
159
|
+
target.style.transform = "";
|
|
160
|
+
if (visualViewport) if (visualViewport.height < window.innerHeight) requestAnimationFrame(() => {
|
|
161
|
+
scrollIntoView(target);
|
|
162
|
+
});
|
|
163
|
+
else visualViewport.addEventListener("resize", () => scrollIntoView(target), { once: true });
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
const onWindowScroll = () => {
|
|
168
|
+
window.scrollTo(0, 0);
|
|
169
|
+
};
|
|
170
|
+
const scrollX = window.pageXOffset;
|
|
171
|
+
const scrollY = window.pageYOffset;
|
|
172
|
+
const restoreStyles = chain$1(setStyle(document.documentElement, "paddingRight", `${window.innerWidth - document.documentElement.clientWidth}px`));
|
|
173
|
+
window.scrollTo(0, 0);
|
|
174
|
+
const removeEvents = chain$1(addEvent(document, "touchstart", onTouchStart, {
|
|
175
|
+
passive: false,
|
|
176
|
+
capture: true
|
|
177
|
+
}), addEvent(document, "touchmove", onTouchMove, {
|
|
178
|
+
passive: false,
|
|
179
|
+
capture: true
|
|
180
|
+
}), addEvent(document, "touchend", onTouchEnd, {
|
|
181
|
+
passive: false,
|
|
182
|
+
capture: true
|
|
183
|
+
}), addEvent(document, "focus", onFocus, true), addEvent(window, "scroll", onWindowScroll));
|
|
184
|
+
return () => {
|
|
185
|
+
restoreStyles();
|
|
186
|
+
removeEvents();
|
|
187
|
+
window.scrollTo(scrollX, scrollY);
|
|
188
|
+
};
|
|
293
189
|
}
|
|
294
|
-
// Sets a CSS property on an element, and returns a function to revert it to the previous value.
|
|
295
190
|
function setStyle(element, style, value) {
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
return ()=>{
|
|
302
|
-
// @ts-ignore
|
|
303
|
-
element.style[style] = cur;
|
|
304
|
-
};
|
|
191
|
+
const cur = element.style[style];
|
|
192
|
+
element.style[style] = value;
|
|
193
|
+
return () => {
|
|
194
|
+
element.style[style] = cur;
|
|
195
|
+
};
|
|
305
196
|
}
|
|
306
|
-
// Adds an event listener to an element, and returns a function to remove it.
|
|
307
197
|
function addEvent(target, event, handler, options) {
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
target.removeEventListener(event, handler, options);
|
|
313
|
-
};
|
|
198
|
+
target.addEventListener(event, handler, options);
|
|
199
|
+
return () => {
|
|
200
|
+
target.removeEventListener(event, handler, options);
|
|
201
|
+
};
|
|
314
202
|
}
|
|
315
203
|
function scrollIntoView(target) {
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
if (targetBottom > keyboardHeight) {
|
|
327
|
-
scrollable.scrollTop += targetTop - scrollableTop;
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
// @ts-ignore
|
|
331
|
-
target = scrollable.parentElement;
|
|
332
|
-
}
|
|
204
|
+
const root = document.scrollingElement || document.documentElement;
|
|
205
|
+
while (target && target !== root) {
|
|
206
|
+
const scrollable = getScrollParent(target);
|
|
207
|
+
if (scrollable !== document.documentElement && scrollable !== document.body && scrollable !== target) {
|
|
208
|
+
const scrollableTop = scrollable.getBoundingClientRect().top;
|
|
209
|
+
const targetTop = target.getBoundingClientRect().top;
|
|
210
|
+
if (target.getBoundingClientRect().bottom > scrollable.getBoundingClientRect().bottom + KEYBOARD_BUFFER) scrollable.scrollTop += targetTop - scrollableTop;
|
|
211
|
+
}
|
|
212
|
+
target = scrollable.parentElement;
|
|
213
|
+
}
|
|
333
214
|
}
|
|
334
215
|
function isInput(target) {
|
|
335
|
-
|
|
216
|
+
return target instanceof HTMLInputElement && !nonTextInputTypes.has(target.type) || target instanceof HTMLTextAreaElement || target instanceof HTMLElement && target.isContentEditable;
|
|
336
217
|
}
|
|
337
218
|
|
|
338
|
-
|
|
219
|
+
//#endregion
|
|
220
|
+
//#region src/use-composed-refs.ts
|
|
339
221
|
/**
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
ref.current = value;
|
|
347
|
-
}
|
|
222
|
+
* Set a given ref to a given value
|
|
223
|
+
* This utility takes care of different types of refs: callback refs and RefObject(s)
|
|
224
|
+
*/
|
|
225
|
+
function setRef(ref, value) {
|
|
226
|
+
if (typeof ref === "function") ref(value);
|
|
227
|
+
else if (ref !== null && ref !== void 0) ref.current = value;
|
|
348
228
|
}
|
|
349
229
|
/**
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
230
|
+
* A utility to compose multiple refs together
|
|
231
|
+
* Accepts callback refs and RefObject(s)
|
|
232
|
+
*/
|
|
233
|
+
function composeRefs(...refs) {
|
|
234
|
+
return (node) => refs.forEach((ref) => setRef(ref, node));
|
|
354
235
|
}
|
|
355
236
|
/**
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
237
|
+
* A custom hook that composes multiple refs
|
|
238
|
+
* Accepts callback refs and RefObject(s)
|
|
239
|
+
*/
|
|
240
|
+
function useComposedRefs(...refs) {
|
|
241
|
+
return React$1.useCallback(composeRefs(...refs), refs);
|
|
361
242
|
}
|
|
362
243
|
|
|
363
|
-
|
|
244
|
+
//#endregion
|
|
245
|
+
//#region src/helpers.ts
|
|
246
|
+
const cache = /* @__PURE__ */ new WeakMap();
|
|
364
247
|
function set(el, styles, ignoreCache = false) {
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
248
|
+
if (!el || !(el instanceof HTMLElement)) return;
|
|
249
|
+
const originalStyles = {};
|
|
250
|
+
Object.entries(styles).forEach(([key, value]) => {
|
|
251
|
+
if (key.startsWith("--")) {
|
|
252
|
+
el.style.setProperty(key, value);
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
originalStyles[key] = el.style[key];
|
|
256
|
+
el.style[key] = value;
|
|
257
|
+
});
|
|
258
|
+
if (ignoreCache) return;
|
|
259
|
+
cache.set(el, originalStyles);
|
|
377
260
|
}
|
|
378
261
|
function reset(el, prop) {
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
}
|
|
262
|
+
if (!el || !(el instanceof HTMLElement)) return;
|
|
263
|
+
const originalStyles = cache.get(el);
|
|
264
|
+
if (!originalStyles) return;
|
|
265
|
+
if (prop) el.style[prop] = originalStyles[prop];
|
|
266
|
+
else Object.entries(originalStyles).forEach(([key, value]) => {
|
|
267
|
+
el.style[key] = value;
|
|
268
|
+
});
|
|
387
269
|
}
|
|
388
|
-
const isVertical = (direction)=>{
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
default:
|
|
397
|
-
return direction;
|
|
398
|
-
}
|
|
270
|
+
const isVertical = (direction) => {
|
|
271
|
+
switch (direction) {
|
|
272
|
+
case "top":
|
|
273
|
+
case "bottom": return true;
|
|
274
|
+
case "left":
|
|
275
|
+
case "right": return false;
|
|
276
|
+
default: return direction;
|
|
277
|
+
}
|
|
399
278
|
};
|
|
400
279
|
function getTranslate(element, direction) {
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
if (mat) {
|
|
409
|
-
// https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function/matrix3d
|
|
410
|
-
return parseFloat(mat[1].split(', ')[isVertical(direction) ? 13 : 12]);
|
|
411
|
-
}
|
|
412
|
-
// https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function/matrix
|
|
413
|
-
mat = transform.match(/^matrix\((.+)\)$/);
|
|
414
|
-
return mat ? parseFloat(mat[1].split(', ')[isVertical(direction) ? 5 : 4]) : null;
|
|
280
|
+
if (!element) return null;
|
|
281
|
+
const style = window.getComputedStyle(element);
|
|
282
|
+
const transform = style.transform || style.webkitTransform || style.mozTransform;
|
|
283
|
+
let mat = transform.match(/^matrix3d\((.+)\)$/);
|
|
284
|
+
if (mat) return parseFloat(mat[1].split(", ")[isVertical(direction) ? 13 : 12]);
|
|
285
|
+
mat = transform.match(/^matrix\((.+)\)$/);
|
|
286
|
+
return mat ? parseFloat(mat[1].split(", ")[isVertical(direction) ? 5 : 4]) : null;
|
|
415
287
|
}
|
|
416
288
|
function dampenValue(v) {
|
|
417
|
-
|
|
289
|
+
return 8 * (Math.log(v + 1) - 2);
|
|
418
290
|
}
|
|
419
291
|
function assignStyle(element, style) {
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
292
|
+
if (!element) return () => {};
|
|
293
|
+
const prevStyle = element.style.cssText;
|
|
294
|
+
Object.assign(element.style, style);
|
|
295
|
+
return () => {
|
|
296
|
+
element.style.cssText = prevStyle;
|
|
297
|
+
};
|
|
426
298
|
}
|
|
427
299
|
/**
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
fn(...args);
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
};
|
|
300
|
+
* Receives functions as arguments and returns a new function that calls all.
|
|
301
|
+
*/
|
|
302
|
+
function chain(...fns) {
|
|
303
|
+
return (...args) => {
|
|
304
|
+
for (const fn of fns) if (typeof fn === "function") fn(...args);
|
|
305
|
+
};
|
|
438
306
|
}
|
|
439
307
|
|
|
308
|
+
//#endregion
|
|
309
|
+
//#region src/constants.ts
|
|
440
310
|
const TRANSITIONS = {
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
311
|
+
DURATION: .5,
|
|
312
|
+
EASE: [
|
|
313
|
+
.32,
|
|
314
|
+
.72,
|
|
315
|
+
0,
|
|
316
|
+
1
|
|
317
|
+
]
|
|
448
318
|
};
|
|
449
|
-
const VELOCITY_THRESHOLD =
|
|
450
|
-
const CLOSE_THRESHOLD =
|
|
319
|
+
const VELOCITY_THRESHOLD = .4;
|
|
320
|
+
const CLOSE_THRESHOLD = .25;
|
|
451
321
|
const SCROLL_LOCK_TIMEOUT = 100;
|
|
452
322
|
const BORDER_RADIUS = 8;
|
|
453
323
|
const NESTED_DISPLACEMENT = 16;
|
|
454
324
|
const WINDOW_TOP_OFFSET = 26;
|
|
455
|
-
const DRAG_CLASS =
|
|
325
|
+
const DRAG_CLASS = "vaul-dragging";
|
|
456
326
|
|
|
457
|
-
|
|
327
|
+
//#endregion
|
|
328
|
+
//#region src/use-controllable-state.ts
|
|
458
329
|
function useCallbackRef(callback) {
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
return React__default.useMemo(()=>(...args)=>callbackRef.current?.(...args), []);
|
|
330
|
+
const callbackRef = React.useRef(callback);
|
|
331
|
+
React.useEffect(() => {
|
|
332
|
+
callbackRef.current = callback;
|
|
333
|
+
});
|
|
334
|
+
return React.useMemo(() => ((...args) => callbackRef.current?.(...args)), []);
|
|
465
335
|
}
|
|
466
336
|
function useUncontrolledState({ defaultProp, onChange }) {
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
337
|
+
const uncontrolledState = React.useState(defaultProp);
|
|
338
|
+
const [value] = uncontrolledState;
|
|
339
|
+
const prevValueRef = React.useRef(value);
|
|
340
|
+
const handleChange = useCallbackRef(onChange);
|
|
341
|
+
React.useEffect(() => {
|
|
342
|
+
if (prevValueRef.current !== value) {
|
|
343
|
+
handleChange(value);
|
|
344
|
+
prevValueRef.current = value;
|
|
345
|
+
}
|
|
346
|
+
}, [
|
|
347
|
+
value,
|
|
348
|
+
prevValueRef,
|
|
349
|
+
handleChange
|
|
350
|
+
]);
|
|
351
|
+
return uncontrolledState;
|
|
482
352
|
}
|
|
483
|
-
function useControllableState({ prop, defaultProp, onChange = ()=>{} }) {
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
setUncontrolledProp,
|
|
503
|
-
handleChange
|
|
504
|
-
]);
|
|
505
|
-
return [
|
|
506
|
-
value,
|
|
507
|
-
setValue
|
|
508
|
-
];
|
|
353
|
+
function useControllableState({ prop, defaultProp, onChange = () => {} }) {
|
|
354
|
+
const [uncontrolledProp, setUncontrolledProp] = useUncontrolledState({
|
|
355
|
+
defaultProp,
|
|
356
|
+
onChange
|
|
357
|
+
});
|
|
358
|
+
const isControlled = prop !== void 0;
|
|
359
|
+
const value = isControlled ? prop : uncontrolledProp;
|
|
360
|
+
const handleChange = useCallbackRef(onChange);
|
|
361
|
+
return [value, React.useCallback((nextValue) => {
|
|
362
|
+
if (isControlled) {
|
|
363
|
+
const value$1 = typeof nextValue === "function" ? nextValue(prop) : nextValue;
|
|
364
|
+
if (value$1 !== prop) handleChange(value$1);
|
|
365
|
+
} else setUncontrolledProp(nextValue);
|
|
366
|
+
}, [
|
|
367
|
+
isControlled,
|
|
368
|
+
prop,
|
|
369
|
+
setUncontrolledProp,
|
|
370
|
+
handleChange
|
|
371
|
+
])];
|
|
509
372
|
}
|
|
510
373
|
|
|
511
|
-
|
|
374
|
+
//#endregion
|
|
375
|
+
//#region src/use-snap-points.ts
|
|
512
376
|
function useSnapPoints({ activeSnapPointProp, setActiveSnapPointProp, snapPoints, drawerRef, overlayRef, fadeFromIndex, onSnapPointChange, direction = "bottom", container, snapToSequentialPoint }) {
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
const newValue = direction === "bottom" || direction === "right" ? activeSnapPointOffset - draggedDistance : activeSnapPointOffset + draggedDistance;
|
|
666
|
-
// Don't do anything if we exceed the last(biggest) snap point
|
|
667
|
-
if ((direction === "bottom" || direction === "right") && newValue < snapPointsOffset[snapPointsOffset.length - 1]) {
|
|
668
|
-
return;
|
|
669
|
-
}
|
|
670
|
-
if ((direction === "top" || direction === "left") && newValue > snapPointsOffset[snapPointsOffset.length - 1]) {
|
|
671
|
-
return;
|
|
672
|
-
}
|
|
673
|
-
set(drawerRef.current, {
|
|
674
|
-
transform: isVertical(direction) ? `translate3d(0, ${newValue}px, 0)` : `translate3d(${newValue}px, 0, 0)`
|
|
675
|
-
});
|
|
676
|
-
}
|
|
677
|
-
function getPercentageDragged(absDraggedDistance, isDraggingDown) {
|
|
678
|
-
if (!snapPoints || typeof activeSnapPointIndex !== "number" || !snapPointsOffset || fadeFromIndex === undefined) return null;
|
|
679
|
-
// If this is true we are dragging to a snap point that is supposed to have an overlay
|
|
680
|
-
const isOverlaySnapPoint = activeSnapPointIndex === fadeFromIndex - 1;
|
|
681
|
-
const isOverlaySnapPointOrHigher = activeSnapPointIndex >= fadeFromIndex;
|
|
682
|
-
if (isOverlaySnapPointOrHigher && isDraggingDown) {
|
|
683
|
-
return 0;
|
|
684
|
-
}
|
|
685
|
-
// Don't animate, but still use this one if we are dragging away from the overlaySnapPoint
|
|
686
|
-
if (isOverlaySnapPoint && !isDraggingDown) return 1;
|
|
687
|
-
if (!shouldFade && !isOverlaySnapPoint) return null;
|
|
688
|
-
// Either fadeFrom index or the one before
|
|
689
|
-
const targetSnapPointIndex = isOverlaySnapPoint ? activeSnapPointIndex + 1 : activeSnapPointIndex - 1;
|
|
690
|
-
// Get the distance from overlaySnapPoint to the one before or vice-versa to calculate the opacity percentage accordingly
|
|
691
|
-
const snapPointDistance = isOverlaySnapPoint ? snapPointsOffset[targetSnapPointIndex] - snapPointsOffset[targetSnapPointIndex - 1] : snapPointsOffset[targetSnapPointIndex + 1] - snapPointsOffset[targetSnapPointIndex];
|
|
692
|
-
const percentageDragged = absDraggedDistance / Math.abs(snapPointDistance);
|
|
693
|
-
if (isOverlaySnapPoint) {
|
|
694
|
-
return 1 - percentageDragged;
|
|
695
|
-
} else {
|
|
696
|
-
return percentageDragged;
|
|
697
|
-
}
|
|
698
|
-
}
|
|
699
|
-
return {
|
|
700
|
-
isLastSnapPoint,
|
|
701
|
-
activeSnapPoint,
|
|
702
|
-
shouldFade,
|
|
703
|
-
getPercentageDragged,
|
|
704
|
-
setActiveSnapPoint,
|
|
705
|
-
activeSnapPointIndex,
|
|
706
|
-
onRelease,
|
|
707
|
-
onDrag,
|
|
708
|
-
snapPointsOffset
|
|
709
|
-
};
|
|
377
|
+
const [activeSnapPoint, setActiveSnapPoint] = useControllableState({
|
|
378
|
+
prop: activeSnapPointProp,
|
|
379
|
+
defaultProp: snapPoints?.[0],
|
|
380
|
+
onChange: setActiveSnapPointProp
|
|
381
|
+
});
|
|
382
|
+
const [windowDimensions, setWindowDimensions] = React.useState(typeof window !== "undefined" ? {
|
|
383
|
+
innerWidth: window.innerWidth,
|
|
384
|
+
innerHeight: window.innerHeight
|
|
385
|
+
} : void 0);
|
|
386
|
+
React.useEffect(() => {
|
|
387
|
+
function onResize() {
|
|
388
|
+
setWindowDimensions({
|
|
389
|
+
innerWidth: window.innerWidth,
|
|
390
|
+
innerHeight: window.innerHeight
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
window.addEventListener("resize", onResize);
|
|
394
|
+
return () => window.removeEventListener("resize", onResize);
|
|
395
|
+
}, []);
|
|
396
|
+
const isLastSnapPoint = React.useMemo(() => activeSnapPoint === snapPoints?.[snapPoints.length - 1] || null, [snapPoints, activeSnapPoint]);
|
|
397
|
+
const activeSnapPointIndex = React.useMemo(() => snapPoints?.findIndex((snapPoint) => snapPoint === activeSnapPoint) ?? null, [snapPoints, activeSnapPoint]);
|
|
398
|
+
const shouldFade = snapPoints && snapPoints.length > 0 && (fadeFromIndex || fadeFromIndex === 0) && !Number.isNaN(fadeFromIndex) && snapPoints[fadeFromIndex] === activeSnapPoint || !snapPoints;
|
|
399
|
+
const snapPointsOffset = React.useMemo(() => {
|
|
400
|
+
const containerSize = container ? {
|
|
401
|
+
width: container.getBoundingClientRect().width,
|
|
402
|
+
height: container.getBoundingClientRect().height
|
|
403
|
+
} : typeof window !== "undefined" ? {
|
|
404
|
+
width: window.innerWidth,
|
|
405
|
+
height: window.innerHeight
|
|
406
|
+
} : {
|
|
407
|
+
width: 0,
|
|
408
|
+
height: 0
|
|
409
|
+
};
|
|
410
|
+
return snapPoints?.map((snapPoint) => {
|
|
411
|
+
const isPx = typeof snapPoint === "string";
|
|
412
|
+
let snapPointAsNumber = 0;
|
|
413
|
+
if (isPx) snapPointAsNumber = parseInt(snapPoint, 10);
|
|
414
|
+
if (isVertical(direction)) {
|
|
415
|
+
const height = isPx ? snapPointAsNumber : windowDimensions ? snapPoint * containerSize.height : 0;
|
|
416
|
+
if (windowDimensions) return direction === "bottom" ? containerSize.height - height : -containerSize.height + height;
|
|
417
|
+
return height;
|
|
418
|
+
}
|
|
419
|
+
const width = isPx ? snapPointAsNumber : windowDimensions ? snapPoint * containerSize.width : 0;
|
|
420
|
+
if (windowDimensions) return direction === "right" ? containerSize.width - width : -containerSize.width + width;
|
|
421
|
+
return width;
|
|
422
|
+
}) ?? [];
|
|
423
|
+
}, [
|
|
424
|
+
snapPoints,
|
|
425
|
+
windowDimensions,
|
|
426
|
+
container
|
|
427
|
+
]);
|
|
428
|
+
const activeSnapPointOffset = React.useMemo(() => activeSnapPointIndex !== null ? snapPointsOffset?.[activeSnapPointIndex] : null, [snapPointsOffset, activeSnapPointIndex]);
|
|
429
|
+
const snapToPoint = React.useCallback((dimension) => {
|
|
430
|
+
const newSnapPointIndex = snapPointsOffset?.findIndex((snapPointDim) => snapPointDim === dimension) ?? null;
|
|
431
|
+
onSnapPointChange(newSnapPointIndex);
|
|
432
|
+
set(drawerRef.current, {
|
|
433
|
+
transition: `transform ${TRANSITIONS.DURATION}s cubic-bezier(${TRANSITIONS.EASE.join(",")})`,
|
|
434
|
+
transform: isVertical(direction) ? `translate3d(0, ${dimension}px, 0)` : `translate3d(${dimension}px, 0, 0)`
|
|
435
|
+
});
|
|
436
|
+
if (snapPointsOffset && newSnapPointIndex !== snapPointsOffset.length - 1 && fadeFromIndex !== void 0 && newSnapPointIndex !== fadeFromIndex && newSnapPointIndex < fadeFromIndex) set(overlayRef.current, {
|
|
437
|
+
transition: `opacity ${TRANSITIONS.DURATION}s cubic-bezier(${TRANSITIONS.EASE.join(",")})`,
|
|
438
|
+
opacity: "0"
|
|
439
|
+
});
|
|
440
|
+
else set(overlayRef.current, {
|
|
441
|
+
transition: `opacity ${TRANSITIONS.DURATION}s cubic-bezier(${TRANSITIONS.EASE.join(",")})`,
|
|
442
|
+
opacity: "1"
|
|
443
|
+
});
|
|
444
|
+
setActiveSnapPoint(snapPoints?.[Math.max(newSnapPointIndex, 0)]);
|
|
445
|
+
}, [
|
|
446
|
+
drawerRef.current,
|
|
447
|
+
snapPoints,
|
|
448
|
+
snapPointsOffset,
|
|
449
|
+
fadeFromIndex,
|
|
450
|
+
overlayRef,
|
|
451
|
+
setActiveSnapPoint
|
|
452
|
+
]);
|
|
453
|
+
React.useEffect(() => {
|
|
454
|
+
if (activeSnapPoint || activeSnapPointProp) {
|
|
455
|
+
const newIndex = snapPoints?.findIndex((snapPoint) => snapPoint === activeSnapPointProp || snapPoint === activeSnapPoint) ?? -1;
|
|
456
|
+
if (snapPointsOffset && newIndex !== -1 && typeof snapPointsOffset[newIndex] === "number") snapToPoint(snapPointsOffset[newIndex]);
|
|
457
|
+
}
|
|
458
|
+
}, [
|
|
459
|
+
activeSnapPoint,
|
|
460
|
+
activeSnapPointProp,
|
|
461
|
+
snapPoints,
|
|
462
|
+
snapPointsOffset,
|
|
463
|
+
snapToPoint
|
|
464
|
+
]);
|
|
465
|
+
function onRelease({ draggedDistance, closeDrawer, velocity, dismissible }) {
|
|
466
|
+
if (fadeFromIndex === void 0) return;
|
|
467
|
+
const currentPosition = direction === "bottom" || direction === "right" ? (activeSnapPointOffset ?? 0) - draggedDistance : (activeSnapPointOffset ?? 0) + draggedDistance;
|
|
468
|
+
const isOverlaySnapPoint = activeSnapPointIndex === fadeFromIndex - 1;
|
|
469
|
+
const isFirst = activeSnapPointIndex === 0;
|
|
470
|
+
const hasDraggedUp = draggedDistance > 0;
|
|
471
|
+
if (isOverlaySnapPoint) set(overlayRef.current, { transition: `opacity ${TRANSITIONS.DURATION}s cubic-bezier(${TRANSITIONS.EASE.join(",")})` });
|
|
472
|
+
if (!snapToSequentialPoint && velocity > 2 && !hasDraggedUp) {
|
|
473
|
+
if (dismissible) closeDrawer();
|
|
474
|
+
else snapToPoint(snapPointsOffset[0]);
|
|
475
|
+
return;
|
|
476
|
+
}
|
|
477
|
+
if (!snapToSequentialPoint && velocity > 2 && hasDraggedUp && snapPointsOffset && snapPoints) {
|
|
478
|
+
snapToPoint(snapPointsOffset[snapPoints.length - 1]);
|
|
479
|
+
return;
|
|
480
|
+
}
|
|
481
|
+
const closestSnapPoint = snapPointsOffset?.reduce((prev, curr) => {
|
|
482
|
+
if (typeof prev !== "number" || typeof curr !== "number") return prev;
|
|
483
|
+
return Math.abs(curr - currentPosition) < Math.abs(prev - currentPosition) ? curr : prev;
|
|
484
|
+
});
|
|
485
|
+
const dim = isVertical(direction) ? window.innerHeight : window.innerWidth;
|
|
486
|
+
if (velocity > VELOCITY_THRESHOLD && Math.abs(draggedDistance) < dim * .4) {
|
|
487
|
+
const dragDirection = hasDraggedUp ? 1 : -1;
|
|
488
|
+
if (dragDirection > 0 && isLastSnapPoint && snapPoints) {
|
|
489
|
+
snapToPoint(snapPointsOffset[snapPoints.length - 1]);
|
|
490
|
+
return;
|
|
491
|
+
}
|
|
492
|
+
if (isFirst && dragDirection < 0 && dismissible) closeDrawer();
|
|
493
|
+
if (activeSnapPointIndex === null) return;
|
|
494
|
+
snapToPoint(snapPointsOffset[activeSnapPointIndex + dragDirection]);
|
|
495
|
+
return;
|
|
496
|
+
}
|
|
497
|
+
snapToPoint(closestSnapPoint);
|
|
498
|
+
}
|
|
499
|
+
function onDrag({ draggedDistance }) {
|
|
500
|
+
if (activeSnapPointOffset === null) return;
|
|
501
|
+
const newValue = direction === "bottom" || direction === "right" ? activeSnapPointOffset - draggedDistance : activeSnapPointOffset + draggedDistance;
|
|
502
|
+
if ((direction === "bottom" || direction === "right") && newValue < snapPointsOffset[snapPointsOffset.length - 1]) return;
|
|
503
|
+
if ((direction === "top" || direction === "left") && newValue > snapPointsOffset[snapPointsOffset.length - 1]) return;
|
|
504
|
+
set(drawerRef.current, { transform: isVertical(direction) ? `translate3d(0, ${newValue}px, 0)` : `translate3d(${newValue}px, 0, 0)` });
|
|
505
|
+
}
|
|
506
|
+
function getPercentageDragged(absDraggedDistance, isDraggingDown) {
|
|
507
|
+
if (!snapPoints || typeof activeSnapPointIndex !== "number" || !snapPointsOffset || fadeFromIndex === void 0) return null;
|
|
508
|
+
const isOverlaySnapPoint = activeSnapPointIndex === fadeFromIndex - 1;
|
|
509
|
+
if (activeSnapPointIndex >= fadeFromIndex && isDraggingDown) return 0;
|
|
510
|
+
if (isOverlaySnapPoint && !isDraggingDown) return 1;
|
|
511
|
+
if (!shouldFade && !isOverlaySnapPoint) return null;
|
|
512
|
+
const targetSnapPointIndex = isOverlaySnapPoint ? activeSnapPointIndex + 1 : activeSnapPointIndex - 1;
|
|
513
|
+
const snapPointDistance = isOverlaySnapPoint ? snapPointsOffset[targetSnapPointIndex] - snapPointsOffset[targetSnapPointIndex - 1] : snapPointsOffset[targetSnapPointIndex + 1] - snapPointsOffset[targetSnapPointIndex];
|
|
514
|
+
const percentageDragged = absDraggedDistance / Math.abs(snapPointDistance);
|
|
515
|
+
if (isOverlaySnapPoint) return 1 - percentageDragged;
|
|
516
|
+
else return percentageDragged;
|
|
517
|
+
}
|
|
518
|
+
return {
|
|
519
|
+
isLastSnapPoint,
|
|
520
|
+
activeSnapPoint,
|
|
521
|
+
shouldFade,
|
|
522
|
+
getPercentageDragged,
|
|
523
|
+
setActiveSnapPoint,
|
|
524
|
+
activeSnapPointIndex,
|
|
525
|
+
onRelease,
|
|
526
|
+
onDrag,
|
|
527
|
+
snapPointsOffset
|
|
528
|
+
};
|
|
710
529
|
}
|
|
711
530
|
|
|
712
|
-
|
|
531
|
+
//#endregion
|
|
532
|
+
//#region src/use-scale-background.ts
|
|
533
|
+
const noop = () => () => {};
|
|
713
534
|
function useScaleBackground() {
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
}
|
|
750
|
-
}, TRANSITIONS.DURATION * 1000);
|
|
751
|
-
};
|
|
752
|
-
}
|
|
753
|
-
}, [
|
|
754
|
-
isOpen,
|
|
755
|
-
shouldScaleBackground,
|
|
756
|
-
initialBackgroundColor
|
|
757
|
-
]);
|
|
535
|
+
const { direction, isOpen, shouldScaleBackground, setBackgroundColorOnScale, noBodyStyles } = useDrawerContext();
|
|
536
|
+
const timeoutIdRef = React.useRef(null);
|
|
537
|
+
const initialBackgroundColor = useMemo(() => document.body.style.backgroundColor, []);
|
|
538
|
+
function getScale() {
|
|
539
|
+
return (window.innerWidth - WINDOW_TOP_OFFSET) / window.innerWidth;
|
|
540
|
+
}
|
|
541
|
+
React.useEffect(() => {
|
|
542
|
+
if (isOpen && shouldScaleBackground) {
|
|
543
|
+
if (timeoutIdRef.current) clearTimeout(timeoutIdRef.current);
|
|
544
|
+
const wrapper = document.querySelector("[data-vaul-drawer-wrapper]") || document.querySelector("[vaul-drawer-wrapper]");
|
|
545
|
+
if (!wrapper) return;
|
|
546
|
+
chain(setBackgroundColorOnScale && !noBodyStyles ? assignStyle(document.body, { background: "black" }) : noop, assignStyle(wrapper, {
|
|
547
|
+
transformOrigin: isVertical(direction) ? "top" : "left",
|
|
548
|
+
transitionProperty: "transform, border-radius",
|
|
549
|
+
transitionDuration: `${TRANSITIONS.DURATION}s`,
|
|
550
|
+
transitionTimingFunction: `cubic-bezier(${TRANSITIONS.EASE.join(",")})`
|
|
551
|
+
}));
|
|
552
|
+
const wrapperStylesCleanup = assignStyle(wrapper, {
|
|
553
|
+
borderRadius: `${BORDER_RADIUS}px`,
|
|
554
|
+
overflow: "hidden",
|
|
555
|
+
...isVertical(direction) ? { transform: `scale(${getScale()}) translate3d(0, calc(env(safe-area-inset-top) + 14px), 0)` } : { transform: `scale(${getScale()}) translate3d(calc(env(safe-area-inset-top) + 14px), 0, 0)` }
|
|
556
|
+
});
|
|
557
|
+
return () => {
|
|
558
|
+
wrapperStylesCleanup();
|
|
559
|
+
timeoutIdRef.current = window.setTimeout(() => {
|
|
560
|
+
if (initialBackgroundColor) document.body.style.background = initialBackgroundColor;
|
|
561
|
+
else document.body.style.removeProperty("background");
|
|
562
|
+
}, TRANSITIONS.DURATION * 1e3);
|
|
563
|
+
};
|
|
564
|
+
}
|
|
565
|
+
}, [
|
|
566
|
+
isOpen,
|
|
567
|
+
shouldScaleBackground,
|
|
568
|
+
initialBackgroundColor
|
|
569
|
+
]);
|
|
758
570
|
}
|
|
759
571
|
|
|
572
|
+
//#endregion
|
|
573
|
+
//#region src/use-position-fixed.ts
|
|
760
574
|
let previousBodyPosition = null;
|
|
761
575
|
/**
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
React__default.useEffect(()=>{
|
|
849
|
-
if (nested || !hasBeenOpened) return;
|
|
850
|
-
// This is needed to force Safari toolbar to show **before** the drawer starts animating to prevent a gnarly shift from happening
|
|
851
|
-
if (isOpen) {
|
|
852
|
-
// avoid for standalone mode (PWA)
|
|
853
|
-
const isStandalone = window.matchMedia('(display-mode: standalone)').matches;
|
|
854
|
-
!isStandalone && setPositionFixed();
|
|
855
|
-
if (!modal) {
|
|
856
|
-
window.setTimeout(()=>{
|
|
857
|
-
restorePositionSetting();
|
|
858
|
-
}, 500);
|
|
859
|
-
}
|
|
860
|
-
} else {
|
|
861
|
-
restorePositionSetting();
|
|
862
|
-
}
|
|
863
|
-
}, [
|
|
864
|
-
isOpen,
|
|
865
|
-
hasBeenOpened,
|
|
866
|
-
activeUrl,
|
|
867
|
-
modal,
|
|
868
|
-
nested,
|
|
869
|
-
setPositionFixed,
|
|
870
|
-
restorePositionSetting
|
|
871
|
-
]);
|
|
872
|
-
return {
|
|
873
|
-
restorePositionSetting
|
|
874
|
-
};
|
|
576
|
+
* This hook is necessary to prevent buggy behavior on iOS devices (need to test on Android).
|
|
577
|
+
* I won't get into too much detail about what bugs it solves, but so far I've found that setting the body to `position: fixed` is the most reliable way to prevent those bugs.
|
|
578
|
+
* Issues that this hook solves:
|
|
579
|
+
* https://github.com/emilkowalski/vaul/issues/435
|
|
580
|
+
* https://github.com/emilkowalski/vaul/issues/433
|
|
581
|
+
* And more that I discovered, but were just not reported.
|
|
582
|
+
*/
|
|
583
|
+
function usePositionFixed({ isOpen, modal, nested, hasBeenOpened, preventScrollRestoration, noBodyStyles }) {
|
|
584
|
+
const [activeUrl, setActiveUrl] = React.useState(() => typeof window !== "undefined" ? window.location.href : "");
|
|
585
|
+
const scrollPos = React.useRef(0);
|
|
586
|
+
const setPositionFixed = React.useCallback(() => {
|
|
587
|
+
if (!isSafari()) return;
|
|
588
|
+
if (previousBodyPosition === null && isOpen && !noBodyStyles) {
|
|
589
|
+
previousBodyPosition = {
|
|
590
|
+
position: document.body.style.position,
|
|
591
|
+
top: document.body.style.top,
|
|
592
|
+
left: document.body.style.left,
|
|
593
|
+
height: document.body.style.height,
|
|
594
|
+
right: "unset"
|
|
595
|
+
};
|
|
596
|
+
const { scrollX, innerHeight } = window;
|
|
597
|
+
document.body.style.setProperty("position", "fixed", "important");
|
|
598
|
+
Object.assign(document.body.style, {
|
|
599
|
+
top: `${-scrollPos.current}px`,
|
|
600
|
+
left: `${-scrollX}px`,
|
|
601
|
+
right: "0px",
|
|
602
|
+
height: "auto"
|
|
603
|
+
});
|
|
604
|
+
window.setTimeout(() => window.requestAnimationFrame(() => {
|
|
605
|
+
const bottomBarHeight = innerHeight - window.innerHeight;
|
|
606
|
+
if (bottomBarHeight && scrollPos.current >= innerHeight) document.body.style.top = `${-(scrollPos.current + bottomBarHeight)}px`;
|
|
607
|
+
}), 300);
|
|
608
|
+
}
|
|
609
|
+
}, [isOpen]);
|
|
610
|
+
const restorePositionSetting = React.useCallback(() => {
|
|
611
|
+
if (!isSafari()) return;
|
|
612
|
+
if (previousBodyPosition !== null && !noBodyStyles) {
|
|
613
|
+
const y = -parseInt(document.body.style.top, 10);
|
|
614
|
+
const x = -parseInt(document.body.style.left, 10);
|
|
615
|
+
Object.assign(document.body.style, previousBodyPosition);
|
|
616
|
+
window.requestAnimationFrame(() => {
|
|
617
|
+
if (preventScrollRestoration && activeUrl !== window.location.href) {
|
|
618
|
+
setActiveUrl(window.location.href);
|
|
619
|
+
return;
|
|
620
|
+
}
|
|
621
|
+
window.scrollTo(x, y);
|
|
622
|
+
});
|
|
623
|
+
previousBodyPosition = null;
|
|
624
|
+
}
|
|
625
|
+
}, [activeUrl]);
|
|
626
|
+
React.useEffect(() => {
|
|
627
|
+
function onScroll() {
|
|
628
|
+
scrollPos.current = window.scrollY;
|
|
629
|
+
}
|
|
630
|
+
onScroll();
|
|
631
|
+
window.addEventListener("scroll", onScroll);
|
|
632
|
+
return () => {
|
|
633
|
+
window.removeEventListener("scroll", onScroll);
|
|
634
|
+
};
|
|
635
|
+
}, []);
|
|
636
|
+
React.useEffect(() => {
|
|
637
|
+
if (!modal) return;
|
|
638
|
+
return () => {
|
|
639
|
+
if (typeof document === "undefined") return;
|
|
640
|
+
if (!!document.querySelector("[data-vaul-drawer]")) return;
|
|
641
|
+
restorePositionSetting();
|
|
642
|
+
};
|
|
643
|
+
}, [modal, restorePositionSetting]);
|
|
644
|
+
React.useEffect(() => {
|
|
645
|
+
if (nested || !hasBeenOpened) return;
|
|
646
|
+
if (isOpen) {
|
|
647
|
+
!window.matchMedia("(display-mode: standalone)").matches && setPositionFixed();
|
|
648
|
+
if (!modal) window.setTimeout(() => {
|
|
649
|
+
restorePositionSetting();
|
|
650
|
+
}, 500);
|
|
651
|
+
} else restorePositionSetting();
|
|
652
|
+
}, [
|
|
653
|
+
isOpen,
|
|
654
|
+
hasBeenOpened,
|
|
655
|
+
activeUrl,
|
|
656
|
+
modal,
|
|
657
|
+
nested,
|
|
658
|
+
setPositionFixed,
|
|
659
|
+
restorePositionSetting
|
|
660
|
+
]);
|
|
661
|
+
return { restorePositionSetting };
|
|
875
662
|
}
|
|
876
663
|
|
|
664
|
+
//#endregion
|
|
665
|
+
//#region src/index.tsx
|
|
877
666
|
function Root({ open: openProp, onOpenChange, children, onDrag: onDragProp, onRelease: onReleaseProp, snapPoints, shouldScaleBackground = false, setBackgroundColorOnScale = true, closeThreshold = CLOSE_THRESHOLD, scrollLockTimeout = SCROLL_LOCK_TIMEOUT, dismissible = true, handleOnly = false, fadeFromIndex = snapPoints && snapPoints.length - 1, activeSnapPoint: activeSnapPointProp, setActiveSnapPoint: setActiveSnapPointProp, fixed, modal = true, onClose, nested, noBodyStyles = false, direction = "bottom", defaultOpen = false, disablePreventScroll = true, snapToSequentialPoint = false, preventScrollRestoration = false, repositionInputs = true, onAnimationEnd, container, autoFocus = false }) {
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
return;
|
|
1260
|
-
}
|
|
1261
|
-
onReleaseProp?.(event, true);
|
|
1262
|
-
resetDrawer();
|
|
1263
|
-
}
|
|
1264
|
-
React__default.useEffect(()=>{
|
|
1265
|
-
// Trigger enter animation without using CSS animation
|
|
1266
|
-
if (isOpen) {
|
|
1267
|
-
set(document.documentElement, {
|
|
1268
|
-
scrollBehavior: "auto"
|
|
1269
|
-
});
|
|
1270
|
-
openTime.current = new Date();
|
|
1271
|
-
}
|
|
1272
|
-
return ()=>{
|
|
1273
|
-
reset(document.documentElement, "scrollBehavior");
|
|
1274
|
-
};
|
|
1275
|
-
}, [
|
|
1276
|
-
isOpen
|
|
1277
|
-
]);
|
|
1278
|
-
function onNestedOpenChange(o) {
|
|
1279
|
-
const scale = o ? (window.innerWidth - NESTED_DISPLACEMENT) / window.innerWidth : 1;
|
|
1280
|
-
const initialTranslate = o ? -NESTED_DISPLACEMENT : 0;
|
|
1281
|
-
if (nestedOpenChangeTimer.current) {
|
|
1282
|
-
window.clearTimeout(nestedOpenChangeTimer.current);
|
|
1283
|
-
}
|
|
1284
|
-
set(drawerRef.current, {
|
|
1285
|
-
transition: `transform ${TRANSITIONS.DURATION}s cubic-bezier(${TRANSITIONS.EASE.join(",")})`,
|
|
1286
|
-
transform: isVertical(direction) ? `scale(${scale}) translate3d(0, ${initialTranslate}px, 0)` : `scale(${scale}) translate3d(${initialTranslate}px, 0, 0)`
|
|
1287
|
-
});
|
|
1288
|
-
if (!o && drawerRef.current) {
|
|
1289
|
-
nestedOpenChangeTimer.current = setTimeout(()=>{
|
|
1290
|
-
const translateValue = getTranslate(drawerRef.current, direction);
|
|
1291
|
-
set(drawerRef.current, {
|
|
1292
|
-
transition: "none",
|
|
1293
|
-
transform: isVertical(direction) ? `translate3d(0, ${translateValue}px, 0)` : `translate3d(${translateValue}px, 0, 0)`
|
|
1294
|
-
});
|
|
1295
|
-
}, 500);
|
|
1296
|
-
}
|
|
1297
|
-
}
|
|
1298
|
-
function onNestedDrag(_event, percentageDragged) {
|
|
1299
|
-
if (percentageDragged < 0) return;
|
|
1300
|
-
const initialScale = (window.innerWidth - NESTED_DISPLACEMENT) / window.innerWidth;
|
|
1301
|
-
const newScale = initialScale + percentageDragged * (1 - initialScale);
|
|
1302
|
-
const newTranslate = -NESTED_DISPLACEMENT + percentageDragged * NESTED_DISPLACEMENT;
|
|
1303
|
-
set(drawerRef.current, {
|
|
1304
|
-
transform: isVertical(direction) ? `scale(${newScale}) translate3d(0, ${newTranslate}px, 0)` : `scale(${newScale}) translate3d(${newTranslate}px, 0, 0)`,
|
|
1305
|
-
transition: "none"
|
|
1306
|
-
});
|
|
1307
|
-
}
|
|
1308
|
-
function onNestedRelease(_event, o) {
|
|
1309
|
-
const dim = isVertical(direction) ? window.innerHeight : window.innerWidth;
|
|
1310
|
-
const scale = o ? (dim - NESTED_DISPLACEMENT) / dim : 1;
|
|
1311
|
-
const translate = o ? -NESTED_DISPLACEMENT : 0;
|
|
1312
|
-
if (o) {
|
|
1313
|
-
set(drawerRef.current, {
|
|
1314
|
-
transition: `transform ${TRANSITIONS.DURATION}s cubic-bezier(${TRANSITIONS.EASE.join(",")})`,
|
|
1315
|
-
transform: isVertical(direction) ? `scale(${scale}) translate3d(0, ${translate}px, 0)` : `scale(${scale}) translate3d(${translate}px, 0, 0)`
|
|
1316
|
-
});
|
|
1317
|
-
}
|
|
1318
|
-
}
|
|
1319
|
-
React__default.useEffect(()=>{
|
|
1320
|
-
if (!modal) {
|
|
1321
|
-
// Need to do this manually unfortunately
|
|
1322
|
-
window.requestAnimationFrame(()=>{
|
|
1323
|
-
document.body.style.pointerEvents = "auto";
|
|
1324
|
-
});
|
|
1325
|
-
}
|
|
1326
|
-
}, [
|
|
1327
|
-
modal
|
|
1328
|
-
]);
|
|
1329
|
-
return /*#__PURE__*/ jsx(Dialog.Root, {
|
|
1330
|
-
defaultOpen: defaultOpen,
|
|
1331
|
-
onOpenChange: (open)=>{
|
|
1332
|
-
if (!dismissible && !open) return;
|
|
1333
|
-
if (open) {
|
|
1334
|
-
setHasBeenOpened(true);
|
|
1335
|
-
} else {
|
|
1336
|
-
closeDrawer(true);
|
|
1337
|
-
}
|
|
1338
|
-
setIsOpen(open);
|
|
1339
|
-
},
|
|
1340
|
-
open: isOpen,
|
|
1341
|
-
modal: modal,
|
|
1342
|
-
children: /*#__PURE__*/ jsx(DrawerContext.Provider, {
|
|
1343
|
-
value: {
|
|
1344
|
-
activeSnapPoint,
|
|
1345
|
-
snapPoints,
|
|
1346
|
-
setActiveSnapPoint,
|
|
1347
|
-
drawerRef,
|
|
1348
|
-
overlayRef,
|
|
1349
|
-
onOpenChange,
|
|
1350
|
-
onPress,
|
|
1351
|
-
onRelease,
|
|
1352
|
-
onDrag,
|
|
1353
|
-
dismissible,
|
|
1354
|
-
shouldAnimate,
|
|
1355
|
-
handleOnly,
|
|
1356
|
-
isOpen,
|
|
1357
|
-
isDragging,
|
|
1358
|
-
shouldFade,
|
|
1359
|
-
closeDrawer,
|
|
1360
|
-
onNestedDrag,
|
|
1361
|
-
onNestedOpenChange,
|
|
1362
|
-
onNestedRelease,
|
|
1363
|
-
keyboardIsOpen,
|
|
1364
|
-
modal,
|
|
1365
|
-
snapPointsOffset,
|
|
1366
|
-
activeSnapPointIndex,
|
|
1367
|
-
direction,
|
|
1368
|
-
shouldScaleBackground,
|
|
1369
|
-
setBackgroundColorOnScale,
|
|
1370
|
-
noBodyStyles,
|
|
1371
|
-
container,
|
|
1372
|
-
autoFocus
|
|
1373
|
-
},
|
|
1374
|
-
children: children
|
|
1375
|
-
})
|
|
1376
|
-
});
|
|
667
|
+
const [isOpen = false, setIsOpen] = useControllableState({
|
|
668
|
+
defaultProp: defaultOpen,
|
|
669
|
+
prop: openProp,
|
|
670
|
+
onChange: (o) => {
|
|
671
|
+
onOpenChange?.(o);
|
|
672
|
+
if (!o && !nested) restorePositionSetting();
|
|
673
|
+
setTimeout(() => {
|
|
674
|
+
onAnimationEnd?.(o);
|
|
675
|
+
}, TRANSITIONS.DURATION * 1e3);
|
|
676
|
+
if (o && !modal) {
|
|
677
|
+
if (typeof window !== "undefined") window.requestAnimationFrame(() => {
|
|
678
|
+
document.body.style.pointerEvents = "auto";
|
|
679
|
+
});
|
|
680
|
+
}
|
|
681
|
+
if (!o) document.body.style.pointerEvents = "auto";
|
|
682
|
+
}
|
|
683
|
+
});
|
|
684
|
+
const [hasBeenOpened, setHasBeenOpened] = React.useState(false);
|
|
685
|
+
const [isDragging, setIsDragging] = React.useState(false);
|
|
686
|
+
const [justReleased, setJustReleased] = React.useState(false);
|
|
687
|
+
const overlayRef = React.useRef(null);
|
|
688
|
+
const openTime = React.useRef(null);
|
|
689
|
+
const dragStartTime = React.useRef(null);
|
|
690
|
+
const dragEndTime = React.useRef(null);
|
|
691
|
+
const lastTimeDragPrevented = React.useRef(null);
|
|
692
|
+
const isAllowedToDrag = React.useRef(false);
|
|
693
|
+
const nestedOpenChangeTimer = React.useRef(null);
|
|
694
|
+
const pointerStart = React.useRef(0);
|
|
695
|
+
const keyboardIsOpen = React.useRef(false);
|
|
696
|
+
const shouldAnimate = React.useRef(!defaultOpen);
|
|
697
|
+
const previousDiffFromInitial = React.useRef(0);
|
|
698
|
+
const drawerRef = React.useRef(null);
|
|
699
|
+
const drawerHeightRef = React.useRef(drawerRef.current?.getBoundingClientRect().height || 0);
|
|
700
|
+
const drawerWidthRef = React.useRef(drawerRef.current?.getBoundingClientRect().width || 0);
|
|
701
|
+
const initialDrawerHeight = React.useRef(0);
|
|
702
|
+
const { activeSnapPoint, activeSnapPointIndex, setActiveSnapPoint, onRelease: onReleaseSnapPoints, snapPointsOffset, onDrag: onDragSnapPoints, shouldFade, getPercentageDragged: getSnapPointsPercentageDragged } = useSnapPoints({
|
|
703
|
+
snapPoints,
|
|
704
|
+
activeSnapPointProp,
|
|
705
|
+
setActiveSnapPointProp,
|
|
706
|
+
drawerRef,
|
|
707
|
+
fadeFromIndex,
|
|
708
|
+
overlayRef,
|
|
709
|
+
onSnapPointChange: React.useCallback((activeSnapPointIndex$1) => {
|
|
710
|
+
if (snapPoints && activeSnapPointIndex$1 === snapPointsOffset.length - 1) openTime.current = /* @__PURE__ */ new Date();
|
|
711
|
+
}, []),
|
|
712
|
+
direction,
|
|
713
|
+
container,
|
|
714
|
+
snapToSequentialPoint
|
|
715
|
+
});
|
|
716
|
+
usePreventScroll({ isDisabled: !isOpen || isDragging || !modal || justReleased || !hasBeenOpened || !repositionInputs || !disablePreventScroll });
|
|
717
|
+
const { restorePositionSetting } = usePositionFixed({
|
|
718
|
+
isOpen,
|
|
719
|
+
modal,
|
|
720
|
+
nested: nested ?? false,
|
|
721
|
+
hasBeenOpened,
|
|
722
|
+
preventScrollRestoration,
|
|
723
|
+
noBodyStyles
|
|
724
|
+
});
|
|
725
|
+
function getScale() {
|
|
726
|
+
return (window.innerWidth - WINDOW_TOP_OFFSET) / window.innerWidth;
|
|
727
|
+
}
|
|
728
|
+
function onPress(event) {
|
|
729
|
+
if (!dismissible && !snapPoints) return;
|
|
730
|
+
if (drawerRef.current && !drawerRef.current.contains(event.target)) return;
|
|
731
|
+
drawerHeightRef.current = drawerRef.current?.getBoundingClientRect().height || 0;
|
|
732
|
+
drawerWidthRef.current = drawerRef.current?.getBoundingClientRect().width || 0;
|
|
733
|
+
setIsDragging(true);
|
|
734
|
+
dragStartTime.current = /* @__PURE__ */ new Date();
|
|
735
|
+
if (isIOS()) window.addEventListener("touchend", () => isAllowedToDrag.current = false, { once: true });
|
|
736
|
+
event.target.setPointerCapture(event.pointerId);
|
|
737
|
+
pointerStart.current = isVertical(direction) ? event.pageY : event.pageX;
|
|
738
|
+
}
|
|
739
|
+
function shouldDrag(el, isDraggingInDirection) {
|
|
740
|
+
let element = el;
|
|
741
|
+
const highlightedText = window.getSelection()?.toString();
|
|
742
|
+
const swipeAmount = drawerRef.current ? getTranslate(drawerRef.current, direction) : null;
|
|
743
|
+
const date = /* @__PURE__ */ new Date();
|
|
744
|
+
if (element.tagName === "SELECT") return false;
|
|
745
|
+
if (element.hasAttribute("data-vaul-no-drag") || element.closest("[data-vaul-no-drag]")) return false;
|
|
746
|
+
if (direction === "right" || direction === "left") return true;
|
|
747
|
+
if (openTime.current && date.getTime() - openTime.current.getTime() < 500) return false;
|
|
748
|
+
if (swipeAmount !== null) {
|
|
749
|
+
if (direction === "bottom" ? swipeAmount > 0 : swipeAmount < 0) return true;
|
|
750
|
+
}
|
|
751
|
+
if (highlightedText && highlightedText.length > 0) return false;
|
|
752
|
+
if (lastTimeDragPrevented.current && date.getTime() - lastTimeDragPrevented.current.getTime() < scrollLockTimeout && swipeAmount === 0) {
|
|
753
|
+
lastTimeDragPrevented.current = date;
|
|
754
|
+
return false;
|
|
755
|
+
}
|
|
756
|
+
if (isDraggingInDirection) {
|
|
757
|
+
lastTimeDragPrevented.current = date;
|
|
758
|
+
return false;
|
|
759
|
+
}
|
|
760
|
+
while (element) {
|
|
761
|
+
if (element.scrollHeight > element.clientHeight) {
|
|
762
|
+
if (element.scrollTop !== 0) {
|
|
763
|
+
lastTimeDragPrevented.current = /* @__PURE__ */ new Date();
|
|
764
|
+
return false;
|
|
765
|
+
}
|
|
766
|
+
if (element.getAttribute("role") === "dialog") return true;
|
|
767
|
+
}
|
|
768
|
+
element = element.parentNode;
|
|
769
|
+
}
|
|
770
|
+
return true;
|
|
771
|
+
}
|
|
772
|
+
function onDrag(event) {
|
|
773
|
+
if (!drawerRef.current) return;
|
|
774
|
+
if (isDragging) {
|
|
775
|
+
const directionMultiplier = direction === "bottom" || direction === "right" ? 1 : -1;
|
|
776
|
+
const draggedDistance = (pointerStart.current - (isVertical(direction) ? event.pageY : event.pageX)) * directionMultiplier;
|
|
777
|
+
const isDraggingInDirection = draggedDistance > 0;
|
|
778
|
+
const noCloseSnapPointsPreCondition = snapPoints && !dismissible && !isDraggingInDirection;
|
|
779
|
+
if (noCloseSnapPointsPreCondition && activeSnapPointIndex === 0) return;
|
|
780
|
+
const absDraggedDistance = Math.abs(draggedDistance);
|
|
781
|
+
const wrapper = document.querySelector("[data-vaul-drawer-wrapper]");
|
|
782
|
+
let percentageDragged = absDraggedDistance / (direction === "bottom" || direction === "top" ? drawerHeightRef.current : drawerWidthRef.current);
|
|
783
|
+
const snapPointPercentageDragged = getSnapPointsPercentageDragged(absDraggedDistance, isDraggingInDirection);
|
|
784
|
+
if (snapPointPercentageDragged !== null) percentageDragged = snapPointPercentageDragged;
|
|
785
|
+
if (noCloseSnapPointsPreCondition && percentageDragged >= 1) return;
|
|
786
|
+
if (!isAllowedToDrag.current && !shouldDrag(event.target, isDraggingInDirection)) return;
|
|
787
|
+
drawerRef.current.classList.add(DRAG_CLASS);
|
|
788
|
+
isAllowedToDrag.current = true;
|
|
789
|
+
set(drawerRef.current, { transition: "none" });
|
|
790
|
+
set(overlayRef.current, { transition: "none" });
|
|
791
|
+
if (snapPoints) onDragSnapPoints({ draggedDistance });
|
|
792
|
+
if (isDraggingInDirection && !snapPoints) {
|
|
793
|
+
const dampenedDraggedDistance = dampenValue(draggedDistance);
|
|
794
|
+
const translateValue = Math.min(dampenedDraggedDistance * -1, 0) * directionMultiplier;
|
|
795
|
+
set(drawerRef.current, { transform: isVertical(direction) ? `translate3d(0, ${translateValue}px, 0)` : `translate3d(${translateValue}px, 0, 0)` });
|
|
796
|
+
return;
|
|
797
|
+
}
|
|
798
|
+
const opacityValue = 1 - percentageDragged;
|
|
799
|
+
if (shouldFade || fadeFromIndex && activeSnapPointIndex === fadeFromIndex - 1) {
|
|
800
|
+
onDragProp?.(event, percentageDragged);
|
|
801
|
+
set(overlayRef.current, {
|
|
802
|
+
opacity: `${opacityValue}`,
|
|
803
|
+
transition: "none"
|
|
804
|
+
}, true);
|
|
805
|
+
}
|
|
806
|
+
if (wrapper && overlayRef.current && shouldScaleBackground) {
|
|
807
|
+
const scaleValue = Math.min(getScale() + percentageDragged * (1 - getScale()), 1);
|
|
808
|
+
const borderRadiusValue = 8 - percentageDragged * 8;
|
|
809
|
+
const translateValue = Math.max(0, 14 - percentageDragged * 14);
|
|
810
|
+
set(wrapper, {
|
|
811
|
+
borderRadius: `${borderRadiusValue}px`,
|
|
812
|
+
transform: isVertical(direction) ? `scale(${scaleValue}) translate3d(0, ${translateValue}px, 0)` : `scale(${scaleValue}) translate3d(${translateValue}px, 0, 0)`,
|
|
813
|
+
transition: "none"
|
|
814
|
+
}, true);
|
|
815
|
+
}
|
|
816
|
+
if (!snapPoints) {
|
|
817
|
+
const translateValue = absDraggedDistance * directionMultiplier;
|
|
818
|
+
set(drawerRef.current, { transform: isVertical(direction) ? `translate3d(0, ${translateValue}px, 0)` : `translate3d(${translateValue}px, 0, 0)` });
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
React.useEffect(() => {
|
|
823
|
+
window.requestAnimationFrame(() => {
|
|
824
|
+
shouldAnimate.current = true;
|
|
825
|
+
});
|
|
826
|
+
}, []);
|
|
827
|
+
React.useEffect(() => {
|
|
828
|
+
function onVisualViewportChange() {
|
|
829
|
+
if (!drawerRef.current || !repositionInputs) return;
|
|
830
|
+
const focusedElement = document.activeElement;
|
|
831
|
+
if (isInput(focusedElement) || keyboardIsOpen.current) {
|
|
832
|
+
const visualViewportHeight = window.visualViewport?.height || 0;
|
|
833
|
+
const totalHeight = window.innerHeight;
|
|
834
|
+
let diffFromInitial = totalHeight - visualViewportHeight;
|
|
835
|
+
const drawerHeight = drawerRef.current.getBoundingClientRect().height || 0;
|
|
836
|
+
const isTallEnough = drawerHeight > totalHeight * .8;
|
|
837
|
+
if (!initialDrawerHeight.current) initialDrawerHeight.current = drawerHeight;
|
|
838
|
+
const offsetFromTop = drawerRef.current.getBoundingClientRect().top;
|
|
839
|
+
if (Math.abs(previousDiffFromInitial.current - diffFromInitial) > 60) keyboardIsOpen.current = !keyboardIsOpen.current;
|
|
840
|
+
if (snapPoints && snapPoints.length > 0 && snapPointsOffset && activeSnapPointIndex) {
|
|
841
|
+
const activeSnapPointHeight = snapPointsOffset[activeSnapPointIndex] || 0;
|
|
842
|
+
diffFromInitial += activeSnapPointHeight;
|
|
843
|
+
}
|
|
844
|
+
previousDiffFromInitial.current = diffFromInitial;
|
|
845
|
+
if (drawerHeight > visualViewportHeight || keyboardIsOpen.current) {
|
|
846
|
+
const height = drawerRef.current.getBoundingClientRect().height;
|
|
847
|
+
let newDrawerHeight = height;
|
|
848
|
+
if (height > visualViewportHeight) newDrawerHeight = visualViewportHeight - (isTallEnough ? offsetFromTop : WINDOW_TOP_OFFSET);
|
|
849
|
+
if (fixed) drawerRef.current.style.height = `${height - Math.max(diffFromInitial, 0)}px`;
|
|
850
|
+
else drawerRef.current.style.height = `${Math.max(newDrawerHeight, visualViewportHeight - offsetFromTop)}px`;
|
|
851
|
+
} else if (!isMobileFirefox()) drawerRef.current.style.height = `${initialDrawerHeight.current}px`;
|
|
852
|
+
if (snapPoints && snapPoints.length > 0 && !keyboardIsOpen.current) drawerRef.current.style.bottom = `0px`;
|
|
853
|
+
else drawerRef.current.style.bottom = `${Math.max(diffFromInitial, 0)}px`;
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
window.visualViewport?.addEventListener("resize", onVisualViewportChange);
|
|
857
|
+
return () => window.visualViewport?.removeEventListener("resize", onVisualViewportChange);
|
|
858
|
+
}, [
|
|
859
|
+
activeSnapPointIndex,
|
|
860
|
+
snapPoints,
|
|
861
|
+
snapPointsOffset
|
|
862
|
+
]);
|
|
863
|
+
function closeDrawer(fromWithin) {
|
|
864
|
+
cancelDrag();
|
|
865
|
+
onClose?.();
|
|
866
|
+
if (!fromWithin) setIsOpen(false);
|
|
867
|
+
setTimeout(() => {
|
|
868
|
+
if (snapPoints) setActiveSnapPoint(snapPoints[0]);
|
|
869
|
+
}, TRANSITIONS.DURATION * 1e3);
|
|
870
|
+
}
|
|
871
|
+
function resetDrawer() {
|
|
872
|
+
if (!drawerRef.current) return;
|
|
873
|
+
const wrapper = document.querySelector("[data-vaul-drawer-wrapper]");
|
|
874
|
+
const currentSwipeAmount = getTranslate(drawerRef.current, direction);
|
|
875
|
+
set(drawerRef.current, {
|
|
876
|
+
transform: "translate3d(0, 0, 0)",
|
|
877
|
+
transition: `transform ${TRANSITIONS.DURATION}s cubic-bezier(${TRANSITIONS.EASE.join(",")})`
|
|
878
|
+
});
|
|
879
|
+
set(overlayRef.current, {
|
|
880
|
+
transition: `opacity ${TRANSITIONS.DURATION}s cubic-bezier(${TRANSITIONS.EASE.join(",")})`,
|
|
881
|
+
opacity: "1"
|
|
882
|
+
});
|
|
883
|
+
if (shouldScaleBackground && currentSwipeAmount && currentSwipeAmount > 0 && isOpen) set(wrapper, {
|
|
884
|
+
borderRadius: `${BORDER_RADIUS}px`,
|
|
885
|
+
overflow: "hidden",
|
|
886
|
+
...isVertical(direction) ? {
|
|
887
|
+
transform: `scale(${getScale()}) translate3d(0, calc(env(safe-area-inset-top) + 14px), 0)`,
|
|
888
|
+
transformOrigin: "top"
|
|
889
|
+
} : {
|
|
890
|
+
transform: `scale(${getScale()}) translate3d(calc(env(safe-area-inset-top) + 14px), 0, 0)`,
|
|
891
|
+
transformOrigin: "left"
|
|
892
|
+
},
|
|
893
|
+
transitionProperty: "transform, border-radius",
|
|
894
|
+
transitionDuration: `${TRANSITIONS.DURATION}s`,
|
|
895
|
+
transitionTimingFunction: `cubic-bezier(${TRANSITIONS.EASE.join(",")})`
|
|
896
|
+
}, true);
|
|
897
|
+
}
|
|
898
|
+
function cancelDrag() {
|
|
899
|
+
if (!isDragging || !drawerRef.current) return;
|
|
900
|
+
drawerRef.current.classList.remove(DRAG_CLASS);
|
|
901
|
+
isAllowedToDrag.current = false;
|
|
902
|
+
setIsDragging(false);
|
|
903
|
+
dragEndTime.current = /* @__PURE__ */ new Date();
|
|
904
|
+
}
|
|
905
|
+
function onRelease(event) {
|
|
906
|
+
if (!isDragging || !drawerRef.current) return;
|
|
907
|
+
drawerRef.current.classList.remove(DRAG_CLASS);
|
|
908
|
+
isAllowedToDrag.current = false;
|
|
909
|
+
setIsDragging(false);
|
|
910
|
+
dragEndTime.current = /* @__PURE__ */ new Date();
|
|
911
|
+
const swipeAmount = getTranslate(drawerRef.current, direction);
|
|
912
|
+
if (!event || !shouldDrag(event.target, false) || !swipeAmount || Number.isNaN(swipeAmount)) return;
|
|
913
|
+
if (dragStartTime.current === null) return;
|
|
914
|
+
const timeTaken = dragEndTime.current.getTime() - dragStartTime.current.getTime();
|
|
915
|
+
const distMoved = pointerStart.current - (isVertical(direction) ? event.pageY : event.pageX);
|
|
916
|
+
const velocity = Math.abs(distMoved) / timeTaken;
|
|
917
|
+
if (velocity > .05) {
|
|
918
|
+
setJustReleased(true);
|
|
919
|
+
setTimeout(() => {
|
|
920
|
+
setJustReleased(false);
|
|
921
|
+
}, 200);
|
|
922
|
+
}
|
|
923
|
+
if (snapPoints) {
|
|
924
|
+
onReleaseSnapPoints({
|
|
925
|
+
draggedDistance: distMoved * (direction === "bottom" || direction === "right" ? 1 : -1),
|
|
926
|
+
closeDrawer,
|
|
927
|
+
velocity,
|
|
928
|
+
dismissible
|
|
929
|
+
});
|
|
930
|
+
onReleaseProp?.(event, true);
|
|
931
|
+
return;
|
|
932
|
+
}
|
|
933
|
+
if (direction === "bottom" || direction === "right" ? distMoved > 0 : distMoved < 0) {
|
|
934
|
+
resetDrawer();
|
|
935
|
+
onReleaseProp?.(event, true);
|
|
936
|
+
return;
|
|
937
|
+
}
|
|
938
|
+
if (velocity > VELOCITY_THRESHOLD) {
|
|
939
|
+
closeDrawer();
|
|
940
|
+
onReleaseProp?.(event, false);
|
|
941
|
+
return;
|
|
942
|
+
}
|
|
943
|
+
const visibleDrawerHeight = Math.min(drawerRef.current.getBoundingClientRect().height ?? 0, window.innerHeight);
|
|
944
|
+
const visibleDrawerWidth = Math.min(drawerRef.current.getBoundingClientRect().width ?? 0, window.innerWidth);
|
|
945
|
+
const isHorizontalSwipe = direction === "left" || direction === "right";
|
|
946
|
+
if (Math.abs(swipeAmount) >= (isHorizontalSwipe ? visibleDrawerWidth : visibleDrawerHeight) * closeThreshold) {
|
|
947
|
+
closeDrawer();
|
|
948
|
+
onReleaseProp?.(event, false);
|
|
949
|
+
return;
|
|
950
|
+
}
|
|
951
|
+
onReleaseProp?.(event, true);
|
|
952
|
+
resetDrawer();
|
|
953
|
+
}
|
|
954
|
+
React.useEffect(() => {
|
|
955
|
+
if (isOpen) {
|
|
956
|
+
set(document.documentElement, { scrollBehavior: "auto" });
|
|
957
|
+
openTime.current = /* @__PURE__ */ new Date();
|
|
958
|
+
}
|
|
959
|
+
return () => {
|
|
960
|
+
reset(document.documentElement, "scrollBehavior");
|
|
961
|
+
};
|
|
962
|
+
}, [isOpen]);
|
|
963
|
+
function onNestedOpenChange(o) {
|
|
964
|
+
const scale = o ? (window.innerWidth - NESTED_DISPLACEMENT) / window.innerWidth : 1;
|
|
965
|
+
const initialTranslate = o ? -NESTED_DISPLACEMENT : 0;
|
|
966
|
+
if (nestedOpenChangeTimer.current) window.clearTimeout(nestedOpenChangeTimer.current);
|
|
967
|
+
set(drawerRef.current, {
|
|
968
|
+
transition: `transform ${TRANSITIONS.DURATION}s cubic-bezier(${TRANSITIONS.EASE.join(",")})`,
|
|
969
|
+
transform: isVertical(direction) ? `scale(${scale}) translate3d(0, ${initialTranslate}px, 0)` : `scale(${scale}) translate3d(${initialTranslate}px, 0, 0)`
|
|
970
|
+
});
|
|
971
|
+
if (!o && drawerRef.current) nestedOpenChangeTimer.current = setTimeout(() => {
|
|
972
|
+
const translateValue = getTranslate(drawerRef.current, direction);
|
|
973
|
+
set(drawerRef.current, {
|
|
974
|
+
transition: "none",
|
|
975
|
+
transform: isVertical(direction) ? `translate3d(0, ${translateValue}px, 0)` : `translate3d(${translateValue}px, 0, 0)`
|
|
976
|
+
});
|
|
977
|
+
}, 500);
|
|
978
|
+
}
|
|
979
|
+
function onNestedDrag(_event, percentageDragged) {
|
|
980
|
+
if (percentageDragged < 0) return;
|
|
981
|
+
const initialScale = (window.innerWidth - NESTED_DISPLACEMENT) / window.innerWidth;
|
|
982
|
+
const newScale = initialScale + percentageDragged * (1 - initialScale);
|
|
983
|
+
const newTranslate = -NESTED_DISPLACEMENT + percentageDragged * NESTED_DISPLACEMENT;
|
|
984
|
+
set(drawerRef.current, {
|
|
985
|
+
transform: isVertical(direction) ? `scale(${newScale}) translate3d(0, ${newTranslate}px, 0)` : `scale(${newScale}) translate3d(${newTranslate}px, 0, 0)`,
|
|
986
|
+
transition: "none"
|
|
987
|
+
});
|
|
988
|
+
}
|
|
989
|
+
function onNestedRelease(_event, o) {
|
|
990
|
+
const dim = isVertical(direction) ? window.innerHeight : window.innerWidth;
|
|
991
|
+
const scale = o ? (dim - NESTED_DISPLACEMENT) / dim : 1;
|
|
992
|
+
const translate = o ? -NESTED_DISPLACEMENT : 0;
|
|
993
|
+
if (o) set(drawerRef.current, {
|
|
994
|
+
transition: `transform ${TRANSITIONS.DURATION}s cubic-bezier(${TRANSITIONS.EASE.join(",")})`,
|
|
995
|
+
transform: isVertical(direction) ? `scale(${scale}) translate3d(0, ${translate}px, 0)` : `scale(${scale}) translate3d(${translate}px, 0, 0)`
|
|
996
|
+
});
|
|
997
|
+
}
|
|
998
|
+
React.useEffect(() => {
|
|
999
|
+
if (!modal) window.requestAnimationFrame(() => {
|
|
1000
|
+
document.body.style.pointerEvents = "auto";
|
|
1001
|
+
});
|
|
1002
|
+
}, [modal]);
|
|
1003
|
+
return /* @__PURE__ */ jsx(Dialog.Root, {
|
|
1004
|
+
defaultOpen,
|
|
1005
|
+
onOpenChange: (open) => {
|
|
1006
|
+
if (!dismissible && !open) return;
|
|
1007
|
+
if (open) setHasBeenOpened(true);
|
|
1008
|
+
else closeDrawer(true);
|
|
1009
|
+
setIsOpen(open);
|
|
1010
|
+
},
|
|
1011
|
+
open: isOpen,
|
|
1012
|
+
modal,
|
|
1013
|
+
children: /* @__PURE__ */ jsx(DrawerContext.Provider, {
|
|
1014
|
+
value: {
|
|
1015
|
+
activeSnapPoint,
|
|
1016
|
+
snapPoints,
|
|
1017
|
+
setActiveSnapPoint,
|
|
1018
|
+
drawerRef,
|
|
1019
|
+
overlayRef,
|
|
1020
|
+
onOpenChange,
|
|
1021
|
+
onPress,
|
|
1022
|
+
onRelease,
|
|
1023
|
+
onDrag,
|
|
1024
|
+
dismissible,
|
|
1025
|
+
shouldAnimate,
|
|
1026
|
+
handleOnly,
|
|
1027
|
+
isOpen,
|
|
1028
|
+
isDragging,
|
|
1029
|
+
shouldFade,
|
|
1030
|
+
closeDrawer,
|
|
1031
|
+
onNestedDrag,
|
|
1032
|
+
onNestedOpenChange,
|
|
1033
|
+
onNestedRelease,
|
|
1034
|
+
keyboardIsOpen,
|
|
1035
|
+
modal,
|
|
1036
|
+
snapPointsOffset,
|
|
1037
|
+
activeSnapPointIndex,
|
|
1038
|
+
direction,
|
|
1039
|
+
shouldScaleBackground,
|
|
1040
|
+
setBackgroundColorOnScale,
|
|
1041
|
+
noBodyStyles,
|
|
1042
|
+
container,
|
|
1043
|
+
autoFocus
|
|
1044
|
+
},
|
|
1045
|
+
children
|
|
1046
|
+
})
|
|
1047
|
+
});
|
|
1377
1048
|
}
|
|
1378
|
-
const Overlay =
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
"data-vaul-snap-points": isOpen && hasSnapPoints ? "true" : "false",
|
|
1394
|
-
"data-vaul-snap-points-overlay": isOpen && shouldFade ? "true" : "false",
|
|
1395
|
-
"data-vaul-animate": shouldAnimate?.current ? "true" : "false",
|
|
1396
|
-
...rest
|
|
1397
|
-
});
|
|
1049
|
+
const Overlay = React.forwardRef(function({ ...rest }, ref) {
|
|
1050
|
+
const { overlayRef, snapPoints, onRelease, shouldFade, isOpen, modal, shouldAnimate } = useDrawerContext();
|
|
1051
|
+
const composedRef = useComposedRefs(ref, overlayRef);
|
|
1052
|
+
const hasSnapPoints = snapPoints && snapPoints.length > 0;
|
|
1053
|
+
const onMouseUp = React.useCallback((event) => onRelease(event), [onRelease]);
|
|
1054
|
+
if (!modal) return null;
|
|
1055
|
+
return /* @__PURE__ */ jsx(Dialog.Backdrop, {
|
|
1056
|
+
onMouseUp,
|
|
1057
|
+
ref: composedRef,
|
|
1058
|
+
"data-vaul-overlay": "",
|
|
1059
|
+
"data-vaul-snap-points": isOpen && hasSnapPoints ? "true" : "false",
|
|
1060
|
+
"data-vaul-snap-points-overlay": isOpen && shouldFade ? "true" : "false",
|
|
1061
|
+
"data-vaul-animate": shouldAnimate?.current ? "true" : "false",
|
|
1062
|
+
...rest
|
|
1063
|
+
});
|
|
1398
1064
|
});
|
|
1399
1065
|
Overlay.displayName = "Drawer.Overlay";
|
|
1400
|
-
const Content =
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
if (isAllowedToSwipe) onDrag(event);
|
|
1483
|
-
else if (Math.abs(xPosition) > swipeStartThreshold || Math.abs(yPosition) > swipeStartThreshold) {
|
|
1484
|
-
pointerStartRef.current = null;
|
|
1485
|
-
}
|
|
1486
|
-
},
|
|
1487
|
-
onPointerUp: (event)=>{
|
|
1488
|
-
rest.onPointerUp?.(event);
|
|
1489
|
-
pointerStartRef.current = null;
|
|
1490
|
-
wasBeyondThePointRef.current = false;
|
|
1491
|
-
onRelease(event);
|
|
1492
|
-
},
|
|
1493
|
-
onPointerOut: (event)=>{
|
|
1494
|
-
rest.onPointerOut?.(event);
|
|
1495
|
-
handleOnPointerUp(lastKnownPointerEventRef.current);
|
|
1496
|
-
},
|
|
1497
|
-
onContextMenu: (event)=>{
|
|
1498
|
-
rest.onContextMenu?.(event);
|
|
1499
|
-
if (lastKnownPointerEventRef.current) {
|
|
1500
|
-
handleOnPointerUp(lastKnownPointerEventRef.current);
|
|
1501
|
-
}
|
|
1502
|
-
}
|
|
1503
|
-
})
|
|
1504
|
-
});
|
|
1066
|
+
const Content = React.forwardRef(function({ style, ...rest }, ref) {
|
|
1067
|
+
const { drawerRef, onPress, onRelease, onDrag, keyboardIsOpen, snapPointsOffset, activeSnapPointIndex, modal, isOpen, direction, snapPoints, container, handleOnly, shouldAnimate, autoFocus } = useDrawerContext();
|
|
1068
|
+
const [delayedSnapPoints, setDelayedSnapPoints] = React.useState(false);
|
|
1069
|
+
const composedRef = useComposedRefs(ref, drawerRef);
|
|
1070
|
+
const pointerStartRef = React.useRef(null);
|
|
1071
|
+
const lastKnownPointerEventRef = React.useRef(null);
|
|
1072
|
+
const wasBeyondThePointRef = React.useRef(false);
|
|
1073
|
+
const hasSnapPoints = snapPoints && snapPoints.length > 0;
|
|
1074
|
+
useScaleBackground();
|
|
1075
|
+
const isDeltaInDirection = (delta, direction$1, threshold = 0) => {
|
|
1076
|
+
if (wasBeyondThePointRef.current) return true;
|
|
1077
|
+
const deltaY = Math.abs(delta.y);
|
|
1078
|
+
const deltaX = Math.abs(delta.x);
|
|
1079
|
+
const isDeltaX = deltaX > deltaY;
|
|
1080
|
+
const dFactor = ["bottom", "right"].includes(direction$1) ? 1 : -1;
|
|
1081
|
+
if (direction$1 === "left" || direction$1 === "right") {
|
|
1082
|
+
if (!(delta.x * dFactor < 0) && deltaX >= 0 && deltaX <= threshold) return isDeltaX;
|
|
1083
|
+
} else if (!(delta.y * dFactor < 0) && deltaY >= 0 && deltaY <= threshold) return !isDeltaX;
|
|
1084
|
+
wasBeyondThePointRef.current = true;
|
|
1085
|
+
return true;
|
|
1086
|
+
};
|
|
1087
|
+
React.useEffect(() => {
|
|
1088
|
+
if (hasSnapPoints) window.requestAnimationFrame(() => {
|
|
1089
|
+
setDelayedSnapPoints(true);
|
|
1090
|
+
});
|
|
1091
|
+
}, []);
|
|
1092
|
+
function handleOnPointerUp(event) {
|
|
1093
|
+
pointerStartRef.current = null;
|
|
1094
|
+
wasBeyondThePointRef.current = false;
|
|
1095
|
+
onRelease(event);
|
|
1096
|
+
}
|
|
1097
|
+
return /* @__PURE__ */ jsx(Dialog.Viewport, { children: /* @__PURE__ */ jsx(Dialog.Popup, {
|
|
1098
|
+
"data-vaul-drawer-direction": direction,
|
|
1099
|
+
"data-vaul-drawer": "",
|
|
1100
|
+
"data-vaul-delayed-snap-points": delayedSnapPoints ? "true" : "false",
|
|
1101
|
+
"data-vaul-snap-points": isOpen && hasSnapPoints ? "true" : "false",
|
|
1102
|
+
"data-vaul-custom-container": container ? "true" : "false",
|
|
1103
|
+
"data-vaul-animate": shouldAnimate?.current ? "true" : "false",
|
|
1104
|
+
...rest,
|
|
1105
|
+
ref: composedRef,
|
|
1106
|
+
style: snapPointsOffset && snapPointsOffset.length > 0 ? {
|
|
1107
|
+
"--snap-point-height": `${snapPointsOffset[activeSnapPointIndex ?? 0]}px`,
|
|
1108
|
+
...style
|
|
1109
|
+
} : style,
|
|
1110
|
+
onPointerDown: (event) => {
|
|
1111
|
+
if (handleOnly) return;
|
|
1112
|
+
rest.onPointerDown?.(event);
|
|
1113
|
+
pointerStartRef.current = {
|
|
1114
|
+
x: event.pageX,
|
|
1115
|
+
y: event.pageY
|
|
1116
|
+
};
|
|
1117
|
+
onPress(event);
|
|
1118
|
+
},
|
|
1119
|
+
onPointerMove: (event) => {
|
|
1120
|
+
lastKnownPointerEventRef.current = event;
|
|
1121
|
+
if (handleOnly) return;
|
|
1122
|
+
rest.onPointerMove?.(event);
|
|
1123
|
+
if (!pointerStartRef.current) return;
|
|
1124
|
+
const yPosition = event.pageY - pointerStartRef.current.y;
|
|
1125
|
+
const xPosition = event.pageX - pointerStartRef.current.x;
|
|
1126
|
+
const swipeStartThreshold = event.pointerType === "touch" ? 10 : 2;
|
|
1127
|
+
if (isDeltaInDirection({
|
|
1128
|
+
x: xPosition,
|
|
1129
|
+
y: yPosition
|
|
1130
|
+
}, direction, swipeStartThreshold)) onDrag(event);
|
|
1131
|
+
else if (Math.abs(xPosition) > swipeStartThreshold || Math.abs(yPosition) > swipeStartThreshold) pointerStartRef.current = null;
|
|
1132
|
+
},
|
|
1133
|
+
onPointerUp: (event) => {
|
|
1134
|
+
rest.onPointerUp?.(event);
|
|
1135
|
+
pointerStartRef.current = null;
|
|
1136
|
+
wasBeyondThePointRef.current = false;
|
|
1137
|
+
onRelease(event);
|
|
1138
|
+
},
|
|
1139
|
+
onPointerOut: (event) => {
|
|
1140
|
+
rest.onPointerOut?.(event);
|
|
1141
|
+
handleOnPointerUp(lastKnownPointerEventRef.current);
|
|
1142
|
+
},
|
|
1143
|
+
onContextMenu: (event) => {
|
|
1144
|
+
rest.onContextMenu?.(event);
|
|
1145
|
+
if (lastKnownPointerEventRef.current) handleOnPointerUp(lastKnownPointerEventRef.current);
|
|
1146
|
+
}
|
|
1147
|
+
}) });
|
|
1505
1148
|
});
|
|
1506
1149
|
Content.displayName = "Drawer.Content";
|
|
1507
1150
|
const LONG_HANDLE_PRESS_TIMEOUT = 250;
|
|
1508
1151
|
const DOUBLE_TAP_TIMEOUT = 120;
|
|
1509
|
-
const Handle =
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
"data-vaul-drawer-visible": isOpen ? "true" : "false",
|
|
1578
|
-
"data-vaul-handle": "",
|
|
1579
|
-
"aria-hidden": "true",
|
|
1580
|
-
...rest,
|
|
1581
|
-
children: /*#__PURE__*/ jsx("span", {
|
|
1582
|
-
"data-vaul-handle-hitarea": "",
|
|
1583
|
-
"aria-hidden": "true",
|
|
1584
|
-
children: children
|
|
1585
|
-
})
|
|
1586
|
-
});
|
|
1152
|
+
const Handle = React.forwardRef(function({ preventCycle = false, children, ...rest }, ref) {
|
|
1153
|
+
const { closeDrawer, isDragging, snapPoints, activeSnapPoint, setActiveSnapPoint, dismissible, handleOnly, isOpen, onPress, onDrag } = useDrawerContext();
|
|
1154
|
+
const closeTimeoutIdRef = React.useRef(null);
|
|
1155
|
+
const shouldCancelInteractionRef = React.useRef(false);
|
|
1156
|
+
function handleStartCycle() {
|
|
1157
|
+
if (shouldCancelInteractionRef.current) {
|
|
1158
|
+
handleCancelInteraction();
|
|
1159
|
+
return;
|
|
1160
|
+
}
|
|
1161
|
+
window.setTimeout(() => {
|
|
1162
|
+
handleCycleSnapPoints();
|
|
1163
|
+
}, DOUBLE_TAP_TIMEOUT);
|
|
1164
|
+
}
|
|
1165
|
+
function handleCycleSnapPoints() {
|
|
1166
|
+
if (isDragging || preventCycle || shouldCancelInteractionRef.current) {
|
|
1167
|
+
handleCancelInteraction();
|
|
1168
|
+
return;
|
|
1169
|
+
}
|
|
1170
|
+
handleCancelInteraction();
|
|
1171
|
+
if (!snapPoints || snapPoints.length === 0) {
|
|
1172
|
+
if (!dismissible) closeDrawer();
|
|
1173
|
+
return;
|
|
1174
|
+
}
|
|
1175
|
+
if (activeSnapPoint === snapPoints[snapPoints.length - 1] && dismissible) {
|
|
1176
|
+
closeDrawer();
|
|
1177
|
+
return;
|
|
1178
|
+
}
|
|
1179
|
+
const currentSnapIndex = snapPoints.findIndex((point) => point === activeSnapPoint);
|
|
1180
|
+
if (currentSnapIndex === -1) return;
|
|
1181
|
+
const nextSnapPoint = snapPoints[currentSnapIndex + 1];
|
|
1182
|
+
setActiveSnapPoint(nextSnapPoint);
|
|
1183
|
+
}
|
|
1184
|
+
function handleStartInteraction() {
|
|
1185
|
+
closeTimeoutIdRef.current = window.setTimeout(() => {
|
|
1186
|
+
shouldCancelInteractionRef.current = true;
|
|
1187
|
+
}, LONG_HANDLE_PRESS_TIMEOUT);
|
|
1188
|
+
}
|
|
1189
|
+
function handleCancelInteraction() {
|
|
1190
|
+
if (closeTimeoutIdRef.current) window.clearTimeout(closeTimeoutIdRef.current);
|
|
1191
|
+
shouldCancelInteractionRef.current = false;
|
|
1192
|
+
}
|
|
1193
|
+
return /* @__PURE__ */ jsx("div", {
|
|
1194
|
+
onClick: handleStartCycle,
|
|
1195
|
+
onPointerCancel: handleCancelInteraction,
|
|
1196
|
+
onPointerDown: (e) => {
|
|
1197
|
+
if (handleOnly) onPress({
|
|
1198
|
+
...e,
|
|
1199
|
+
preventBaseUIHandler: () => {}
|
|
1200
|
+
});
|
|
1201
|
+
handleStartInteraction();
|
|
1202
|
+
},
|
|
1203
|
+
onPointerMove: (e) => {
|
|
1204
|
+
if (handleOnly) onDrag({
|
|
1205
|
+
...e,
|
|
1206
|
+
preventBaseUIHandler: () => {}
|
|
1207
|
+
});
|
|
1208
|
+
},
|
|
1209
|
+
ref,
|
|
1210
|
+
"data-vaul-drawer-visible": isOpen ? "true" : "false",
|
|
1211
|
+
"data-vaul-handle": "",
|
|
1212
|
+
"aria-hidden": "true",
|
|
1213
|
+
...rest,
|
|
1214
|
+
children: /* @__PURE__ */ jsx("span", {
|
|
1215
|
+
"data-vaul-handle-hitarea": "",
|
|
1216
|
+
"aria-hidden": "true",
|
|
1217
|
+
children
|
|
1218
|
+
})
|
|
1219
|
+
});
|
|
1587
1220
|
});
|
|
1588
1221
|
Handle.displayName = "Drawer.Handle";
|
|
1589
1222
|
function NestedRoot({ onDrag, onOpenChange, open: nestedIsOpen, ...rest }) {
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
},
|
|
1610
|
-
onRelease: onNestedRelease,
|
|
1611
|
-
...rest
|
|
1612
|
-
});
|
|
1223
|
+
const { onNestedDrag, onNestedOpenChange, onNestedRelease } = useDrawerContext();
|
|
1224
|
+
if (!onNestedDrag) throw new Error("Drawer.NestedRoot must be placed in another drawer");
|
|
1225
|
+
return /* @__PURE__ */ jsx(Root, {
|
|
1226
|
+
nested: true,
|
|
1227
|
+
open: nestedIsOpen,
|
|
1228
|
+
onClose: () => {
|
|
1229
|
+
onNestedOpenChange(false);
|
|
1230
|
+
},
|
|
1231
|
+
onDrag: (e, p) => {
|
|
1232
|
+
onNestedDrag(e, p);
|
|
1233
|
+
onDrag?.(e, p);
|
|
1234
|
+
},
|
|
1235
|
+
onOpenChange: (o) => {
|
|
1236
|
+
if (o) onNestedOpenChange(o);
|
|
1237
|
+
onOpenChange?.(o);
|
|
1238
|
+
},
|
|
1239
|
+
onRelease: onNestedRelease,
|
|
1240
|
+
...rest
|
|
1241
|
+
});
|
|
1613
1242
|
}
|
|
1614
1243
|
function Portal(props) {
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1244
|
+
const context = useDrawerContext();
|
|
1245
|
+
const { container = context.container, ...portalProps } = props;
|
|
1246
|
+
return /* @__PURE__ */ jsx(Dialog.Portal, {
|
|
1247
|
+
container,
|
|
1248
|
+
...portalProps
|
|
1249
|
+
});
|
|
1621
1250
|
}
|
|
1622
1251
|
const Drawer = {
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1252
|
+
Root,
|
|
1253
|
+
NestedRoot,
|
|
1254
|
+
Content,
|
|
1255
|
+
Overlay,
|
|
1256
|
+
Trigger: Dialog.Trigger,
|
|
1257
|
+
Portal,
|
|
1258
|
+
Handle,
|
|
1259
|
+
Close: Dialog.Close,
|
|
1260
|
+
Title: Dialog.Title,
|
|
1261
|
+
Description: Dialog.Description
|
|
1633
1262
|
};
|
|
1634
1263
|
|
|
1635
|
-
|
|
1264
|
+
//#endregion
|
|
1265
|
+
export { Content, Drawer, Handle, NestedRoot, Overlay, Portal, Root };
|