base-vaul 0.0.2 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +1314 -0
- package/{style.css → dist/index.css} +32 -30
- package/dist/index.css.map +1 -0
- package/dist/index.d.cts +186 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.mts +169 -131
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +1174 -1527
- package/dist/index.mjs.map +1 -0
- package/package.json +12 -11
- package/dist/index.d.ts +0 -148
- package/dist/index.js +0 -1635
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,1314 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
require('./index.css');
|
|
4
|
+
//#region rolldown:runtime
|
|
5
|
+
var __create = Object.create;
|
|
6
|
+
var __defProp = Object.defineProperty;
|
|
7
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
8
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
9
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
10
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
14
|
+
key = keys[i];
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except) {
|
|
16
|
+
__defProp(to, key, {
|
|
17
|
+
get: ((k) => from[k]).bind(null, key),
|
|
18
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return to;
|
|
24
|
+
};
|
|
25
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
26
|
+
value: mod,
|
|
27
|
+
enumerable: true
|
|
28
|
+
}) : target, mod));
|
|
29
|
+
|
|
30
|
+
//#endregion
|
|
31
|
+
let _base_ui_react = require("@base-ui/react");
|
|
32
|
+
let react = require("react");
|
|
33
|
+
react = __toESM(react);
|
|
34
|
+
let react_jsx_runtime = require("react/jsx-runtime");
|
|
35
|
+
|
|
36
|
+
//#region src/context.ts
|
|
37
|
+
const DrawerContext = react.default.createContext({
|
|
38
|
+
drawerRef: { current: null },
|
|
39
|
+
overlayRef: { current: null },
|
|
40
|
+
onPress: () => {},
|
|
41
|
+
onRelease: () => {},
|
|
42
|
+
onDrag: () => {},
|
|
43
|
+
onNestedDrag: () => {},
|
|
44
|
+
onNestedOpenChange: () => {},
|
|
45
|
+
onNestedRelease: () => {},
|
|
46
|
+
openProp: void 0,
|
|
47
|
+
dismissible: false,
|
|
48
|
+
isOpen: false,
|
|
49
|
+
isDragging: false,
|
|
50
|
+
keyboardIsOpen: { current: false },
|
|
51
|
+
snapPointsOffset: null,
|
|
52
|
+
snapPoints: null,
|
|
53
|
+
handleOnly: false,
|
|
54
|
+
modal: false,
|
|
55
|
+
shouldFade: false,
|
|
56
|
+
activeSnapPoint: null,
|
|
57
|
+
onOpenChange: () => {},
|
|
58
|
+
setActiveSnapPoint: () => {},
|
|
59
|
+
closeDrawer: () => {},
|
|
60
|
+
direction: "bottom",
|
|
61
|
+
shouldAnimate: { current: true },
|
|
62
|
+
shouldScaleBackground: false,
|
|
63
|
+
setBackgroundColorOnScale: true,
|
|
64
|
+
noBodyStyles: false,
|
|
65
|
+
container: null,
|
|
66
|
+
autoFocus: false
|
|
67
|
+
});
|
|
68
|
+
const useDrawerContext = () => {
|
|
69
|
+
const context = react.default.useContext(DrawerContext);
|
|
70
|
+
if (!context) throw new Error("useDrawerContext must be used within a Drawer.Root");
|
|
71
|
+
return context;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
//#endregion
|
|
75
|
+
//#region src/browser.ts
|
|
76
|
+
function isMobileFirefox() {
|
|
77
|
+
const userAgent = navigator.userAgent;
|
|
78
|
+
return typeof window !== "undefined" && (/Firefox/.test(userAgent) && /Mobile/.test(userAgent) || /FxiOS/.test(userAgent));
|
|
79
|
+
}
|
|
80
|
+
function isMac() {
|
|
81
|
+
return testPlatform(/^Mac/);
|
|
82
|
+
}
|
|
83
|
+
function isIPhone() {
|
|
84
|
+
return testPlatform(/^iPhone/);
|
|
85
|
+
}
|
|
86
|
+
function isSafari() {
|
|
87
|
+
return /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
|
|
88
|
+
}
|
|
89
|
+
function isIPad() {
|
|
90
|
+
return testPlatform(/^iPad/) || isMac() && navigator.maxTouchPoints > 1;
|
|
91
|
+
}
|
|
92
|
+
function isIOS() {
|
|
93
|
+
return isIPhone() || isIPad();
|
|
94
|
+
}
|
|
95
|
+
function testPlatform(re) {
|
|
96
|
+
return typeof window !== "undefined" && window.navigator != null ? re.test(window.navigator.platform) : void 0;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
//#endregion
|
|
100
|
+
//#region src/use-prevent-scroll.ts
|
|
101
|
+
const KEYBOARD_BUFFER = 24;
|
|
102
|
+
const useIsomorphicLayoutEffect = typeof window !== "undefined" ? react.useLayoutEffect : react.useEffect;
|
|
103
|
+
function chain$1(...callbacks) {
|
|
104
|
+
return (...args) => {
|
|
105
|
+
for (const callback of callbacks) if (typeof callback === "function") callback(...args);
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
const visualViewport = typeof document !== "undefined" && window.visualViewport;
|
|
109
|
+
function isScrollable(node) {
|
|
110
|
+
const style = window.getComputedStyle(node);
|
|
111
|
+
return /(auto|scroll)/.test(style.overflow + style.overflowX + style.overflowY);
|
|
112
|
+
}
|
|
113
|
+
function getScrollParent(node) {
|
|
114
|
+
if (isScrollable(node)) node = node.parentElement;
|
|
115
|
+
while (node && !isScrollable(node)) node = node.parentElement;
|
|
116
|
+
return node || document.scrollingElement || document.documentElement;
|
|
117
|
+
}
|
|
118
|
+
const nonTextInputTypes = new Set([
|
|
119
|
+
"checkbox",
|
|
120
|
+
"radio",
|
|
121
|
+
"range",
|
|
122
|
+
"color",
|
|
123
|
+
"file",
|
|
124
|
+
"image",
|
|
125
|
+
"button",
|
|
126
|
+
"submit",
|
|
127
|
+
"reset"
|
|
128
|
+
]);
|
|
129
|
+
let preventScrollCount = 0;
|
|
130
|
+
let restore;
|
|
131
|
+
/**
|
|
132
|
+
* Prevents scrolling on the document body on mount, and
|
|
133
|
+
* restores it on unmount. Also ensures that content does not
|
|
134
|
+
* shift due to the scrollbars disappearing.
|
|
135
|
+
*/
|
|
136
|
+
function usePreventScroll(options = {}) {
|
|
137
|
+
const { isDisabled } = options;
|
|
138
|
+
useIsomorphicLayoutEffect(() => {
|
|
139
|
+
if (isDisabled) return;
|
|
140
|
+
preventScrollCount++;
|
|
141
|
+
if (preventScrollCount === 1) {
|
|
142
|
+
if (isIOS()) restore = preventScrollMobileSafari();
|
|
143
|
+
}
|
|
144
|
+
return () => {
|
|
145
|
+
preventScrollCount--;
|
|
146
|
+
if (preventScrollCount === 0) restore === null || restore === void 0 || restore();
|
|
147
|
+
};
|
|
148
|
+
}, [isDisabled]);
|
|
149
|
+
}
|
|
150
|
+
function preventScrollMobileSafari() {
|
|
151
|
+
let scrollable;
|
|
152
|
+
let lastY = 0;
|
|
153
|
+
const onTouchStart = (e) => {
|
|
154
|
+
scrollable = getScrollParent(e.target);
|
|
155
|
+
if (scrollable === document.documentElement && scrollable === document.body) return;
|
|
156
|
+
lastY = e.changedTouches[0].pageY;
|
|
157
|
+
};
|
|
158
|
+
const onTouchMove = (e) => {
|
|
159
|
+
if (!scrollable || scrollable === document.documentElement || scrollable === document.body) {
|
|
160
|
+
e.preventDefault();
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
const y = e.changedTouches[0].pageY;
|
|
164
|
+
const scrollTop = scrollable.scrollTop;
|
|
165
|
+
const bottom = scrollable.scrollHeight - scrollable.clientHeight;
|
|
166
|
+
if (bottom === 0) return;
|
|
167
|
+
if (scrollTop <= 0 && y > lastY || scrollTop >= bottom && y < lastY) e.preventDefault();
|
|
168
|
+
lastY = y;
|
|
169
|
+
};
|
|
170
|
+
const onTouchEnd = (e) => {
|
|
171
|
+
const target = e.target;
|
|
172
|
+
if (isInput(target) && target !== document.activeElement) {
|
|
173
|
+
e.preventDefault();
|
|
174
|
+
target.style.transform = "translateY(-2000px)";
|
|
175
|
+
target.focus();
|
|
176
|
+
requestAnimationFrame(() => {
|
|
177
|
+
target.style.transform = "";
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
const onFocus = (e) => {
|
|
182
|
+
const target = e.target;
|
|
183
|
+
if (isInput(target)) {
|
|
184
|
+
target.style.transform = "translateY(-2000px)";
|
|
185
|
+
requestAnimationFrame(() => {
|
|
186
|
+
target.style.transform = "";
|
|
187
|
+
if (visualViewport) if (visualViewport.height < window.innerHeight) requestAnimationFrame(() => {
|
|
188
|
+
scrollIntoView(target);
|
|
189
|
+
});
|
|
190
|
+
else visualViewport.addEventListener("resize", () => scrollIntoView(target), { once: true });
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
const onWindowScroll = () => {
|
|
195
|
+
window.scrollTo(0, 0);
|
|
196
|
+
};
|
|
197
|
+
const scrollX = window.pageXOffset;
|
|
198
|
+
const scrollY = window.pageYOffset;
|
|
199
|
+
const restoreStyles = chain$1(setStyle(document.documentElement, "paddingRight", `${window.innerWidth - document.documentElement.clientWidth}px`));
|
|
200
|
+
window.scrollTo(0, 0);
|
|
201
|
+
const removeEvents = chain$1(addEvent(document, "touchstart", onTouchStart, {
|
|
202
|
+
passive: false,
|
|
203
|
+
capture: true
|
|
204
|
+
}), addEvent(document, "touchmove", onTouchMove, {
|
|
205
|
+
passive: false,
|
|
206
|
+
capture: true
|
|
207
|
+
}), addEvent(document, "touchend", onTouchEnd, {
|
|
208
|
+
passive: false,
|
|
209
|
+
capture: true
|
|
210
|
+
}), addEvent(document, "focus", onFocus, true), addEvent(window, "scroll", onWindowScroll));
|
|
211
|
+
return () => {
|
|
212
|
+
restoreStyles();
|
|
213
|
+
removeEvents();
|
|
214
|
+
window.scrollTo(scrollX, scrollY);
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
function setStyle(element, style, value) {
|
|
218
|
+
const cur = element.style[style];
|
|
219
|
+
element.style[style] = value;
|
|
220
|
+
return () => {
|
|
221
|
+
element.style[style] = cur;
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
function addEvent(target, event, handler, options) {
|
|
225
|
+
target.addEventListener(event, handler, options);
|
|
226
|
+
return () => {
|
|
227
|
+
target.removeEventListener(event, handler, options);
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
function scrollIntoView(target) {
|
|
231
|
+
const root = document.scrollingElement || document.documentElement;
|
|
232
|
+
while (target && target !== root) {
|
|
233
|
+
const scrollable = getScrollParent(target);
|
|
234
|
+
if (scrollable !== document.documentElement && scrollable !== document.body && scrollable !== target) {
|
|
235
|
+
const scrollableTop = scrollable.getBoundingClientRect().top;
|
|
236
|
+
const targetTop = target.getBoundingClientRect().top;
|
|
237
|
+
if (target.getBoundingClientRect().bottom > scrollable.getBoundingClientRect().bottom + KEYBOARD_BUFFER) scrollable.scrollTop += targetTop - scrollableTop;
|
|
238
|
+
}
|
|
239
|
+
target = scrollable.parentElement;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
function isInput(target) {
|
|
243
|
+
return target instanceof HTMLInputElement && !nonTextInputTypes.has(target.type) || target instanceof HTMLTextAreaElement || target instanceof HTMLElement && target.isContentEditable;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
//#endregion
|
|
247
|
+
//#region src/use-composed-refs.ts
|
|
248
|
+
/**
|
|
249
|
+
* Set a given ref to a given value
|
|
250
|
+
* This utility takes care of different types of refs: callback refs and RefObject(s)
|
|
251
|
+
*/
|
|
252
|
+
function setRef(ref, value) {
|
|
253
|
+
if (typeof ref === "function") ref(value);
|
|
254
|
+
else if (ref !== null && ref !== void 0) ref.current = value;
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* A utility to compose multiple refs together
|
|
258
|
+
* Accepts callback refs and RefObject(s)
|
|
259
|
+
*/
|
|
260
|
+
function composeRefs(...refs) {
|
|
261
|
+
return (node) => refs.forEach((ref) => setRef(ref, node));
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* A custom hook that composes multiple refs
|
|
265
|
+
* Accepts callback refs and RefObject(s)
|
|
266
|
+
*/
|
|
267
|
+
function useComposedRefs(...refs) {
|
|
268
|
+
return react.useCallback(composeRefs(...refs), refs);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
//#endregion
|
|
272
|
+
//#region src/helpers.ts
|
|
273
|
+
const cache = /* @__PURE__ */ new WeakMap();
|
|
274
|
+
function set(el, styles, ignoreCache = false) {
|
|
275
|
+
if (!el || !(el instanceof HTMLElement)) return;
|
|
276
|
+
const originalStyles = {};
|
|
277
|
+
Object.entries(styles).forEach(([key, value]) => {
|
|
278
|
+
if (key.startsWith("--")) {
|
|
279
|
+
el.style.setProperty(key, value);
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
originalStyles[key] = el.style[key];
|
|
283
|
+
el.style[key] = value;
|
|
284
|
+
});
|
|
285
|
+
if (ignoreCache) return;
|
|
286
|
+
cache.set(el, originalStyles);
|
|
287
|
+
}
|
|
288
|
+
function reset(el, prop) {
|
|
289
|
+
if (!el || !(el instanceof HTMLElement)) return;
|
|
290
|
+
const originalStyles = cache.get(el);
|
|
291
|
+
if (!originalStyles) return;
|
|
292
|
+
if (prop) el.style[prop] = originalStyles[prop];
|
|
293
|
+
else Object.entries(originalStyles).forEach(([key, value]) => {
|
|
294
|
+
el.style[key] = value;
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
const isVertical = (direction) => {
|
|
298
|
+
switch (direction) {
|
|
299
|
+
case "top":
|
|
300
|
+
case "bottom": return true;
|
|
301
|
+
case "left":
|
|
302
|
+
case "right": return false;
|
|
303
|
+
default: return direction;
|
|
304
|
+
}
|
|
305
|
+
};
|
|
306
|
+
function getTranslate(element, direction) {
|
|
307
|
+
if (!element) return null;
|
|
308
|
+
const style = window.getComputedStyle(element);
|
|
309
|
+
const transform = style.transform || style.webkitTransform || style.mozTransform;
|
|
310
|
+
let mat = transform.match(/^matrix3d\((.+)\)$/);
|
|
311
|
+
if (mat) return parseFloat(mat[1].split(", ")[isVertical(direction) ? 13 : 12]);
|
|
312
|
+
mat = transform.match(/^matrix\((.+)\)$/);
|
|
313
|
+
return mat ? parseFloat(mat[1].split(", ")[isVertical(direction) ? 5 : 4]) : null;
|
|
314
|
+
}
|
|
315
|
+
function dampenValue(v) {
|
|
316
|
+
return 8 * (Math.log(v + 1) - 2);
|
|
317
|
+
}
|
|
318
|
+
function assignStyle(element, style) {
|
|
319
|
+
if (!element) return () => {};
|
|
320
|
+
const prevStyle = element.style.cssText;
|
|
321
|
+
Object.assign(element.style, style);
|
|
322
|
+
return () => {
|
|
323
|
+
element.style.cssText = prevStyle;
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* Receives functions as arguments and returns a new function that calls all.
|
|
328
|
+
*/
|
|
329
|
+
function chain(...fns) {
|
|
330
|
+
return (...args) => {
|
|
331
|
+
for (const fn of fns) if (typeof fn === "function") fn(...args);
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
//#endregion
|
|
336
|
+
//#region src/constants.ts
|
|
337
|
+
const TRANSITIONS = {
|
|
338
|
+
DURATION: .5,
|
|
339
|
+
EASE: [
|
|
340
|
+
.32,
|
|
341
|
+
.72,
|
|
342
|
+
0,
|
|
343
|
+
1
|
|
344
|
+
]
|
|
345
|
+
};
|
|
346
|
+
const VELOCITY_THRESHOLD = .4;
|
|
347
|
+
const CLOSE_THRESHOLD = .25;
|
|
348
|
+
const SCROLL_LOCK_TIMEOUT = 100;
|
|
349
|
+
const BORDER_RADIUS = 8;
|
|
350
|
+
const NESTED_DISPLACEMENT = 16;
|
|
351
|
+
const WINDOW_TOP_OFFSET = 26;
|
|
352
|
+
const DRAG_CLASS = "vaul-dragging";
|
|
353
|
+
|
|
354
|
+
//#endregion
|
|
355
|
+
//#region src/use-controllable-state.ts
|
|
356
|
+
function useCallbackRef(callback) {
|
|
357
|
+
const callbackRef = react.default.useRef(callback);
|
|
358
|
+
react.default.useEffect(() => {
|
|
359
|
+
callbackRef.current = callback;
|
|
360
|
+
});
|
|
361
|
+
return react.default.useMemo(() => ((...args) => {
|
|
362
|
+
var _callbackRef$current;
|
|
363
|
+
return (_callbackRef$current = callbackRef.current) === null || _callbackRef$current === void 0 ? void 0 : _callbackRef$current.call(callbackRef, ...args);
|
|
364
|
+
}), []);
|
|
365
|
+
}
|
|
366
|
+
function useUncontrolledState({ defaultProp, onChange }) {
|
|
367
|
+
const uncontrolledState = react.default.useState(defaultProp);
|
|
368
|
+
const [value] = uncontrolledState;
|
|
369
|
+
const prevValueRef = react.default.useRef(value);
|
|
370
|
+
const handleChange = useCallbackRef(onChange);
|
|
371
|
+
react.default.useEffect(() => {
|
|
372
|
+
if (prevValueRef.current !== value) {
|
|
373
|
+
handleChange(value);
|
|
374
|
+
prevValueRef.current = value;
|
|
375
|
+
}
|
|
376
|
+
}, [
|
|
377
|
+
value,
|
|
378
|
+
prevValueRef,
|
|
379
|
+
handleChange
|
|
380
|
+
]);
|
|
381
|
+
return uncontrolledState;
|
|
382
|
+
}
|
|
383
|
+
function useControllableState({ prop, defaultProp, onChange = () => {} }) {
|
|
384
|
+
const [uncontrolledProp, setUncontrolledProp] = useUncontrolledState({
|
|
385
|
+
defaultProp,
|
|
386
|
+
onChange
|
|
387
|
+
});
|
|
388
|
+
const isControlled = prop !== void 0;
|
|
389
|
+
const value = isControlled ? prop : uncontrolledProp;
|
|
390
|
+
const handleChange = useCallbackRef(onChange);
|
|
391
|
+
return [value, react.default.useCallback((nextValue) => {
|
|
392
|
+
if (isControlled) {
|
|
393
|
+
const value$1 = typeof nextValue === "function" ? nextValue(prop) : nextValue;
|
|
394
|
+
if (value$1 !== prop) handleChange(value$1);
|
|
395
|
+
} else setUncontrolledProp(nextValue);
|
|
396
|
+
}, [
|
|
397
|
+
isControlled,
|
|
398
|
+
prop,
|
|
399
|
+
setUncontrolledProp,
|
|
400
|
+
handleChange
|
|
401
|
+
])];
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
//#endregion
|
|
405
|
+
//#region src/use-snap-points.ts
|
|
406
|
+
function useSnapPoints({ activeSnapPointProp, setActiveSnapPointProp, snapPoints, drawerRef, overlayRef, fadeFromIndex, onSnapPointChange, direction = "bottom", container, snapToSequentialPoint }) {
|
|
407
|
+
const [activeSnapPoint, setActiveSnapPoint] = useControllableState({
|
|
408
|
+
prop: activeSnapPointProp,
|
|
409
|
+
defaultProp: snapPoints === null || snapPoints === void 0 ? void 0 : snapPoints[0],
|
|
410
|
+
onChange: setActiveSnapPointProp
|
|
411
|
+
});
|
|
412
|
+
const [windowDimensions, setWindowDimensions] = react.default.useState(typeof window !== "undefined" ? {
|
|
413
|
+
innerWidth: window.innerWidth,
|
|
414
|
+
innerHeight: window.innerHeight
|
|
415
|
+
} : void 0);
|
|
416
|
+
react.default.useEffect(() => {
|
|
417
|
+
function onResize() {
|
|
418
|
+
setWindowDimensions({
|
|
419
|
+
innerWidth: window.innerWidth,
|
|
420
|
+
innerHeight: window.innerHeight
|
|
421
|
+
});
|
|
422
|
+
}
|
|
423
|
+
window.addEventListener("resize", onResize);
|
|
424
|
+
return () => window.removeEventListener("resize", onResize);
|
|
425
|
+
}, []);
|
|
426
|
+
const isLastSnapPoint = react.default.useMemo(() => activeSnapPoint === (snapPoints === null || snapPoints === void 0 ? void 0 : snapPoints[snapPoints.length - 1]) || null, [snapPoints, activeSnapPoint]);
|
|
427
|
+
const activeSnapPointIndex = react.default.useMemo(() => (snapPoints === null || snapPoints === void 0 ? void 0 : snapPoints.findIndex((snapPoint) => snapPoint === activeSnapPoint)) ?? null, [snapPoints, activeSnapPoint]);
|
|
428
|
+
const shouldFade = snapPoints && snapPoints.length > 0 && (fadeFromIndex || fadeFromIndex === 0) && !Number.isNaN(fadeFromIndex) && snapPoints[fadeFromIndex] === activeSnapPoint || !snapPoints;
|
|
429
|
+
const snapPointsOffset = react.default.useMemo(() => {
|
|
430
|
+
const containerSize = container ? {
|
|
431
|
+
width: container.getBoundingClientRect().width,
|
|
432
|
+
height: container.getBoundingClientRect().height
|
|
433
|
+
} : typeof window !== "undefined" ? {
|
|
434
|
+
width: window.innerWidth,
|
|
435
|
+
height: window.innerHeight
|
|
436
|
+
} : {
|
|
437
|
+
width: 0,
|
|
438
|
+
height: 0
|
|
439
|
+
};
|
|
440
|
+
return (snapPoints === null || snapPoints === void 0 ? void 0 : snapPoints.map((snapPoint) => {
|
|
441
|
+
const isPx = typeof snapPoint === "string";
|
|
442
|
+
let snapPointAsNumber = 0;
|
|
443
|
+
if (isPx) snapPointAsNumber = parseInt(snapPoint, 10);
|
|
444
|
+
if (isVertical(direction)) {
|
|
445
|
+
const height = isPx ? snapPointAsNumber : windowDimensions ? snapPoint * containerSize.height : 0;
|
|
446
|
+
if (windowDimensions) return direction === "bottom" ? containerSize.height - height : -containerSize.height + height;
|
|
447
|
+
return height;
|
|
448
|
+
}
|
|
449
|
+
const width = isPx ? snapPointAsNumber : windowDimensions ? snapPoint * containerSize.width : 0;
|
|
450
|
+
if (windowDimensions) return direction === "right" ? containerSize.width - width : -containerSize.width + width;
|
|
451
|
+
return width;
|
|
452
|
+
})) ?? [];
|
|
453
|
+
}, [
|
|
454
|
+
snapPoints,
|
|
455
|
+
windowDimensions,
|
|
456
|
+
container
|
|
457
|
+
]);
|
|
458
|
+
const activeSnapPointOffset = react.default.useMemo(() => activeSnapPointIndex !== null ? snapPointsOffset === null || snapPointsOffset === void 0 ? void 0 : snapPointsOffset[activeSnapPointIndex] : null, [snapPointsOffset, activeSnapPointIndex]);
|
|
459
|
+
const snapToPoint = react.default.useCallback((dimension) => {
|
|
460
|
+
const newSnapPointIndex = (snapPointsOffset === null || snapPointsOffset === void 0 ? void 0 : snapPointsOffset.findIndex((snapPointDim) => snapPointDim === dimension)) ?? null;
|
|
461
|
+
onSnapPointChange(newSnapPointIndex);
|
|
462
|
+
set(drawerRef.current, {
|
|
463
|
+
transition: `transform ${TRANSITIONS.DURATION}s cubic-bezier(${TRANSITIONS.EASE.join(",")})`,
|
|
464
|
+
transform: isVertical(direction) ? `translate3d(0, ${dimension}px, 0)` : `translate3d(${dimension}px, 0, 0)`
|
|
465
|
+
});
|
|
466
|
+
if (snapPointsOffset && newSnapPointIndex !== snapPointsOffset.length - 1 && fadeFromIndex !== void 0 && newSnapPointIndex !== fadeFromIndex && newSnapPointIndex < fadeFromIndex) set(overlayRef.current, {
|
|
467
|
+
transition: `opacity ${TRANSITIONS.DURATION}s cubic-bezier(${TRANSITIONS.EASE.join(",")})`,
|
|
468
|
+
opacity: "0"
|
|
469
|
+
});
|
|
470
|
+
else set(overlayRef.current, {
|
|
471
|
+
transition: `opacity ${TRANSITIONS.DURATION}s cubic-bezier(${TRANSITIONS.EASE.join(",")})`,
|
|
472
|
+
opacity: "1"
|
|
473
|
+
});
|
|
474
|
+
setActiveSnapPoint(snapPoints === null || snapPoints === void 0 ? void 0 : snapPoints[Math.max(newSnapPointIndex, 0)]);
|
|
475
|
+
}, [
|
|
476
|
+
drawerRef.current,
|
|
477
|
+
snapPoints,
|
|
478
|
+
snapPointsOffset,
|
|
479
|
+
fadeFromIndex,
|
|
480
|
+
overlayRef,
|
|
481
|
+
setActiveSnapPoint
|
|
482
|
+
]);
|
|
483
|
+
react.default.useEffect(() => {
|
|
484
|
+
if (activeSnapPoint || activeSnapPointProp) {
|
|
485
|
+
const newIndex = (snapPoints === null || snapPoints === void 0 ? void 0 : snapPoints.findIndex((snapPoint) => snapPoint === activeSnapPointProp || snapPoint === activeSnapPoint)) ?? -1;
|
|
486
|
+
if (snapPointsOffset && newIndex !== -1 && typeof snapPointsOffset[newIndex] === "number") snapToPoint(snapPointsOffset[newIndex]);
|
|
487
|
+
}
|
|
488
|
+
}, [
|
|
489
|
+
activeSnapPoint,
|
|
490
|
+
activeSnapPointProp,
|
|
491
|
+
snapPoints,
|
|
492
|
+
snapPointsOffset,
|
|
493
|
+
snapToPoint
|
|
494
|
+
]);
|
|
495
|
+
function onRelease({ draggedDistance, closeDrawer, velocity, dismissible }) {
|
|
496
|
+
if (fadeFromIndex === void 0) return;
|
|
497
|
+
const currentPosition = direction === "bottom" || direction === "right" ? (activeSnapPointOffset ?? 0) - draggedDistance : (activeSnapPointOffset ?? 0) + draggedDistance;
|
|
498
|
+
const isOverlaySnapPoint = activeSnapPointIndex === fadeFromIndex - 1;
|
|
499
|
+
const isFirst = activeSnapPointIndex === 0;
|
|
500
|
+
const hasDraggedUp = draggedDistance > 0;
|
|
501
|
+
if (isOverlaySnapPoint) set(overlayRef.current, { transition: `opacity ${TRANSITIONS.DURATION}s cubic-bezier(${TRANSITIONS.EASE.join(",")})` });
|
|
502
|
+
if (!snapToSequentialPoint && velocity > 2 && !hasDraggedUp) {
|
|
503
|
+
if (dismissible) closeDrawer();
|
|
504
|
+
else snapToPoint(snapPointsOffset[0]);
|
|
505
|
+
return;
|
|
506
|
+
}
|
|
507
|
+
if (!snapToSequentialPoint && velocity > 2 && hasDraggedUp && snapPointsOffset && snapPoints) {
|
|
508
|
+
snapToPoint(snapPointsOffset[snapPoints.length - 1]);
|
|
509
|
+
return;
|
|
510
|
+
}
|
|
511
|
+
const closestSnapPoint = snapPointsOffset === null || snapPointsOffset === void 0 ? void 0 : snapPointsOffset.reduce((prev, curr) => {
|
|
512
|
+
if (typeof prev !== "number" || typeof curr !== "number") return prev;
|
|
513
|
+
return Math.abs(curr - currentPosition) < Math.abs(prev - currentPosition) ? curr : prev;
|
|
514
|
+
});
|
|
515
|
+
const dim = isVertical(direction) ? window.innerHeight : window.innerWidth;
|
|
516
|
+
if (velocity > VELOCITY_THRESHOLD && Math.abs(draggedDistance) < dim * .4) {
|
|
517
|
+
const dragDirection = hasDraggedUp ? 1 : -1;
|
|
518
|
+
if (dragDirection > 0 && isLastSnapPoint && snapPoints) {
|
|
519
|
+
snapToPoint(snapPointsOffset[snapPoints.length - 1]);
|
|
520
|
+
return;
|
|
521
|
+
}
|
|
522
|
+
if (isFirst && dragDirection < 0 && dismissible) closeDrawer();
|
|
523
|
+
if (activeSnapPointIndex === null) return;
|
|
524
|
+
snapToPoint(snapPointsOffset[activeSnapPointIndex + dragDirection]);
|
|
525
|
+
return;
|
|
526
|
+
}
|
|
527
|
+
snapToPoint(closestSnapPoint);
|
|
528
|
+
}
|
|
529
|
+
function onDrag({ draggedDistance }) {
|
|
530
|
+
if (activeSnapPointOffset === null) return;
|
|
531
|
+
const newValue = direction === "bottom" || direction === "right" ? activeSnapPointOffset - draggedDistance : activeSnapPointOffset + draggedDistance;
|
|
532
|
+
if ((direction === "bottom" || direction === "right") && newValue < snapPointsOffset[snapPointsOffset.length - 1]) return;
|
|
533
|
+
if ((direction === "top" || direction === "left") && newValue > snapPointsOffset[snapPointsOffset.length - 1]) return;
|
|
534
|
+
set(drawerRef.current, { transform: isVertical(direction) ? `translate3d(0, ${newValue}px, 0)` : `translate3d(${newValue}px, 0, 0)` });
|
|
535
|
+
}
|
|
536
|
+
function getPercentageDragged(absDraggedDistance, isDraggingDown) {
|
|
537
|
+
if (!snapPoints || typeof activeSnapPointIndex !== "number" || !snapPointsOffset || fadeFromIndex === void 0) return null;
|
|
538
|
+
const isOverlaySnapPoint = activeSnapPointIndex === fadeFromIndex - 1;
|
|
539
|
+
if (activeSnapPointIndex >= fadeFromIndex && isDraggingDown) return 0;
|
|
540
|
+
if (isOverlaySnapPoint && !isDraggingDown) return 1;
|
|
541
|
+
if (!shouldFade && !isOverlaySnapPoint) return null;
|
|
542
|
+
const targetSnapPointIndex = isOverlaySnapPoint ? activeSnapPointIndex + 1 : activeSnapPointIndex - 1;
|
|
543
|
+
const snapPointDistance = isOverlaySnapPoint ? snapPointsOffset[targetSnapPointIndex] - snapPointsOffset[targetSnapPointIndex - 1] : snapPointsOffset[targetSnapPointIndex + 1] - snapPointsOffset[targetSnapPointIndex];
|
|
544
|
+
const percentageDragged = absDraggedDistance / Math.abs(snapPointDistance);
|
|
545
|
+
if (isOverlaySnapPoint) return 1 - percentageDragged;
|
|
546
|
+
else return percentageDragged;
|
|
547
|
+
}
|
|
548
|
+
return {
|
|
549
|
+
isLastSnapPoint,
|
|
550
|
+
activeSnapPoint,
|
|
551
|
+
shouldFade,
|
|
552
|
+
getPercentageDragged,
|
|
553
|
+
setActiveSnapPoint,
|
|
554
|
+
activeSnapPointIndex,
|
|
555
|
+
onRelease,
|
|
556
|
+
onDrag,
|
|
557
|
+
snapPointsOffset
|
|
558
|
+
};
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
//#endregion
|
|
562
|
+
//#region src/use-scale-background.ts
|
|
563
|
+
const noop = () => () => {};
|
|
564
|
+
function useScaleBackground() {
|
|
565
|
+
const { direction, isOpen, shouldScaleBackground, setBackgroundColorOnScale, noBodyStyles } = useDrawerContext();
|
|
566
|
+
const timeoutIdRef = react.default.useRef(null);
|
|
567
|
+
const initialBackgroundColor = (0, react.useMemo)(() => document.body.style.backgroundColor, []);
|
|
568
|
+
function getScale() {
|
|
569
|
+
return (window.innerWidth - WINDOW_TOP_OFFSET) / window.innerWidth;
|
|
570
|
+
}
|
|
571
|
+
react.default.useEffect(() => {
|
|
572
|
+
if (isOpen && shouldScaleBackground) {
|
|
573
|
+
if (timeoutIdRef.current) clearTimeout(timeoutIdRef.current);
|
|
574
|
+
const wrapper = document.querySelector("[data-vaul-drawer-wrapper]") || document.querySelector("[vaul-drawer-wrapper]");
|
|
575
|
+
if (!wrapper) return;
|
|
576
|
+
chain(setBackgroundColorOnScale && !noBodyStyles ? assignStyle(document.body, { background: "black" }) : noop, assignStyle(wrapper, {
|
|
577
|
+
transformOrigin: isVertical(direction) ? "top" : "left",
|
|
578
|
+
transitionProperty: "transform, border-radius",
|
|
579
|
+
transitionDuration: `${TRANSITIONS.DURATION}s`,
|
|
580
|
+
transitionTimingFunction: `cubic-bezier(${TRANSITIONS.EASE.join(",")})`
|
|
581
|
+
}));
|
|
582
|
+
const wrapperStylesCleanup = assignStyle(wrapper, {
|
|
583
|
+
borderRadius: `${BORDER_RADIUS}px`,
|
|
584
|
+
overflow: "hidden",
|
|
585
|
+
...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)` }
|
|
586
|
+
});
|
|
587
|
+
return () => {
|
|
588
|
+
wrapperStylesCleanup();
|
|
589
|
+
timeoutIdRef.current = window.setTimeout(() => {
|
|
590
|
+
if (initialBackgroundColor) document.body.style.background = initialBackgroundColor;
|
|
591
|
+
else document.body.style.removeProperty("background");
|
|
592
|
+
}, TRANSITIONS.DURATION * 1e3);
|
|
593
|
+
};
|
|
594
|
+
}
|
|
595
|
+
}, [
|
|
596
|
+
isOpen,
|
|
597
|
+
shouldScaleBackground,
|
|
598
|
+
initialBackgroundColor
|
|
599
|
+
]);
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
//#endregion
|
|
603
|
+
//#region src/use-position-fixed.ts
|
|
604
|
+
let previousBodyPosition = null;
|
|
605
|
+
/**
|
|
606
|
+
* This hook is necessary to prevent buggy behavior on iOS devices (need to test on Android).
|
|
607
|
+
* 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.
|
|
608
|
+
* Issues that this hook solves:
|
|
609
|
+
* https://github.com/emilkowalski/vaul/issues/435
|
|
610
|
+
* https://github.com/emilkowalski/vaul/issues/433
|
|
611
|
+
* And more that I discovered, but were just not reported.
|
|
612
|
+
*/
|
|
613
|
+
function usePositionFixed({ isOpen, modal, nested, hasBeenOpened, preventScrollRestoration, noBodyStyles }) {
|
|
614
|
+
const [activeUrl, setActiveUrl] = react.default.useState(() => typeof window !== "undefined" ? window.location.href : "");
|
|
615
|
+
const scrollPos = react.default.useRef(0);
|
|
616
|
+
const setPositionFixed = react.default.useCallback(() => {
|
|
617
|
+
if (!isSafari()) return;
|
|
618
|
+
if (previousBodyPosition === null && isOpen && !noBodyStyles) {
|
|
619
|
+
previousBodyPosition = {
|
|
620
|
+
position: document.body.style.position,
|
|
621
|
+
top: document.body.style.top,
|
|
622
|
+
left: document.body.style.left,
|
|
623
|
+
height: document.body.style.height,
|
|
624
|
+
right: "unset"
|
|
625
|
+
};
|
|
626
|
+
const { scrollX, innerHeight } = window;
|
|
627
|
+
document.body.style.setProperty("position", "fixed", "important");
|
|
628
|
+
Object.assign(document.body.style, {
|
|
629
|
+
top: `${-scrollPos.current}px`,
|
|
630
|
+
left: `${-scrollX}px`,
|
|
631
|
+
right: "0px",
|
|
632
|
+
height: "auto"
|
|
633
|
+
});
|
|
634
|
+
window.setTimeout(() => window.requestAnimationFrame(() => {
|
|
635
|
+
const bottomBarHeight = innerHeight - window.innerHeight;
|
|
636
|
+
if (bottomBarHeight && scrollPos.current >= innerHeight) document.body.style.top = `${-(scrollPos.current + bottomBarHeight)}px`;
|
|
637
|
+
}), 300);
|
|
638
|
+
}
|
|
639
|
+
}, [isOpen]);
|
|
640
|
+
const restorePositionSetting = react.default.useCallback(() => {
|
|
641
|
+
if (!isSafari()) return;
|
|
642
|
+
if (previousBodyPosition !== null && !noBodyStyles) {
|
|
643
|
+
const y = -parseInt(document.body.style.top, 10);
|
|
644
|
+
const x = -parseInt(document.body.style.left, 10);
|
|
645
|
+
Object.assign(document.body.style, previousBodyPosition);
|
|
646
|
+
window.requestAnimationFrame(() => {
|
|
647
|
+
if (preventScrollRestoration && activeUrl !== window.location.href) {
|
|
648
|
+
setActiveUrl(window.location.href);
|
|
649
|
+
return;
|
|
650
|
+
}
|
|
651
|
+
window.scrollTo(x, y);
|
|
652
|
+
});
|
|
653
|
+
previousBodyPosition = null;
|
|
654
|
+
}
|
|
655
|
+
}, [activeUrl]);
|
|
656
|
+
react.default.useEffect(() => {
|
|
657
|
+
function onScroll() {
|
|
658
|
+
scrollPos.current = window.scrollY;
|
|
659
|
+
}
|
|
660
|
+
onScroll();
|
|
661
|
+
window.addEventListener("scroll", onScroll);
|
|
662
|
+
return () => {
|
|
663
|
+
window.removeEventListener("scroll", onScroll);
|
|
664
|
+
};
|
|
665
|
+
}, []);
|
|
666
|
+
react.default.useEffect(() => {
|
|
667
|
+
if (!modal) return;
|
|
668
|
+
return () => {
|
|
669
|
+
if (typeof document === "undefined") return;
|
|
670
|
+
if (!!document.querySelector("[data-vaul-drawer]")) return;
|
|
671
|
+
restorePositionSetting();
|
|
672
|
+
};
|
|
673
|
+
}, [modal, restorePositionSetting]);
|
|
674
|
+
react.default.useEffect(() => {
|
|
675
|
+
if (nested || !hasBeenOpened) return;
|
|
676
|
+
if (isOpen) {
|
|
677
|
+
!window.matchMedia("(display-mode: standalone)").matches && setPositionFixed();
|
|
678
|
+
if (!modal) window.setTimeout(() => {
|
|
679
|
+
restorePositionSetting();
|
|
680
|
+
}, 500);
|
|
681
|
+
} else restorePositionSetting();
|
|
682
|
+
}, [
|
|
683
|
+
isOpen,
|
|
684
|
+
hasBeenOpened,
|
|
685
|
+
activeUrl,
|
|
686
|
+
modal,
|
|
687
|
+
nested,
|
|
688
|
+
setPositionFixed,
|
|
689
|
+
restorePositionSetting
|
|
690
|
+
]);
|
|
691
|
+
return { restorePositionSetting };
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
//#endregion
|
|
695
|
+
//#region src/index.tsx
|
|
696
|
+
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 }) {
|
|
697
|
+
var _drawerRef$current, _drawerRef$current2;
|
|
698
|
+
const [isOpen = false, setIsOpen] = useControllableState({
|
|
699
|
+
defaultProp: defaultOpen,
|
|
700
|
+
prop: openProp,
|
|
701
|
+
onChange: (o) => {
|
|
702
|
+
onOpenChange === null || onOpenChange === void 0 || onOpenChange(o);
|
|
703
|
+
if (!o && !nested) restorePositionSetting();
|
|
704
|
+
setTimeout(() => {
|
|
705
|
+
onAnimationEnd === null || onAnimationEnd === void 0 || onAnimationEnd(o);
|
|
706
|
+
}, TRANSITIONS.DURATION * 1e3);
|
|
707
|
+
if (o && !modal) {
|
|
708
|
+
if (typeof window !== "undefined") window.requestAnimationFrame(() => {
|
|
709
|
+
document.body.style.pointerEvents = "auto";
|
|
710
|
+
});
|
|
711
|
+
}
|
|
712
|
+
if (!o) document.body.style.pointerEvents = "auto";
|
|
713
|
+
}
|
|
714
|
+
});
|
|
715
|
+
const [hasBeenOpened, setHasBeenOpened] = react.default.useState(false);
|
|
716
|
+
const [isDragging, setIsDragging] = react.default.useState(false);
|
|
717
|
+
const [justReleased, setJustReleased] = react.default.useState(false);
|
|
718
|
+
const overlayRef = react.default.useRef(null);
|
|
719
|
+
const openTime = react.default.useRef(null);
|
|
720
|
+
const dragStartTime = react.default.useRef(null);
|
|
721
|
+
const dragEndTime = react.default.useRef(null);
|
|
722
|
+
const lastTimeDragPrevented = react.default.useRef(null);
|
|
723
|
+
const isAllowedToDrag = react.default.useRef(false);
|
|
724
|
+
const nestedOpenChangeTimer = react.default.useRef(null);
|
|
725
|
+
const pointerStart = react.default.useRef(0);
|
|
726
|
+
const keyboardIsOpen = react.default.useRef(false);
|
|
727
|
+
const shouldAnimate = react.default.useRef(!defaultOpen);
|
|
728
|
+
const previousDiffFromInitial = react.default.useRef(0);
|
|
729
|
+
const drawerRef = react.default.useRef(null);
|
|
730
|
+
const drawerHeightRef = react.default.useRef(((_drawerRef$current = drawerRef.current) === null || _drawerRef$current === void 0 ? void 0 : _drawerRef$current.getBoundingClientRect().height) || 0);
|
|
731
|
+
const drawerWidthRef = react.default.useRef(((_drawerRef$current2 = drawerRef.current) === null || _drawerRef$current2 === void 0 ? void 0 : _drawerRef$current2.getBoundingClientRect().width) || 0);
|
|
732
|
+
const initialDrawerHeight = react.default.useRef(0);
|
|
733
|
+
const { activeSnapPoint, activeSnapPointIndex, setActiveSnapPoint, onRelease: onReleaseSnapPoints, snapPointsOffset, onDrag: onDragSnapPoints, shouldFade, getPercentageDragged: getSnapPointsPercentageDragged } = useSnapPoints({
|
|
734
|
+
snapPoints,
|
|
735
|
+
activeSnapPointProp,
|
|
736
|
+
setActiveSnapPointProp,
|
|
737
|
+
drawerRef,
|
|
738
|
+
fadeFromIndex,
|
|
739
|
+
overlayRef,
|
|
740
|
+
onSnapPointChange: react.default.useCallback((activeSnapPointIndex$1) => {
|
|
741
|
+
if (snapPoints && activeSnapPointIndex$1 === snapPointsOffset.length - 1) openTime.current = /* @__PURE__ */ new Date();
|
|
742
|
+
}, []),
|
|
743
|
+
direction,
|
|
744
|
+
container,
|
|
745
|
+
snapToSequentialPoint
|
|
746
|
+
});
|
|
747
|
+
usePreventScroll({ isDisabled: !isOpen || isDragging || !modal || justReleased || !hasBeenOpened || !repositionInputs || !disablePreventScroll });
|
|
748
|
+
const { restorePositionSetting } = usePositionFixed({
|
|
749
|
+
isOpen,
|
|
750
|
+
modal,
|
|
751
|
+
nested: nested ?? false,
|
|
752
|
+
hasBeenOpened,
|
|
753
|
+
preventScrollRestoration,
|
|
754
|
+
noBodyStyles
|
|
755
|
+
});
|
|
756
|
+
function getScale() {
|
|
757
|
+
return (window.innerWidth - WINDOW_TOP_OFFSET) / window.innerWidth;
|
|
758
|
+
}
|
|
759
|
+
function onPress(event) {
|
|
760
|
+
var _drawerRef$current3, _drawerRef$current4;
|
|
761
|
+
if (!dismissible && !snapPoints) return;
|
|
762
|
+
if (drawerRef.current && !drawerRef.current.contains(event.target)) return;
|
|
763
|
+
drawerHeightRef.current = ((_drawerRef$current3 = drawerRef.current) === null || _drawerRef$current3 === void 0 ? void 0 : _drawerRef$current3.getBoundingClientRect().height) || 0;
|
|
764
|
+
drawerWidthRef.current = ((_drawerRef$current4 = drawerRef.current) === null || _drawerRef$current4 === void 0 ? void 0 : _drawerRef$current4.getBoundingClientRect().width) || 0;
|
|
765
|
+
setIsDragging(true);
|
|
766
|
+
dragStartTime.current = /* @__PURE__ */ new Date();
|
|
767
|
+
if (isIOS()) window.addEventListener("touchend", () => isAllowedToDrag.current = false, { once: true });
|
|
768
|
+
event.target.setPointerCapture(event.pointerId);
|
|
769
|
+
pointerStart.current = isVertical(direction) ? event.pageY : event.pageX;
|
|
770
|
+
}
|
|
771
|
+
function shouldDrag(el, isDraggingInDirection) {
|
|
772
|
+
var _window$getSelection;
|
|
773
|
+
let element = el;
|
|
774
|
+
const highlightedText = (_window$getSelection = window.getSelection()) === null || _window$getSelection === void 0 ? void 0 : _window$getSelection.toString();
|
|
775
|
+
const swipeAmount = drawerRef.current ? getTranslate(drawerRef.current, direction) : null;
|
|
776
|
+
const date = /* @__PURE__ */ new Date();
|
|
777
|
+
if (element.tagName === "SELECT") return false;
|
|
778
|
+
if (element.hasAttribute("data-vaul-no-drag") || element.closest("[data-vaul-no-drag]")) return false;
|
|
779
|
+
if (direction === "right" || direction === "left") return true;
|
|
780
|
+
if (openTime.current && date.getTime() - openTime.current.getTime() < 500) return false;
|
|
781
|
+
if (swipeAmount !== null) {
|
|
782
|
+
if (direction === "bottom" ? swipeAmount > 0 : swipeAmount < 0) return true;
|
|
783
|
+
}
|
|
784
|
+
if (highlightedText && highlightedText.length > 0) return false;
|
|
785
|
+
if (lastTimeDragPrevented.current && date.getTime() - lastTimeDragPrevented.current.getTime() < scrollLockTimeout && swipeAmount === 0) {
|
|
786
|
+
lastTimeDragPrevented.current = date;
|
|
787
|
+
return false;
|
|
788
|
+
}
|
|
789
|
+
if (isDraggingInDirection) {
|
|
790
|
+
lastTimeDragPrevented.current = date;
|
|
791
|
+
return false;
|
|
792
|
+
}
|
|
793
|
+
while (element) {
|
|
794
|
+
if (element.scrollHeight > element.clientHeight) {
|
|
795
|
+
if (element.scrollTop !== 0) {
|
|
796
|
+
lastTimeDragPrevented.current = /* @__PURE__ */ new Date();
|
|
797
|
+
return false;
|
|
798
|
+
}
|
|
799
|
+
if (element.getAttribute("role") === "dialog") return true;
|
|
800
|
+
}
|
|
801
|
+
element = element.parentNode;
|
|
802
|
+
}
|
|
803
|
+
return true;
|
|
804
|
+
}
|
|
805
|
+
function onDrag(event) {
|
|
806
|
+
if (!drawerRef.current) return;
|
|
807
|
+
if (isDragging) {
|
|
808
|
+
const directionMultiplier = direction === "bottom" || direction === "right" ? 1 : -1;
|
|
809
|
+
const draggedDistance = (pointerStart.current - (isVertical(direction) ? event.pageY : event.pageX)) * directionMultiplier;
|
|
810
|
+
const isDraggingInDirection = draggedDistance > 0;
|
|
811
|
+
const noCloseSnapPointsPreCondition = snapPoints && !dismissible && !isDraggingInDirection;
|
|
812
|
+
if (noCloseSnapPointsPreCondition && activeSnapPointIndex === 0) return;
|
|
813
|
+
const absDraggedDistance = Math.abs(draggedDistance);
|
|
814
|
+
const wrapper = document.querySelector("[data-vaul-drawer-wrapper]");
|
|
815
|
+
let percentageDragged = absDraggedDistance / (direction === "bottom" || direction === "top" ? drawerHeightRef.current : drawerWidthRef.current);
|
|
816
|
+
const snapPointPercentageDragged = getSnapPointsPercentageDragged(absDraggedDistance, isDraggingInDirection);
|
|
817
|
+
if (snapPointPercentageDragged !== null) percentageDragged = snapPointPercentageDragged;
|
|
818
|
+
if (noCloseSnapPointsPreCondition && percentageDragged >= 1) return;
|
|
819
|
+
if (!isAllowedToDrag.current && !shouldDrag(event.target, isDraggingInDirection)) return;
|
|
820
|
+
drawerRef.current.classList.add(DRAG_CLASS);
|
|
821
|
+
isAllowedToDrag.current = true;
|
|
822
|
+
set(drawerRef.current, { transition: "none" });
|
|
823
|
+
set(overlayRef.current, { transition: "none" });
|
|
824
|
+
if (snapPoints) onDragSnapPoints({ draggedDistance });
|
|
825
|
+
if (isDraggingInDirection && !snapPoints) {
|
|
826
|
+
const dampenedDraggedDistance = dampenValue(draggedDistance);
|
|
827
|
+
const translateValue = Math.min(dampenedDraggedDistance * -1, 0) * directionMultiplier;
|
|
828
|
+
set(drawerRef.current, { transform: isVertical(direction) ? `translate3d(0, ${translateValue}px, 0)` : `translate3d(${translateValue}px, 0, 0)` });
|
|
829
|
+
return;
|
|
830
|
+
}
|
|
831
|
+
const opacityValue = 1 - percentageDragged;
|
|
832
|
+
if (shouldFade || fadeFromIndex && activeSnapPointIndex === fadeFromIndex - 1) {
|
|
833
|
+
onDragProp === null || onDragProp === void 0 || onDragProp(event, percentageDragged);
|
|
834
|
+
set(overlayRef.current, {
|
|
835
|
+
opacity: `${opacityValue}`,
|
|
836
|
+
transition: "none"
|
|
837
|
+
}, true);
|
|
838
|
+
}
|
|
839
|
+
if (wrapper && overlayRef.current && shouldScaleBackground) {
|
|
840
|
+
const scaleValue = Math.min(getScale() + percentageDragged * (1 - getScale()), 1);
|
|
841
|
+
const borderRadiusValue = 8 - percentageDragged * 8;
|
|
842
|
+
const translateValue = Math.max(0, 14 - percentageDragged * 14);
|
|
843
|
+
set(wrapper, {
|
|
844
|
+
borderRadius: `${borderRadiusValue}px`,
|
|
845
|
+
transform: isVertical(direction) ? `scale(${scaleValue}) translate3d(0, ${translateValue}px, 0)` : `scale(${scaleValue}) translate3d(${translateValue}px, 0, 0)`,
|
|
846
|
+
transition: "none"
|
|
847
|
+
}, true);
|
|
848
|
+
}
|
|
849
|
+
if (!snapPoints) {
|
|
850
|
+
const translateValue = absDraggedDistance * directionMultiplier;
|
|
851
|
+
set(drawerRef.current, { transform: isVertical(direction) ? `translate3d(0, ${translateValue}px, 0)` : `translate3d(${translateValue}px, 0, 0)` });
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
react.default.useEffect(() => {
|
|
856
|
+
window.requestAnimationFrame(() => {
|
|
857
|
+
shouldAnimate.current = true;
|
|
858
|
+
});
|
|
859
|
+
}, []);
|
|
860
|
+
react.default.useEffect(() => {
|
|
861
|
+
var _window$visualViewpor2;
|
|
862
|
+
function onVisualViewportChange() {
|
|
863
|
+
if (!drawerRef.current || !repositionInputs) return;
|
|
864
|
+
const focusedElement = document.activeElement;
|
|
865
|
+
if (isInput(focusedElement) || keyboardIsOpen.current) {
|
|
866
|
+
var _window$visualViewpor;
|
|
867
|
+
const visualViewportHeight = ((_window$visualViewpor = window.visualViewport) === null || _window$visualViewpor === void 0 ? void 0 : _window$visualViewpor.height) || 0;
|
|
868
|
+
const totalHeight = window.innerHeight;
|
|
869
|
+
let diffFromInitial = totalHeight - visualViewportHeight;
|
|
870
|
+
const drawerHeight = drawerRef.current.getBoundingClientRect().height || 0;
|
|
871
|
+
const isTallEnough = drawerHeight > totalHeight * .8;
|
|
872
|
+
if (!initialDrawerHeight.current) initialDrawerHeight.current = drawerHeight;
|
|
873
|
+
const offsetFromTop = drawerRef.current.getBoundingClientRect().top;
|
|
874
|
+
if (Math.abs(previousDiffFromInitial.current - diffFromInitial) > 60) keyboardIsOpen.current = !keyboardIsOpen.current;
|
|
875
|
+
if (snapPoints && snapPoints.length > 0 && snapPointsOffset && activeSnapPointIndex) {
|
|
876
|
+
const activeSnapPointHeight = snapPointsOffset[activeSnapPointIndex] || 0;
|
|
877
|
+
diffFromInitial += activeSnapPointHeight;
|
|
878
|
+
}
|
|
879
|
+
previousDiffFromInitial.current = diffFromInitial;
|
|
880
|
+
if (drawerHeight > visualViewportHeight || keyboardIsOpen.current) {
|
|
881
|
+
const height = drawerRef.current.getBoundingClientRect().height;
|
|
882
|
+
let newDrawerHeight = height;
|
|
883
|
+
if (height > visualViewportHeight) newDrawerHeight = visualViewportHeight - (isTallEnough ? offsetFromTop : WINDOW_TOP_OFFSET);
|
|
884
|
+
if (fixed) drawerRef.current.style.height = `${height - Math.max(diffFromInitial, 0)}px`;
|
|
885
|
+
else drawerRef.current.style.height = `${Math.max(newDrawerHeight, visualViewportHeight - offsetFromTop)}px`;
|
|
886
|
+
} else if (!isMobileFirefox()) drawerRef.current.style.height = `${initialDrawerHeight.current}px`;
|
|
887
|
+
if (snapPoints && snapPoints.length > 0 && !keyboardIsOpen.current) drawerRef.current.style.bottom = `0px`;
|
|
888
|
+
else drawerRef.current.style.bottom = `${Math.max(diffFromInitial, 0)}px`;
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
(_window$visualViewpor2 = window.visualViewport) === null || _window$visualViewpor2 === void 0 || _window$visualViewpor2.addEventListener("resize", onVisualViewportChange);
|
|
892
|
+
return () => {
|
|
893
|
+
var _window$visualViewpor3;
|
|
894
|
+
return (_window$visualViewpor3 = window.visualViewport) === null || _window$visualViewpor3 === void 0 ? void 0 : _window$visualViewpor3.removeEventListener("resize", onVisualViewportChange);
|
|
895
|
+
};
|
|
896
|
+
}, [
|
|
897
|
+
activeSnapPointIndex,
|
|
898
|
+
snapPoints,
|
|
899
|
+
snapPointsOffset
|
|
900
|
+
]);
|
|
901
|
+
function closeDrawer(fromWithin) {
|
|
902
|
+
cancelDrag();
|
|
903
|
+
onClose === null || onClose === void 0 || onClose();
|
|
904
|
+
if (!fromWithin) setIsOpen(false);
|
|
905
|
+
setTimeout(() => {
|
|
906
|
+
if (snapPoints) setActiveSnapPoint(snapPoints[0]);
|
|
907
|
+
}, TRANSITIONS.DURATION * 1e3);
|
|
908
|
+
}
|
|
909
|
+
function resetDrawer() {
|
|
910
|
+
if (!drawerRef.current) return;
|
|
911
|
+
const wrapper = document.querySelector("[data-vaul-drawer-wrapper]");
|
|
912
|
+
const currentSwipeAmount = getTranslate(drawerRef.current, direction);
|
|
913
|
+
set(drawerRef.current, {
|
|
914
|
+
transform: "translate3d(0, 0, 0)",
|
|
915
|
+
transition: `transform ${TRANSITIONS.DURATION}s cubic-bezier(${TRANSITIONS.EASE.join(",")})`
|
|
916
|
+
});
|
|
917
|
+
set(overlayRef.current, {
|
|
918
|
+
transition: `opacity ${TRANSITIONS.DURATION}s cubic-bezier(${TRANSITIONS.EASE.join(",")})`,
|
|
919
|
+
opacity: "1"
|
|
920
|
+
});
|
|
921
|
+
if (shouldScaleBackground && currentSwipeAmount && currentSwipeAmount > 0 && isOpen) set(wrapper, {
|
|
922
|
+
borderRadius: `${BORDER_RADIUS}px`,
|
|
923
|
+
overflow: "hidden",
|
|
924
|
+
...isVertical(direction) ? {
|
|
925
|
+
transform: `scale(${getScale()}) translate3d(0, calc(env(safe-area-inset-top) + 14px), 0)`,
|
|
926
|
+
transformOrigin: "top"
|
|
927
|
+
} : {
|
|
928
|
+
transform: `scale(${getScale()}) translate3d(calc(env(safe-area-inset-top) + 14px), 0, 0)`,
|
|
929
|
+
transformOrigin: "left"
|
|
930
|
+
},
|
|
931
|
+
transitionProperty: "transform, border-radius",
|
|
932
|
+
transitionDuration: `${TRANSITIONS.DURATION}s`,
|
|
933
|
+
transitionTimingFunction: `cubic-bezier(${TRANSITIONS.EASE.join(",")})`
|
|
934
|
+
}, true);
|
|
935
|
+
}
|
|
936
|
+
function cancelDrag() {
|
|
937
|
+
if (!isDragging || !drawerRef.current) return;
|
|
938
|
+
drawerRef.current.classList.remove(DRAG_CLASS);
|
|
939
|
+
isAllowedToDrag.current = false;
|
|
940
|
+
setIsDragging(false);
|
|
941
|
+
dragEndTime.current = /* @__PURE__ */ new Date();
|
|
942
|
+
}
|
|
943
|
+
function onRelease(event) {
|
|
944
|
+
if (!isDragging || !drawerRef.current) return;
|
|
945
|
+
drawerRef.current.classList.remove(DRAG_CLASS);
|
|
946
|
+
isAllowedToDrag.current = false;
|
|
947
|
+
setIsDragging(false);
|
|
948
|
+
dragEndTime.current = /* @__PURE__ */ new Date();
|
|
949
|
+
const swipeAmount = getTranslate(drawerRef.current, direction);
|
|
950
|
+
if (!event || !shouldDrag(event.target, false) || !swipeAmount || Number.isNaN(swipeAmount)) return;
|
|
951
|
+
if (dragStartTime.current === null) return;
|
|
952
|
+
const timeTaken = dragEndTime.current.getTime() - dragStartTime.current.getTime();
|
|
953
|
+
const distMoved = pointerStart.current - (isVertical(direction) ? event.pageY : event.pageX);
|
|
954
|
+
const velocity = Math.abs(distMoved) / timeTaken;
|
|
955
|
+
if (velocity > .05) {
|
|
956
|
+
setJustReleased(true);
|
|
957
|
+
setTimeout(() => {
|
|
958
|
+
setJustReleased(false);
|
|
959
|
+
}, 200);
|
|
960
|
+
}
|
|
961
|
+
if (snapPoints) {
|
|
962
|
+
onReleaseSnapPoints({
|
|
963
|
+
draggedDistance: distMoved * (direction === "bottom" || direction === "right" ? 1 : -1),
|
|
964
|
+
closeDrawer,
|
|
965
|
+
velocity,
|
|
966
|
+
dismissible
|
|
967
|
+
});
|
|
968
|
+
onReleaseProp === null || onReleaseProp === void 0 || onReleaseProp(event, true);
|
|
969
|
+
return;
|
|
970
|
+
}
|
|
971
|
+
if (direction === "bottom" || direction === "right" ? distMoved > 0 : distMoved < 0) {
|
|
972
|
+
resetDrawer();
|
|
973
|
+
onReleaseProp === null || onReleaseProp === void 0 || onReleaseProp(event, true);
|
|
974
|
+
return;
|
|
975
|
+
}
|
|
976
|
+
if (velocity > VELOCITY_THRESHOLD) {
|
|
977
|
+
closeDrawer();
|
|
978
|
+
onReleaseProp === null || onReleaseProp === void 0 || onReleaseProp(event, false);
|
|
979
|
+
return;
|
|
980
|
+
}
|
|
981
|
+
const visibleDrawerHeight = Math.min(drawerRef.current.getBoundingClientRect().height ?? 0, window.innerHeight);
|
|
982
|
+
const visibleDrawerWidth = Math.min(drawerRef.current.getBoundingClientRect().width ?? 0, window.innerWidth);
|
|
983
|
+
const isHorizontalSwipe = direction === "left" || direction === "right";
|
|
984
|
+
if (Math.abs(swipeAmount) >= (isHorizontalSwipe ? visibleDrawerWidth : visibleDrawerHeight) * closeThreshold) {
|
|
985
|
+
closeDrawer();
|
|
986
|
+
onReleaseProp === null || onReleaseProp === void 0 || onReleaseProp(event, false);
|
|
987
|
+
return;
|
|
988
|
+
}
|
|
989
|
+
onReleaseProp === null || onReleaseProp === void 0 || onReleaseProp(event, true);
|
|
990
|
+
resetDrawer();
|
|
991
|
+
}
|
|
992
|
+
react.default.useEffect(() => {
|
|
993
|
+
if (isOpen) {
|
|
994
|
+
set(document.documentElement, { scrollBehavior: "auto" });
|
|
995
|
+
openTime.current = /* @__PURE__ */ new Date();
|
|
996
|
+
}
|
|
997
|
+
return () => {
|
|
998
|
+
reset(document.documentElement, "scrollBehavior");
|
|
999
|
+
};
|
|
1000
|
+
}, [isOpen]);
|
|
1001
|
+
function onNestedOpenChange(o) {
|
|
1002
|
+
const scale = o ? (window.innerWidth - NESTED_DISPLACEMENT) / window.innerWidth : 1;
|
|
1003
|
+
const initialTranslate = o ? -NESTED_DISPLACEMENT : 0;
|
|
1004
|
+
if (nestedOpenChangeTimer.current) window.clearTimeout(nestedOpenChangeTimer.current);
|
|
1005
|
+
set(drawerRef.current, {
|
|
1006
|
+
transition: `transform ${TRANSITIONS.DURATION}s cubic-bezier(${TRANSITIONS.EASE.join(",")})`,
|
|
1007
|
+
transform: isVertical(direction) ? `scale(${scale}) translate3d(0, ${initialTranslate}px, 0)` : `scale(${scale}) translate3d(${initialTranslate}px, 0, 0)`
|
|
1008
|
+
});
|
|
1009
|
+
if (!o && drawerRef.current) nestedOpenChangeTimer.current = setTimeout(() => {
|
|
1010
|
+
const translateValue = getTranslate(drawerRef.current, direction);
|
|
1011
|
+
set(drawerRef.current, {
|
|
1012
|
+
transition: "none",
|
|
1013
|
+
transform: isVertical(direction) ? `translate3d(0, ${translateValue}px, 0)` : `translate3d(${translateValue}px, 0, 0)`
|
|
1014
|
+
});
|
|
1015
|
+
}, 500);
|
|
1016
|
+
}
|
|
1017
|
+
function onNestedDrag(_event, percentageDragged) {
|
|
1018
|
+
if (percentageDragged < 0) return;
|
|
1019
|
+
const initialScale = (window.innerWidth - NESTED_DISPLACEMENT) / window.innerWidth;
|
|
1020
|
+
const newScale = initialScale + percentageDragged * (1 - initialScale);
|
|
1021
|
+
const newTranslate = -NESTED_DISPLACEMENT + percentageDragged * NESTED_DISPLACEMENT;
|
|
1022
|
+
set(drawerRef.current, {
|
|
1023
|
+
transform: isVertical(direction) ? `scale(${newScale}) translate3d(0, ${newTranslate}px, 0)` : `scale(${newScale}) translate3d(${newTranslate}px, 0, 0)`,
|
|
1024
|
+
transition: "none"
|
|
1025
|
+
});
|
|
1026
|
+
}
|
|
1027
|
+
function onNestedRelease(_event, o) {
|
|
1028
|
+
const dim = isVertical(direction) ? window.innerHeight : window.innerWidth;
|
|
1029
|
+
const scale = o ? (dim - NESTED_DISPLACEMENT) / dim : 1;
|
|
1030
|
+
const translate = o ? -NESTED_DISPLACEMENT : 0;
|
|
1031
|
+
if (o) set(drawerRef.current, {
|
|
1032
|
+
transition: `transform ${TRANSITIONS.DURATION}s cubic-bezier(${TRANSITIONS.EASE.join(",")})`,
|
|
1033
|
+
transform: isVertical(direction) ? `scale(${scale}) translate3d(0, ${translate}px, 0)` : `scale(${scale}) translate3d(${translate}px, 0, 0)`
|
|
1034
|
+
});
|
|
1035
|
+
}
|
|
1036
|
+
react.default.useEffect(() => {
|
|
1037
|
+
if (!modal) window.requestAnimationFrame(() => {
|
|
1038
|
+
document.body.style.pointerEvents = "auto";
|
|
1039
|
+
});
|
|
1040
|
+
}, [modal]);
|
|
1041
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_base_ui_react.Dialog.Root, {
|
|
1042
|
+
defaultOpen,
|
|
1043
|
+
onOpenChange: (open) => {
|
|
1044
|
+
if (!dismissible && !open) return;
|
|
1045
|
+
if (open) setHasBeenOpened(true);
|
|
1046
|
+
else closeDrawer(true);
|
|
1047
|
+
setIsOpen(open);
|
|
1048
|
+
},
|
|
1049
|
+
open: isOpen,
|
|
1050
|
+
modal,
|
|
1051
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DrawerContext.Provider, {
|
|
1052
|
+
value: {
|
|
1053
|
+
activeSnapPoint,
|
|
1054
|
+
snapPoints,
|
|
1055
|
+
setActiveSnapPoint,
|
|
1056
|
+
drawerRef,
|
|
1057
|
+
overlayRef,
|
|
1058
|
+
onOpenChange,
|
|
1059
|
+
onPress,
|
|
1060
|
+
onRelease,
|
|
1061
|
+
onDrag,
|
|
1062
|
+
dismissible,
|
|
1063
|
+
shouldAnimate,
|
|
1064
|
+
handleOnly,
|
|
1065
|
+
isOpen,
|
|
1066
|
+
isDragging,
|
|
1067
|
+
shouldFade,
|
|
1068
|
+
closeDrawer,
|
|
1069
|
+
onNestedDrag,
|
|
1070
|
+
onNestedOpenChange,
|
|
1071
|
+
onNestedRelease,
|
|
1072
|
+
keyboardIsOpen,
|
|
1073
|
+
modal,
|
|
1074
|
+
snapPointsOffset,
|
|
1075
|
+
activeSnapPointIndex,
|
|
1076
|
+
direction,
|
|
1077
|
+
shouldScaleBackground,
|
|
1078
|
+
setBackgroundColorOnScale,
|
|
1079
|
+
noBodyStyles,
|
|
1080
|
+
container,
|
|
1081
|
+
autoFocus
|
|
1082
|
+
},
|
|
1083
|
+
children
|
|
1084
|
+
})
|
|
1085
|
+
});
|
|
1086
|
+
}
|
|
1087
|
+
const Overlay = react.default.forwardRef(function({ ...rest }, ref) {
|
|
1088
|
+
const { overlayRef, snapPoints, onRelease, shouldFade, isOpen, modal, shouldAnimate } = useDrawerContext();
|
|
1089
|
+
const composedRef = useComposedRefs(ref, overlayRef);
|
|
1090
|
+
const hasSnapPoints = snapPoints && snapPoints.length > 0;
|
|
1091
|
+
const onMouseUp = react.default.useCallback((event) => onRelease(event), [onRelease]);
|
|
1092
|
+
if (!modal) return null;
|
|
1093
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_base_ui_react.Dialog.Backdrop, {
|
|
1094
|
+
onMouseUp,
|
|
1095
|
+
ref: composedRef,
|
|
1096
|
+
"data-vaul-overlay": "",
|
|
1097
|
+
"data-vaul-snap-points": isOpen && hasSnapPoints ? "true" : "false",
|
|
1098
|
+
"data-vaul-snap-points-overlay": isOpen && shouldFade ? "true" : "false",
|
|
1099
|
+
"data-vaul-animate": (shouldAnimate === null || shouldAnimate === void 0 ? void 0 : shouldAnimate.current) ? "true" : "false",
|
|
1100
|
+
...rest
|
|
1101
|
+
});
|
|
1102
|
+
});
|
|
1103
|
+
Overlay.displayName = "Drawer.Overlay";
|
|
1104
|
+
const Content = react.default.forwardRef(function({ style, ...rest }, ref) {
|
|
1105
|
+
const { drawerRef, onPress, onRelease, onDrag, keyboardIsOpen, snapPointsOffset, activeSnapPointIndex, modal, isOpen, direction, snapPoints, container, handleOnly, shouldAnimate, autoFocus } = useDrawerContext();
|
|
1106
|
+
const [delayedSnapPoints, setDelayedSnapPoints] = react.default.useState(false);
|
|
1107
|
+
const composedRef = useComposedRefs(ref, drawerRef);
|
|
1108
|
+
const pointerStartRef = react.default.useRef(null);
|
|
1109
|
+
const lastKnownPointerEventRef = react.default.useRef(null);
|
|
1110
|
+
const wasBeyondThePointRef = react.default.useRef(false);
|
|
1111
|
+
const hasSnapPoints = snapPoints && snapPoints.length > 0;
|
|
1112
|
+
useScaleBackground();
|
|
1113
|
+
const isDeltaInDirection = (delta, direction$1, threshold = 0) => {
|
|
1114
|
+
if (wasBeyondThePointRef.current) return true;
|
|
1115
|
+
const deltaY = Math.abs(delta.y);
|
|
1116
|
+
const deltaX = Math.abs(delta.x);
|
|
1117
|
+
const isDeltaX = deltaX > deltaY;
|
|
1118
|
+
const dFactor = ["bottom", "right"].includes(direction$1) ? 1 : -1;
|
|
1119
|
+
if (direction$1 === "left" || direction$1 === "right") {
|
|
1120
|
+
if (!(delta.x * dFactor < 0) && deltaX >= 0 && deltaX <= threshold) return isDeltaX;
|
|
1121
|
+
} else if (!(delta.y * dFactor < 0) && deltaY >= 0 && deltaY <= threshold) return !isDeltaX;
|
|
1122
|
+
wasBeyondThePointRef.current = true;
|
|
1123
|
+
return true;
|
|
1124
|
+
};
|
|
1125
|
+
react.default.useEffect(() => {
|
|
1126
|
+
if (hasSnapPoints) window.requestAnimationFrame(() => {
|
|
1127
|
+
setDelayedSnapPoints(true);
|
|
1128
|
+
});
|
|
1129
|
+
}, []);
|
|
1130
|
+
function handleOnPointerUp(event) {
|
|
1131
|
+
pointerStartRef.current = null;
|
|
1132
|
+
wasBeyondThePointRef.current = false;
|
|
1133
|
+
onRelease(event);
|
|
1134
|
+
}
|
|
1135
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_base_ui_react.Dialog.Viewport, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_base_ui_react.Dialog.Popup, {
|
|
1136
|
+
"data-vaul-drawer-direction": direction,
|
|
1137
|
+
"data-vaul-drawer": "",
|
|
1138
|
+
"data-vaul-delayed-snap-points": delayedSnapPoints ? "true" : "false",
|
|
1139
|
+
"data-vaul-snap-points": isOpen && hasSnapPoints ? "true" : "false",
|
|
1140
|
+
"data-vaul-custom-container": container ? "true" : "false",
|
|
1141
|
+
"data-vaul-animate": (shouldAnimate === null || shouldAnimate === void 0 ? void 0 : shouldAnimate.current) ? "true" : "false",
|
|
1142
|
+
...rest,
|
|
1143
|
+
ref: composedRef,
|
|
1144
|
+
style: snapPointsOffset && snapPointsOffset.length > 0 ? {
|
|
1145
|
+
"--snap-point-height": `${snapPointsOffset[activeSnapPointIndex ?? 0]}px`,
|
|
1146
|
+
...style
|
|
1147
|
+
} : style,
|
|
1148
|
+
onPointerDown: (event) => {
|
|
1149
|
+
var _rest$onPointerDown;
|
|
1150
|
+
if (handleOnly) return;
|
|
1151
|
+
(_rest$onPointerDown = rest.onPointerDown) === null || _rest$onPointerDown === void 0 || _rest$onPointerDown.call(rest, event);
|
|
1152
|
+
pointerStartRef.current = {
|
|
1153
|
+
x: event.pageX,
|
|
1154
|
+
y: event.pageY
|
|
1155
|
+
};
|
|
1156
|
+
onPress(event);
|
|
1157
|
+
},
|
|
1158
|
+
onPointerMove: (event) => {
|
|
1159
|
+
var _rest$onPointerMove;
|
|
1160
|
+
lastKnownPointerEventRef.current = event;
|
|
1161
|
+
if (handleOnly) return;
|
|
1162
|
+
(_rest$onPointerMove = rest.onPointerMove) === null || _rest$onPointerMove === void 0 || _rest$onPointerMove.call(rest, event);
|
|
1163
|
+
if (!pointerStartRef.current) return;
|
|
1164
|
+
const yPosition = event.pageY - pointerStartRef.current.y;
|
|
1165
|
+
const xPosition = event.pageX - pointerStartRef.current.x;
|
|
1166
|
+
const swipeStartThreshold = event.pointerType === "touch" ? 10 : 2;
|
|
1167
|
+
if (isDeltaInDirection({
|
|
1168
|
+
x: xPosition,
|
|
1169
|
+
y: yPosition
|
|
1170
|
+
}, direction, swipeStartThreshold)) onDrag(event);
|
|
1171
|
+
else if (Math.abs(xPosition) > swipeStartThreshold || Math.abs(yPosition) > swipeStartThreshold) pointerStartRef.current = null;
|
|
1172
|
+
},
|
|
1173
|
+
onPointerUp: (event) => {
|
|
1174
|
+
var _rest$onPointerUp;
|
|
1175
|
+
(_rest$onPointerUp = rest.onPointerUp) === null || _rest$onPointerUp === void 0 || _rest$onPointerUp.call(rest, event);
|
|
1176
|
+
pointerStartRef.current = null;
|
|
1177
|
+
wasBeyondThePointRef.current = false;
|
|
1178
|
+
onRelease(event);
|
|
1179
|
+
},
|
|
1180
|
+
onPointerOut: (event) => {
|
|
1181
|
+
var _rest$onPointerOut;
|
|
1182
|
+
(_rest$onPointerOut = rest.onPointerOut) === null || _rest$onPointerOut === void 0 || _rest$onPointerOut.call(rest, event);
|
|
1183
|
+
handleOnPointerUp(lastKnownPointerEventRef.current);
|
|
1184
|
+
},
|
|
1185
|
+
onContextMenu: (event) => {
|
|
1186
|
+
var _rest$onContextMenu;
|
|
1187
|
+
(_rest$onContextMenu = rest.onContextMenu) === null || _rest$onContextMenu === void 0 || _rest$onContextMenu.call(rest, event);
|
|
1188
|
+
if (lastKnownPointerEventRef.current) handleOnPointerUp(lastKnownPointerEventRef.current);
|
|
1189
|
+
}
|
|
1190
|
+
}) });
|
|
1191
|
+
});
|
|
1192
|
+
Content.displayName = "Drawer.Content";
|
|
1193
|
+
const LONG_HANDLE_PRESS_TIMEOUT = 250;
|
|
1194
|
+
const DOUBLE_TAP_TIMEOUT = 120;
|
|
1195
|
+
const Handle = react.default.forwardRef(function({ preventCycle = false, children, ...rest }, ref) {
|
|
1196
|
+
const { closeDrawer, isDragging, snapPoints, activeSnapPoint, setActiveSnapPoint, dismissible, handleOnly, isOpen, onPress, onDrag } = useDrawerContext();
|
|
1197
|
+
const closeTimeoutIdRef = react.default.useRef(null);
|
|
1198
|
+
const shouldCancelInteractionRef = react.default.useRef(false);
|
|
1199
|
+
function handleStartCycle() {
|
|
1200
|
+
if (shouldCancelInteractionRef.current) {
|
|
1201
|
+
handleCancelInteraction();
|
|
1202
|
+
return;
|
|
1203
|
+
}
|
|
1204
|
+
window.setTimeout(() => {
|
|
1205
|
+
handleCycleSnapPoints();
|
|
1206
|
+
}, DOUBLE_TAP_TIMEOUT);
|
|
1207
|
+
}
|
|
1208
|
+
function handleCycleSnapPoints() {
|
|
1209
|
+
if (isDragging || preventCycle || shouldCancelInteractionRef.current) {
|
|
1210
|
+
handleCancelInteraction();
|
|
1211
|
+
return;
|
|
1212
|
+
}
|
|
1213
|
+
handleCancelInteraction();
|
|
1214
|
+
if (!snapPoints || snapPoints.length === 0) {
|
|
1215
|
+
if (!dismissible) closeDrawer();
|
|
1216
|
+
return;
|
|
1217
|
+
}
|
|
1218
|
+
if (activeSnapPoint === snapPoints[snapPoints.length - 1] && dismissible) {
|
|
1219
|
+
closeDrawer();
|
|
1220
|
+
return;
|
|
1221
|
+
}
|
|
1222
|
+
const currentSnapIndex = snapPoints.findIndex((point) => point === activeSnapPoint);
|
|
1223
|
+
if (currentSnapIndex === -1) return;
|
|
1224
|
+
const nextSnapPoint = snapPoints[currentSnapIndex + 1];
|
|
1225
|
+
setActiveSnapPoint(nextSnapPoint);
|
|
1226
|
+
}
|
|
1227
|
+
function handleStartInteraction() {
|
|
1228
|
+
closeTimeoutIdRef.current = window.setTimeout(() => {
|
|
1229
|
+
shouldCancelInteractionRef.current = true;
|
|
1230
|
+
}, LONG_HANDLE_PRESS_TIMEOUT);
|
|
1231
|
+
}
|
|
1232
|
+
function handleCancelInteraction() {
|
|
1233
|
+
if (closeTimeoutIdRef.current) window.clearTimeout(closeTimeoutIdRef.current);
|
|
1234
|
+
shouldCancelInteractionRef.current = false;
|
|
1235
|
+
}
|
|
1236
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1237
|
+
onClick: handleStartCycle,
|
|
1238
|
+
onPointerCancel: handleCancelInteraction,
|
|
1239
|
+
onPointerDown: (e) => {
|
|
1240
|
+
if (handleOnly) onPress({
|
|
1241
|
+
...e,
|
|
1242
|
+
preventBaseUIHandler: () => {}
|
|
1243
|
+
});
|
|
1244
|
+
handleStartInteraction();
|
|
1245
|
+
},
|
|
1246
|
+
onPointerMove: (e) => {
|
|
1247
|
+
if (handleOnly) onDrag({
|
|
1248
|
+
...e,
|
|
1249
|
+
preventBaseUIHandler: () => {}
|
|
1250
|
+
});
|
|
1251
|
+
},
|
|
1252
|
+
ref,
|
|
1253
|
+
"data-vaul-drawer-visible": isOpen ? "true" : "false",
|
|
1254
|
+
"data-vaul-handle": "",
|
|
1255
|
+
"aria-hidden": "true",
|
|
1256
|
+
...rest,
|
|
1257
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1258
|
+
"data-vaul-handle-hitarea": "",
|
|
1259
|
+
"aria-hidden": "true",
|
|
1260
|
+
children
|
|
1261
|
+
})
|
|
1262
|
+
});
|
|
1263
|
+
});
|
|
1264
|
+
Handle.displayName = "Drawer.Handle";
|
|
1265
|
+
function NestedRoot({ onDrag, onOpenChange, open: nestedIsOpen, ...rest }) {
|
|
1266
|
+
const { onNestedDrag, onNestedOpenChange, onNestedRelease } = useDrawerContext();
|
|
1267
|
+
if (!onNestedDrag) throw new Error("Drawer.NestedRoot must be placed in another drawer");
|
|
1268
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Root, {
|
|
1269
|
+
nested: true,
|
|
1270
|
+
open: nestedIsOpen,
|
|
1271
|
+
onClose: () => {
|
|
1272
|
+
onNestedOpenChange(false);
|
|
1273
|
+
},
|
|
1274
|
+
onDrag: (e, p) => {
|
|
1275
|
+
onNestedDrag(e, p);
|
|
1276
|
+
onDrag === null || onDrag === void 0 || onDrag(e, p);
|
|
1277
|
+
},
|
|
1278
|
+
onOpenChange: (o) => {
|
|
1279
|
+
if (o) onNestedOpenChange(o);
|
|
1280
|
+
onOpenChange === null || onOpenChange === void 0 || onOpenChange(o);
|
|
1281
|
+
},
|
|
1282
|
+
onRelease: onNestedRelease,
|
|
1283
|
+
...rest
|
|
1284
|
+
});
|
|
1285
|
+
}
|
|
1286
|
+
function Portal(props) {
|
|
1287
|
+
const context = useDrawerContext();
|
|
1288
|
+
const { container = context.container, ...portalProps } = props;
|
|
1289
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_base_ui_react.Dialog.Portal, {
|
|
1290
|
+
container,
|
|
1291
|
+
...portalProps
|
|
1292
|
+
});
|
|
1293
|
+
}
|
|
1294
|
+
const Drawer = {
|
|
1295
|
+
Root,
|
|
1296
|
+
NestedRoot,
|
|
1297
|
+
Content,
|
|
1298
|
+
Overlay,
|
|
1299
|
+
Trigger: _base_ui_react.Dialog.Trigger,
|
|
1300
|
+
Portal,
|
|
1301
|
+
Handle,
|
|
1302
|
+
Close: _base_ui_react.Dialog.Close,
|
|
1303
|
+
Title: _base_ui_react.Dialog.Title,
|
|
1304
|
+
Description: _base_ui_react.Dialog.Description
|
|
1305
|
+
};
|
|
1306
|
+
|
|
1307
|
+
//#endregion
|
|
1308
|
+
exports.Content = Content;
|
|
1309
|
+
exports.Drawer = Drawer;
|
|
1310
|
+
exports.Handle = Handle;
|
|
1311
|
+
exports.NestedRoot = NestedRoot;
|
|
1312
|
+
exports.Overlay = Overlay;
|
|
1313
|
+
exports.Portal = Portal;
|
|
1314
|
+
exports.Root = Root;
|