@surdeddd/bottom-sheet 0.1.0 → 0.3.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/README.md +57 -21
- package/dist/BottomSheetEngine-CAymrH-P.d.cts +285 -0
- package/dist/BottomSheetEngine-DcqcuudN.d.ts +285 -0
- package/dist/element.cjs +775 -537
- package/dist/element.d.cts +9 -57
- package/dist/element.d.ts +9 -57
- package/dist/element.iife.global.js +2 -2
- package/dist/element.js +775 -537
- package/dist/index.cjs +760 -600
- package/dist/index.d.cts +18 -85
- package/dist/index.d.ts +18 -85
- package/dist/index.js +758 -601
- package/dist/integrations/formik.d.cts +2 -8
- package/dist/integrations/formik.d.ts +2 -8
- package/dist/integrations/react-hook-form.d.cts +2 -11
- package/dist/integrations/react-hook-form.d.ts +2 -11
- package/dist/overlay.cjs +168 -122
- package/dist/overlay.d.cts +14 -137
- package/dist/overlay.d.ts +14 -137
- package/dist/overlay.js +168 -122
- package/dist/preact.d.cts +2 -2
- package/dist/preact.d.ts +2 -2
- package/dist/preact.js +0 -1
- package/dist/qwik.cjs +667 -519
- package/dist/qwik.d.cts +3 -24
- package/dist/qwik.d.ts +3 -24
- package/dist/qwik.js +668 -520
- package/dist/react.cjs +873 -648
- package/dist/react.d.cts +34 -159
- package/dist/react.d.ts +34 -159
- package/dist/react.js +873 -648
- package/dist/solid.cjs +703 -560
- package/dist/solid.d.cts +264 -32
- package/dist/solid.d.ts +264 -32
- package/dist/solid.js +704 -561
- package/dist/solid.source.jsx +3215 -0
- package/dist/styles.css +37 -112
- package/dist/svelte-core.cjs +3149 -0
- package/dist/svelte-core.js +3147 -0
- package/dist/svelte-src/BottomSheet.svelte +210 -0
- package/dist/svelte-src/index.js +2 -0
- package/dist/svelte.cjs +718 -547
- package/dist/svelte.d.cts +100 -0
- package/dist/svelte.d.ts +15 -4
- package/dist/svelte.js +719 -548
- package/dist/themes/ios.css +0 -3
- package/dist/themes/material.css +0 -3
- package/dist/themes/vercel.css +0 -3
- package/dist/{transform-Brkf-wzo.d.cts → transform-jVfAikE9.d.cts} +0 -1
- package/dist/{transform-Brkf-wzo.d.ts → transform-jVfAikE9.d.ts} +0 -1
- package/dist/vue.cjs +677 -526
- package/dist/vue.d.cts +78 -0
- package/dist/vue.d.ts +12 -2
- package/dist/vue.js +678 -527
- package/package.json +105 -45
- package/dist/BottomSheetEngine-CvSVbo6c.d.cts +0 -704
- package/dist/BottomSheetEngine-DbO8rW_3.d.ts +0 -704
package/dist/overlay.js
CHANGED
|
@@ -20,6 +20,18 @@ var SheetStack = class {
|
|
|
20
20
|
this.entries.splice(idx, 1);
|
|
21
21
|
this.recompute();
|
|
22
22
|
}
|
|
23
|
+
promote(id) {
|
|
24
|
+
const idx = this.entries.findIndex((e) => e.id === id);
|
|
25
|
+
if (idx === -1) return;
|
|
26
|
+
if (idx !== this.entries.length - 1) {
|
|
27
|
+
const [entry] = this.entries.splice(idx, 1);
|
|
28
|
+
this.entries.push(entry);
|
|
29
|
+
}
|
|
30
|
+
this.recompute();
|
|
31
|
+
}
|
|
32
|
+
update() {
|
|
33
|
+
this.recompute();
|
|
34
|
+
}
|
|
23
35
|
size() {
|
|
24
36
|
return this.entries.length;
|
|
25
37
|
}
|
|
@@ -27,9 +39,24 @@ var SheetStack = class {
|
|
|
27
39
|
this.entries = [];
|
|
28
40
|
}
|
|
29
41
|
recompute() {
|
|
42
|
+
let top = null;
|
|
43
|
+
const open = [];
|
|
44
|
+
for (const entry of this.entries) {
|
|
45
|
+
if (!entry.isOpen || entry.isOpen()) {
|
|
46
|
+
top = entry;
|
|
47
|
+
open.push(entry);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
if (!top && this.entries.length > 0) {
|
|
51
|
+
top = this.entries[this.entries.length - 1];
|
|
52
|
+
}
|
|
30
53
|
this.entries.forEach((entry, i) => {
|
|
31
54
|
entry.setZIndex(BASE_Z + i * STEP);
|
|
32
|
-
entry.setIsTop(
|
|
55
|
+
entry.setIsTop(entry === top);
|
|
56
|
+
if (entry.setDepth) {
|
|
57
|
+
const openIdx = open.indexOf(entry);
|
|
58
|
+
entry.setDepth(openIdx === -1 ? 0 : open.length - 1 - openIdx);
|
|
59
|
+
}
|
|
33
60
|
});
|
|
34
61
|
}
|
|
35
62
|
};
|
|
@@ -152,9 +179,13 @@ var isVisible = (el) => {
|
|
|
152
179
|
var focusables = (root) => Array.from(root.querySelectorAll(FOCUSABLE)).filter(
|
|
153
180
|
(el) => !el.hasAttribute("inert") && isVisible(el) && !el.matches("[aria-hidden='true']")
|
|
154
181
|
);
|
|
182
|
+
var trapStack = [];
|
|
155
183
|
var installFocusTrap = (container, options = {}) => {
|
|
156
184
|
if (typeof document === "undefined") return () => {
|
|
157
185
|
};
|
|
186
|
+
const token = {};
|
|
187
|
+
trapStack.push(token);
|
|
188
|
+
const isActive = () => trapStack[trapStack.length - 1] === token;
|
|
158
189
|
const previouslyFocused = document.activeElement;
|
|
159
190
|
const focusInitial = () => {
|
|
160
191
|
const initial = typeof options.initialFocus === "string" ? container.querySelector(options.initialFocus) : options.initialFocus ?? focusables(container)[0] ?? container;
|
|
@@ -162,6 +193,7 @@ var installFocusTrap = (container, options = {}) => {
|
|
|
162
193
|
};
|
|
163
194
|
focusInitial();
|
|
164
195
|
const handleKey = (e) => {
|
|
196
|
+
if (!isActive()) return;
|
|
165
197
|
if (e.key === "Escape" && options.onEscape) {
|
|
166
198
|
options.onEscape();
|
|
167
199
|
return;
|
|
@@ -184,6 +216,7 @@ var installFocusTrap = (container, options = {}) => {
|
|
|
184
216
|
}
|
|
185
217
|
};
|
|
186
218
|
const handleFocusIn = (e) => {
|
|
219
|
+
if (!isActive()) return;
|
|
187
220
|
const target = e.target;
|
|
188
221
|
if (target && !container.contains(target)) {
|
|
189
222
|
const list = focusables(container);
|
|
@@ -196,6 +229,8 @@ var installFocusTrap = (container, options = {}) => {
|
|
|
196
229
|
return () => {
|
|
197
230
|
if (released) return;
|
|
198
231
|
released = true;
|
|
232
|
+
const idx = trapStack.indexOf(token);
|
|
233
|
+
if (idx !== -1) trapStack.splice(idx, 1);
|
|
199
234
|
document.removeEventListener("keydown", handleKey, true);
|
|
200
235
|
document.removeEventListener("focusin", handleFocusIn, true);
|
|
201
236
|
if (previouslyFocused && document.contains(previouslyFocused)) {
|
|
@@ -251,11 +286,17 @@ function createInertSiblings(rootProvider) {
|
|
|
251
286
|
return {
|
|
252
287
|
apply() {
|
|
253
288
|
if (typeof document === "undefined") return;
|
|
254
|
-
|
|
255
|
-
|
|
289
|
+
let top = rootProvider();
|
|
290
|
+
while (top.parentElement && top.parentElement !== document.body) {
|
|
291
|
+
top = top.parentElement;
|
|
292
|
+
}
|
|
293
|
+
const parent = top.parentElement ?? document.body;
|
|
256
294
|
for (const child of Array.from(parent.children)) {
|
|
257
|
-
if (child ===
|
|
295
|
+
if (child === top) continue;
|
|
258
296
|
if (child.hasAttribute("inert")) continue;
|
|
297
|
+
if (child.classList.contains("bs-backdrop") || child.classList.contains("bs-screen") || child.classList.contains("bs-root")) {
|
|
298
|
+
continue;
|
|
299
|
+
}
|
|
259
300
|
child.setAttribute("inert", "");
|
|
260
301
|
tracked.push(child);
|
|
261
302
|
}
|
|
@@ -271,7 +312,6 @@ function createInertSiblings(rootProvider) {
|
|
|
271
312
|
var LifecycleController = class {
|
|
272
313
|
constructor(deps, opts) {
|
|
273
314
|
__publicField(this, "element");
|
|
274
|
-
__publicField(this, "closeFn");
|
|
275
315
|
__publicField(this, "focusTrapEnabled");
|
|
276
316
|
__publicField(this, "initialFocus");
|
|
277
317
|
__publicField(this, "closeOnEscape");
|
|
@@ -280,16 +320,11 @@ var LifecycleController = class {
|
|
|
280
320
|
__publicField(this, "inertSiblings");
|
|
281
321
|
__publicField(this, "shouldApplyInertSiblings");
|
|
282
322
|
__publicField(this, "returnFocus");
|
|
283
|
-
/** Toggle-state teardowns — fire on close, NOT in destroy-only stack. */
|
|
284
323
|
__publicField(this, "releaseFocusTrap", null);
|
|
285
324
|
__publicField(this, "releaseScrollLock", null);
|
|
286
|
-
/** True between install() and release(). Guards release()/destroy() from
|
|
287
|
-
* re-running side effects (notably `returnFocus.focus()`) on already-
|
|
288
|
-
* released or never-installed instances. */
|
|
289
325
|
__publicField(this, "installed", false);
|
|
290
326
|
__publicField(this, "destroyed", false);
|
|
291
327
|
this.element = deps.element;
|
|
292
|
-
this.closeFn = deps.close;
|
|
293
328
|
this.focusTrapEnabled = opts.focusTrap ?? false;
|
|
294
329
|
this.initialFocus = opts.initialFocus;
|
|
295
330
|
this.closeOnEscape = opts.closeOnEscape ?? true;
|
|
@@ -299,14 +334,9 @@ var LifecycleController = class {
|
|
|
299
334
|
this.returnFocus = opts.returnFocus;
|
|
300
335
|
this.inertSiblings = createInertSiblings(() => this.element);
|
|
301
336
|
}
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
* doesn't double-install. Sets `installed=true` BEFORE the first side
|
|
306
|
-
* effect so a partial throw (detached / shadow-DOM target) leaves the
|
|
307
|
-
* caller's `release()` rollback path armed to undo whatever did succeed.
|
|
308
|
-
* No-op when destroyed.
|
|
309
|
-
*/
|
|
337
|
+
get isInstalled() {
|
|
338
|
+
return this.installed;
|
|
339
|
+
}
|
|
310
340
|
install() {
|
|
311
341
|
if (this.destroyed) return;
|
|
312
342
|
this.installed = true;
|
|
@@ -315,30 +345,17 @@ var LifecycleController = class {
|
|
|
315
345
|
}
|
|
316
346
|
if (this.focusTrapEnabled && !this.releaseFocusTrap) {
|
|
317
347
|
this.releaseFocusTrap = installFocusTrap(this.element, {
|
|
318
|
-
initialFocus: this.initialFocus
|
|
319
|
-
onEscape: this.closeOnEscape ? () => void this.closeFn() : void 0
|
|
348
|
+
initialFocus: this.initialFocus
|
|
320
349
|
});
|
|
321
350
|
}
|
|
322
351
|
if (this.inertSiblingsEnabled && this.shouldApplyInertSiblings()) {
|
|
323
352
|
this.inertSiblings.apply();
|
|
324
353
|
}
|
|
325
354
|
}
|
|
326
|
-
/**
|
|
327
|
-
* Update the post-release focus target. Overlay re-binds this when the
|
|
328
|
-
* consumer changes `returnFocus` after construction; the new value applies
|
|
329
|
-
* on the next `release()`. No-op when destroyed.
|
|
330
|
-
*/
|
|
331
355
|
setReturnFocus(target) {
|
|
332
356
|
if (this.destroyed) return;
|
|
333
357
|
this.returnFocus = target;
|
|
334
358
|
}
|
|
335
|
-
/**
|
|
336
|
-
* Release all install-time teardowns. Short-circuits when not installed,
|
|
337
|
-
* so `close() → destroy()` (each routing through here) only restores
|
|
338
|
-
* focus once. The `installed` guard lets `inertSiblings.remove()` and
|
|
339
|
-
* the focus-trap/scroll-lock teardown nullings stay simple — they were
|
|
340
|
-
* already idempotent, but `returnFocus.focus()` was firing on every call.
|
|
341
|
-
*/
|
|
342
359
|
release() {
|
|
343
360
|
if (!this.installed) return;
|
|
344
361
|
this.installed = false;
|
|
@@ -352,18 +369,18 @@ var LifecycleController = class {
|
|
|
352
369
|
target?.focus?.();
|
|
353
370
|
}
|
|
354
371
|
}
|
|
355
|
-
/**
|
|
356
|
-
* Final teardown — releases any still-installed listeners and seals the
|
|
357
|
-
* controller. After destroy(), install() is a no-op and setReturnFocus()
|
|
358
|
-
* is rejected, so post-destroy mutations can't queue up a stale focus
|
|
359
|
-
* restore.
|
|
360
|
-
*/
|
|
361
372
|
destroy() {
|
|
362
373
|
this.release();
|
|
363
374
|
this.destroyed = true;
|
|
364
375
|
}
|
|
365
376
|
};
|
|
366
377
|
|
|
378
|
+
// src/core/animation/animation.ts
|
|
379
|
+
var prefersReducedMotion = () => {
|
|
380
|
+
if (typeof window === "undefined" || !window.matchMedia) return false;
|
|
381
|
+
return window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
382
|
+
};
|
|
383
|
+
|
|
367
384
|
// src/core/overlay.ts
|
|
368
385
|
var OVERLAY_PRESETS = Object.freeze({
|
|
369
386
|
sheet: Object.freeze({
|
|
@@ -429,8 +446,6 @@ var OverlayEngine = class {
|
|
|
429
446
|
__publicField(this, "exitEasing");
|
|
430
447
|
__publicField(this, "backdropOpacity");
|
|
431
448
|
__publicField(this, "backdropFilter");
|
|
432
|
-
// Definite-assignment: written via assignSwipeFromConfig in the
|
|
433
|
-
// constructor — TS can't follow the helper, mark the assertion explicit.
|
|
434
449
|
__publicField(this, "swipeToCloseEnabled");
|
|
435
450
|
__publicField(this, "swipeThreshold");
|
|
436
451
|
__publicField(this, "swipeVelocityThreshold");
|
|
@@ -445,18 +460,16 @@ var OverlayEngine = class {
|
|
|
445
460
|
__publicField(this, "closeOnOutsidePointerEnabled");
|
|
446
461
|
__publicField(this, "detachOutsidePointer", null);
|
|
447
462
|
__publicField(this, "detachSwipe", null);
|
|
463
|
+
__publicField(this, "respectReducedMotionEnabled");
|
|
464
|
+
__publicField(this, "isTop", true);
|
|
465
|
+
__publicField(this, "backPushed", false);
|
|
466
|
+
__publicField(this, "movedToTarget", false);
|
|
448
467
|
__publicField(this, "onOpenCb");
|
|
449
468
|
__publicField(this, "onCloseCb");
|
|
450
469
|
__publicField(this, "isOpen_", false);
|
|
451
470
|
__publicField(this, "isAnimating_", false);
|
|
452
471
|
__publicField(this, "destroyed", false);
|
|
453
|
-
// Tracks whether setOverlayChildren ever populated the panel — lets
|
|
454
|
-
// clearOverlayChildren skip the DOM mutation when nothing was injected
|
|
455
|
-
// (so the field also acts as a zero-allocation feature flag).
|
|
456
472
|
__publicField(this, "hasInjectedChildren", false);
|
|
457
|
-
// Bumped on every open/close. awaitTransition captures it and ignores
|
|
458
|
-
// transitionend/timeout fired by older cycles — prevents cross-cycle
|
|
459
|
-
// listener bleed during rapid toggling.
|
|
460
473
|
__publicField(this, "cycleNonce", 0);
|
|
461
474
|
__publicField(this, "bus", createEventBus());
|
|
462
475
|
__publicField(this, "detachEscape", null);
|
|
@@ -487,6 +500,7 @@ var OverlayEngine = class {
|
|
|
487
500
|
this.mountTarget = opts.mountTo;
|
|
488
501
|
this.peekRaw = opts.peek;
|
|
489
502
|
this.closeOnOutsidePointerEnabled = opts.closeOnOutsidePointer ?? false;
|
|
503
|
+
this.respectReducedMotionEnabled = opts.respectReducedMotion ?? true;
|
|
490
504
|
this.onOpenCb = opts.onOpen;
|
|
491
505
|
this.onCloseCb = opts.onClose;
|
|
492
506
|
if (this.peekRaw !== void 0 && this.edge !== "bottom") {
|
|
@@ -509,9 +523,6 @@ var OverlayEngine = class {
|
|
|
509
523
|
closeOnEscape: this.closeOnEscape,
|
|
510
524
|
lockBodyScroll: this.bodyScrollLockEnabled,
|
|
511
525
|
inertSiblings: this.inertSiblingsEnabled,
|
|
512
|
-
// Overlay-specific guard: only paint body siblings inert when the
|
|
513
|
-
// overlay element is actually a body descendant. Detached / shadow-
|
|
514
|
-
// DOM / portal overlays would otherwise lock unrelated trees.
|
|
515
526
|
shouldApplyInertSiblings: () => {
|
|
516
527
|
const bodyChildren = Array.from(document.body.children);
|
|
517
528
|
return bodyChildren.some(
|
|
@@ -537,15 +548,17 @@ var OverlayEngine = class {
|
|
|
537
548
|
this.emit("before-open", void 0);
|
|
538
549
|
this.isOpen_ = true;
|
|
539
550
|
this.isAnimating_ = true;
|
|
551
|
+
sheetStack.promote(this.id);
|
|
540
552
|
this.element.removeAttribute("hidden");
|
|
541
553
|
this.element.setAttribute("data-state", "entering");
|
|
542
554
|
if (this.focusTrapEnabled) {
|
|
543
555
|
this.element.setAttribute("aria-modal", "true");
|
|
544
556
|
}
|
|
545
|
-
const
|
|
557
|
+
const dur = this.effectiveDuration();
|
|
558
|
+
const transitionStr = `transform ${dur}ms ${this.enterEasing}, opacity ${dur}ms ease-out`;
|
|
546
559
|
this.element.style.transition = transitionStr;
|
|
547
560
|
if (this.backdrop) {
|
|
548
|
-
this.backdrop.style.transition = `opacity ${
|
|
561
|
+
this.backdrop.style.transition = `opacity ${dur}ms ease-out`;
|
|
549
562
|
}
|
|
550
563
|
const cycle = ++this.cycleNonce;
|
|
551
564
|
return new Promise((resolve) => {
|
|
@@ -557,7 +570,7 @@ var OverlayEngine = class {
|
|
|
557
570
|
this.element.style.opacity = "1";
|
|
558
571
|
if (this.backdrop) {
|
|
559
572
|
this.backdrop.style.opacity = String(this.backdropOpacity);
|
|
560
|
-
this.backdrop.style.pointerEvents = "auto";
|
|
573
|
+
this.backdrop.style.pointerEvents = this.backdropOpacity > 0 ? "auto" : "none";
|
|
561
574
|
if (this.backdropFilter) {
|
|
562
575
|
this.backdrop.style.backdropFilter = this.backdropFilter;
|
|
563
576
|
this.backdrop.style.webkitBackdropFilter = this.backdropFilter;
|
|
@@ -593,7 +606,10 @@ var OverlayEngine = class {
|
|
|
593
606
|
});
|
|
594
607
|
return resolve();
|
|
595
608
|
}
|
|
596
|
-
this.awaitTransition(
|
|
609
|
+
this.awaitTransition(
|
|
610
|
+
cycle,
|
|
611
|
+
this.enterAnimation === "fade" ? "opacity" : "transform"
|
|
612
|
+
).then(() => {
|
|
597
613
|
if (this.destroyed || cycle !== this.cycleNonce) return resolve();
|
|
598
614
|
this.isAnimating_ = false;
|
|
599
615
|
this.element.setAttribute("data-state", "open");
|
|
@@ -610,11 +626,14 @@ var OverlayEngine = class {
|
|
|
610
626
|
this.emit("before-close", void 0);
|
|
611
627
|
this.isOpen_ = false;
|
|
612
628
|
this.isAnimating_ = true;
|
|
629
|
+
sheetStack.update();
|
|
630
|
+
this.popBackMarker(reason);
|
|
613
631
|
this.element.setAttribute("data-state", "exiting");
|
|
614
|
-
const
|
|
632
|
+
const dur = this.effectiveDuration();
|
|
633
|
+
const transitionStr = `transform ${dur}ms ${this.exitEasing}, opacity ${dur}ms ease-in`;
|
|
615
634
|
this.element.style.transition = transitionStr;
|
|
616
635
|
if (this.backdrop) {
|
|
617
|
-
this.backdrop.style.transition = `opacity ${
|
|
636
|
+
this.backdrop.style.transition = `opacity ${dur}ms ease-in`;
|
|
618
637
|
}
|
|
619
638
|
const cycle = ++this.cycleNonce;
|
|
620
639
|
return new Promise((resolve) => {
|
|
@@ -625,7 +644,7 @@ var OverlayEngine = class {
|
|
|
625
644
|
this.exitAnimation,
|
|
626
645
|
this.peekPx
|
|
627
646
|
);
|
|
628
|
-
this.element.style.opacity = "0";
|
|
647
|
+
this.element.style.opacity = this.peekPx !== void 0 ? "1" : "0";
|
|
629
648
|
if (this.backdrop) {
|
|
630
649
|
this.backdrop.style.opacity = "0";
|
|
631
650
|
this.backdrop.style.pointerEvents = "none";
|
|
@@ -635,7 +654,10 @@ var OverlayEngine = class {
|
|
|
635
654
|
}
|
|
636
655
|
}
|
|
637
656
|
this.releaseInteractiveListeners();
|
|
638
|
-
this.awaitTransition(
|
|
657
|
+
this.awaitTransition(
|
|
658
|
+
cycle,
|
|
659
|
+
this.exitAnimation === "fade" ? "opacity" : "transform"
|
|
660
|
+
).then(() => {
|
|
639
661
|
if (this.destroyed || cycle !== this.cycleNonce) return resolve();
|
|
640
662
|
this.isAnimating_ = false;
|
|
641
663
|
if (this.peekPx === void 0) {
|
|
@@ -663,14 +685,12 @@ var OverlayEngine = class {
|
|
|
663
685
|
this.releaseInteractiveListeners();
|
|
664
686
|
this.releaseStackEntry?.();
|
|
665
687
|
this.releaseStackEntry = null;
|
|
688
|
+
const hadOriginalParent = this.originalParent !== null;
|
|
666
689
|
this.restoreMount();
|
|
690
|
+
if (this.movedToTarget && !hadOriginalParent && this.element.parentNode) {
|
|
691
|
+
this.element.parentNode.removeChild(this.element);
|
|
692
|
+
}
|
|
667
693
|
}
|
|
668
|
-
/**
|
|
669
|
-
* Runtime visual setter — opacity is applied immediately when open so a
|
|
670
|
-
* live-controls panel can preview the change without re-opening. When
|
|
671
|
-
* closed we only stash the field; next open() picks it up so we don't
|
|
672
|
-
* mutate hidden DOM (avoids paint thrash on detached panels).
|
|
673
|
-
*/
|
|
674
694
|
setBackdropOpacity(opacity) {
|
|
675
695
|
if (this.destroyed) return;
|
|
676
696
|
this.backdropOpacity = opacity;
|
|
@@ -678,11 +698,6 @@ var OverlayEngine = class {
|
|
|
678
698
|
this.backdrop.style.opacity = String(opacity);
|
|
679
699
|
}
|
|
680
700
|
}
|
|
681
|
-
/**
|
|
682
|
-
* Runtime visual setter for `backdrop-filter`. `undefined` clears the
|
|
683
|
-
* filter (mirrors the close() behaviour). Defers to next open() when the
|
|
684
|
-
* overlay is currently closed.
|
|
685
|
-
*/
|
|
686
701
|
setBackdropFilter(filter) {
|
|
687
702
|
if (this.destroyed) return;
|
|
688
703
|
this.backdropFilter = filter;
|
|
@@ -696,12 +711,6 @@ var OverlayEngine = class {
|
|
|
696
711
|
style.webkitBackdropFilter = "";
|
|
697
712
|
}
|
|
698
713
|
}
|
|
699
|
-
/**
|
|
700
|
-
* Toggle / reconfigure swipe-to-close at runtime. Re-resolves the flat
|
|
701
|
-
* threshold fields, then installs or tears down the pointer listener so
|
|
702
|
-
* the change applies to the CURRENT open cycle. Idempotent: safe to call
|
|
703
|
-
* with the same config repeatedly.
|
|
704
|
-
*/
|
|
705
714
|
setSwipeToClose(config) {
|
|
706
715
|
if (this.destroyed) return;
|
|
707
716
|
const wasEnabled = this.swipeToCloseEnabled;
|
|
@@ -714,7 +723,6 @@ var OverlayEngine = class {
|
|
|
714
723
|
this.detachSwipe = null;
|
|
715
724
|
}
|
|
716
725
|
}
|
|
717
|
-
/** Animation setters only stage the field — applied on next open/close. */
|
|
718
726
|
setEnterAnimation(animation) {
|
|
719
727
|
if (this.destroyed) return;
|
|
720
728
|
this.enterAnimation = animation;
|
|
@@ -723,23 +731,15 @@ var OverlayEngine = class {
|
|
|
723
731
|
if (this.destroyed) return;
|
|
724
732
|
this.exitAnimation = animation;
|
|
725
733
|
}
|
|
726
|
-
/** Updates the field used at next close() — no immediate side effect. */
|
|
727
734
|
setReturnFocus(target) {
|
|
728
735
|
if (this.destroyed) return;
|
|
729
736
|
this.lifecycle.setReturnFocus(target);
|
|
730
737
|
}
|
|
731
|
-
/**
|
|
732
|
-
* Batch update — preset first, then individual overrides. Applied as a
|
|
733
|
-
* single DOM-write phase when open so live-controls don't trigger
|
|
734
|
-
* cascading reflows. Safe to call when closed (defers to next open).
|
|
735
|
-
*/
|
|
736
738
|
setOverlay(opts) {
|
|
737
739
|
if (this.destroyed) return;
|
|
738
740
|
const preset = opts.preset ? OVERLAY_PRESETS[opts.preset] : void 0;
|
|
739
741
|
const merged = preset ? {
|
|
740
742
|
backdropOpacity: preset.backdropOpacity,
|
|
741
|
-
// Preset's `undefined` filter means "no filter" — surface as null
|
|
742
|
-
// so the merged step clears the field instead of inheriting old.
|
|
743
743
|
backdropFilter: preset.backdropFilter === void 0 ? null : preset.backdropFilter,
|
|
744
744
|
swipeToClose: preset.swipeToClose,
|
|
745
745
|
enterAnimation: preset.enterAnimation,
|
|
@@ -790,6 +790,36 @@ var OverlayEngine = class {
|
|
|
790
790
|
}
|
|
791
791
|
}
|
|
792
792
|
}
|
|
793
|
+
if (opts.duration !== void 0) this.duration = opts.duration;
|
|
794
|
+
if (opts.enterEasing !== void 0) this.enterEasing = opts.enterEasing;
|
|
795
|
+
if (opts.exitEasing !== void 0) this.exitEasing = opts.exitEasing;
|
|
796
|
+
if (opts.closeOnEscape !== void 0) {
|
|
797
|
+
this.closeOnEscape = opts.closeOnEscape;
|
|
798
|
+
if (!opts.closeOnEscape) {
|
|
799
|
+
this.detachEscape?.();
|
|
800
|
+
this.detachEscape = null;
|
|
801
|
+
} else if (this.isOpen_) {
|
|
802
|
+
this.installInteractiveListeners();
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
if (opts.closeOnBackdrop !== void 0) {
|
|
806
|
+
this.closeOnBackdropEnabled = opts.closeOnBackdrop;
|
|
807
|
+
if (!opts.closeOnBackdrop) {
|
|
808
|
+
this.detachBackdrop?.();
|
|
809
|
+
this.detachBackdrop = null;
|
|
810
|
+
} else if (this.isOpen_) {
|
|
811
|
+
this.installInteractiveListeners();
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
if (opts.closeOnOutsidePointer !== void 0) {
|
|
815
|
+
this.closeOnOutsidePointerEnabled = opts.closeOnOutsidePointer;
|
|
816
|
+
if (!opts.closeOnOutsidePointer) {
|
|
817
|
+
this.detachOutsidePointer?.();
|
|
818
|
+
this.detachOutsidePointer = null;
|
|
819
|
+
} else if (this.isOpen_) {
|
|
820
|
+
this.installInteractiveListeners();
|
|
821
|
+
}
|
|
822
|
+
}
|
|
793
823
|
if (opts.children !== void 0) {
|
|
794
824
|
if (opts.children === null) {
|
|
795
825
|
this.clearOverlayChildren();
|
|
@@ -798,30 +828,17 @@ var OverlayEngine = class {
|
|
|
798
828
|
}
|
|
799
829
|
}
|
|
800
830
|
}
|
|
801
|
-
/**
|
|
802
|
-
* Runtime children injection — replace the panel's contents without
|
|
803
|
-
* unmounting the engine. `replaceChildren()` is a single atomic DOM op,
|
|
804
|
-
* so callers can swap content rapidly without intermediate flicker.
|
|
805
|
-
* Function form is resolved exactly ONCE per call (cheap lazy content).
|
|
806
|
-
*/
|
|
807
831
|
setOverlayChildren(children) {
|
|
808
832
|
if (this.destroyed) return;
|
|
809
833
|
const node = typeof children === "function" ? children() : children;
|
|
810
834
|
this.element.replaceChildren(node);
|
|
811
835
|
this.hasInjectedChildren = true;
|
|
812
836
|
}
|
|
813
|
-
/**
|
|
814
|
-
* Clear injected children. No-op when nothing was injected so consumers
|
|
815
|
-
* who never used setOverlayChildren can call this defensively without
|
|
816
|
-
* paying for a DOM mutation.
|
|
817
|
-
*/
|
|
818
837
|
clearOverlayChildren() {
|
|
819
838
|
if (this.destroyed || !this.hasInjectedChildren) return;
|
|
820
839
|
this.element.replaceChildren();
|
|
821
840
|
this.hasInjectedChildren = false;
|
|
822
841
|
}
|
|
823
|
-
// Shared swipe-config normalizer — used by constructor, setSwipeToClose,
|
|
824
|
-
// and setOverlay so the union/object shape is decoded in exactly one place.
|
|
825
842
|
assignSwipeFromConfig(swipe) {
|
|
826
843
|
if (typeof swipe === "object" && swipe !== null) {
|
|
827
844
|
this.swipeToCloseEnabled = swipe.enabled ?? true;
|
|
@@ -836,6 +853,22 @@ var OverlayEngine = class {
|
|
|
836
853
|
emit(event, payload) {
|
|
837
854
|
this.bus.emit(event, payload);
|
|
838
855
|
}
|
|
856
|
+
effectiveDuration() {
|
|
857
|
+
return this.respectReducedMotionEnabled && prefersReducedMotion() ? 0 : this.duration;
|
|
858
|
+
}
|
|
859
|
+
popBackMarker(reason) {
|
|
860
|
+
if (!this.backPushed) return;
|
|
861
|
+
this.backPushed = false;
|
|
862
|
+
if (reason === "back") return;
|
|
863
|
+
const state = history.state;
|
|
864
|
+
if (state && state.__bsOverlay === this.id) {
|
|
865
|
+
try {
|
|
866
|
+
history.back();
|
|
867
|
+
} catch {
|
|
868
|
+
return;
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
}
|
|
839
872
|
resolvePeekPx(raw) {
|
|
840
873
|
if (typeof raw === "number") return Math.max(0, raw);
|
|
841
874
|
try {
|
|
@@ -857,6 +890,7 @@ var OverlayEngine = class {
|
|
|
857
890
|
this.originalParent = this.element.parentNode;
|
|
858
891
|
this.originalNextSibling = this.element.nextSibling;
|
|
859
892
|
target.appendChild(this.element);
|
|
893
|
+
this.movedToTarget = true;
|
|
860
894
|
this.mounted = true;
|
|
861
895
|
}
|
|
862
896
|
restoreMount() {
|
|
@@ -890,7 +924,7 @@ var OverlayEngine = class {
|
|
|
890
924
|
this.backdrop.style.willChange = "opacity";
|
|
891
925
|
}
|
|
892
926
|
}
|
|
893
|
-
awaitTransition(cycle) {
|
|
927
|
+
awaitTransition(cycle, property = "transform") {
|
|
894
928
|
return new Promise((resolve) => {
|
|
895
929
|
let done = false;
|
|
896
930
|
const finish = () => {
|
|
@@ -905,10 +939,10 @@ var OverlayEngine = class {
|
|
|
905
939
|
finish();
|
|
906
940
|
return;
|
|
907
941
|
}
|
|
908
|
-
if (e.propertyName ===
|
|
942
|
+
if (e.target === this.element && e.propertyName === property) finish();
|
|
909
943
|
};
|
|
910
944
|
this.element.addEventListener("transitionend", onEnd);
|
|
911
|
-
const timer = window.setTimeout(finish, this.
|
|
945
|
+
const timer = window.setTimeout(finish, this.effectiveDuration() + 50);
|
|
912
946
|
});
|
|
913
947
|
}
|
|
914
948
|
installInteractiveListeners() {
|
|
@@ -918,7 +952,10 @@ var OverlayEngine = class {
|
|
|
918
952
|
}
|
|
919
953
|
if (this.closeOnEscape && !this.detachEscape) {
|
|
920
954
|
const handler = (e) => {
|
|
921
|
-
if (e.
|
|
955
|
+
if (e.defaultPrevented) return;
|
|
956
|
+
if (e.key === "Escape" && this.isOpen_ && this.isTop) {
|
|
957
|
+
void this.close("escape");
|
|
958
|
+
}
|
|
922
959
|
};
|
|
923
960
|
document.addEventListener("keydown", handler);
|
|
924
961
|
this.detachEscape = () => document.removeEventListener("keydown", handler);
|
|
@@ -931,10 +968,14 @@ var OverlayEngine = class {
|
|
|
931
968
|
if (this.closeOnBackEnabled && typeof window !== "undefined" && !this.detachHardwareBack) {
|
|
932
969
|
try {
|
|
933
970
|
history.pushState({ __bsOverlay: this.id }, "");
|
|
971
|
+
this.backPushed = true;
|
|
934
972
|
} catch {
|
|
935
973
|
}
|
|
936
974
|
const onPop = () => {
|
|
937
|
-
if (this.isOpen_
|
|
975
|
+
if (this.isOpen_ && this.isTop) {
|
|
976
|
+
this.backPushed = false;
|
|
977
|
+
void this.close("back");
|
|
978
|
+
}
|
|
938
979
|
};
|
|
939
980
|
window.addEventListener("popstate", onPop);
|
|
940
981
|
this.detachHardwareBack = () => window.removeEventListener("popstate", onPop);
|
|
@@ -965,55 +1006,57 @@ var OverlayEngine = class {
|
|
|
965
1006
|
this.detachOutsidePointer = null;
|
|
966
1007
|
this.lifecycle.release();
|
|
967
1008
|
}
|
|
968
|
-
/**
|
|
969
|
-
* Light pointer-driven dismiss. Tracks finger along the edge axis, follows
|
|
970
|
-
* with translate, dismisses if past `swipeThreshold` (fraction of panel
|
|
971
|
-
* size) or release velocity exceeds `swipeVelocityThreshold` (px/ms).
|
|
972
|
-
* Pure CSS-transform during drag — no rAF or physics required since the
|
|
973
|
-
* panel only moves one direction.
|
|
974
|
-
*/
|
|
975
1009
|
installSwipeToClose() {
|
|
976
1010
|
const el = this.element;
|
|
977
1011
|
const edge = this.edge;
|
|
978
1012
|
const isVertical = edge === "bottom" || edge === "top";
|
|
979
1013
|
const sign = edge === "bottom" || edge === "right" ? 1 : -1;
|
|
980
|
-
const
|
|
981
|
-
const velocityThreshold = this.swipeVelocityThreshold;
|
|
1014
|
+
const SLOP = 8;
|
|
982
1015
|
let startX = 0;
|
|
983
1016
|
let startY = 0;
|
|
984
1017
|
let startT = 0;
|
|
1018
|
+
let tracking = false;
|
|
985
1019
|
let dragging = false;
|
|
986
1020
|
let pointerId = null;
|
|
1021
|
+
const previousTouchAction = el.style.touchAction;
|
|
1022
|
+
el.style.touchAction = isVertical ? "pan-x" : "pan-y";
|
|
987
1023
|
const onDown = (e) => {
|
|
988
1024
|
if (e.button !== void 0 && e.button !== 0) return;
|
|
989
1025
|
pointerId = e.pointerId;
|
|
990
1026
|
startX = e.clientX;
|
|
991
1027
|
startY = e.clientY;
|
|
992
1028
|
startT = performance.now();
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
try {
|
|
996
|
-
el.setPointerCapture(e.pointerId);
|
|
997
|
-
} catch {
|
|
998
|
-
}
|
|
1029
|
+
tracking = true;
|
|
1030
|
+
dragging = false;
|
|
999
1031
|
};
|
|
1000
1032
|
const onMove = (e) => {
|
|
1001
|
-
if (!
|
|
1033
|
+
if (!tracking || e.pointerId !== pointerId) return;
|
|
1002
1034
|
const delta = isVertical ? e.clientY - startY : e.clientX - startX;
|
|
1035
|
+
if (!dragging) {
|
|
1036
|
+
if (Math.abs(delta) < SLOP) return;
|
|
1037
|
+
dragging = true;
|
|
1038
|
+
el.style.transition = "none";
|
|
1039
|
+
try {
|
|
1040
|
+
el.setPointerCapture(e.pointerId);
|
|
1041
|
+
} catch {
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1003
1044
|
const offset = Math.max(0, sign * delta);
|
|
1004
1045
|
el.style.transform = isVertical ? `translate3d(0, ${sign * offset}px, 0)` : `translate3d(${sign * offset}px, 0, 0)`;
|
|
1005
1046
|
};
|
|
1006
1047
|
const finish = (e) => {
|
|
1007
|
-
if (!
|
|
1008
|
-
|
|
1048
|
+
if (!tracking || e.pointerId !== pointerId) return;
|
|
1049
|
+
tracking = false;
|
|
1009
1050
|
pointerId = null;
|
|
1051
|
+
if (!dragging) return;
|
|
1052
|
+
dragging = false;
|
|
1010
1053
|
const dt = performance.now() - startT || 1;
|
|
1011
1054
|
const delta = isVertical ? e.clientY - startY : e.clientX - startX;
|
|
1012
1055
|
const offset = Math.max(0, sign * delta);
|
|
1013
1056
|
const velocity = sign * delta / dt;
|
|
1014
1057
|
const size = isVertical ? el.offsetHeight : el.offsetWidth;
|
|
1015
|
-
const past = offset > size *
|
|
1016
|
-
el.style.transition = `transform ${this.
|
|
1058
|
+
const past = offset > size * this.swipeThreshold || velocity > this.swipeVelocityThreshold;
|
|
1059
|
+
el.style.transition = `transform ${this.effectiveDuration()}ms ${this.exitEasing}`;
|
|
1017
1060
|
if (past) {
|
|
1018
1061
|
void this.close("swipe");
|
|
1019
1062
|
} else {
|
|
@@ -1025,6 +1068,7 @@ var OverlayEngine = class {
|
|
|
1025
1068
|
el.addEventListener("pointerup", finish);
|
|
1026
1069
|
el.addEventListener("pointercancel", finish);
|
|
1027
1070
|
return () => {
|
|
1071
|
+
el.style.touchAction = previousTouchAction;
|
|
1028
1072
|
el.removeEventListener("pointerdown", onDown);
|
|
1029
1073
|
el.removeEventListener("pointermove", onMove);
|
|
1030
1074
|
el.removeEventListener("pointerup", finish);
|
|
@@ -1039,10 +1083,12 @@ var OverlayEngine = class {
|
|
|
1039
1083
|
if (this.backdrop) this.backdrop.style.zIndex = String(z - 1);
|
|
1040
1084
|
},
|
|
1041
1085
|
setIsTop: (isTop) => {
|
|
1086
|
+
this.isTop = isTop;
|
|
1042
1087
|
if (this.backdrop) {
|
|
1043
1088
|
this.backdrop.style.display = isTop ? "" : "none";
|
|
1044
1089
|
}
|
|
1045
|
-
}
|
|
1090
|
+
},
|
|
1091
|
+
isOpen: () => this.isOpen_
|
|
1046
1092
|
});
|
|
1047
1093
|
}
|
|
1048
1094
|
};
|
package/dist/preact.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { BottomSheet, BottomSheetFromConfig, BottomSheetFromConfigProps, BottomSheetHandle, BottomSheetProps, HeaderProp, Overlay, OverlayHandle, OverlayProps, SheetConfig, UseBottomSheetRefs, UseBottomSheetReturn, UseOverlayReturn, useBottomSheet, useOverlay } from './react.cjs';
|
|
2
|
-
export { E as EngineState,
|
|
2
|
+
export { E as EngineState, b as SheetMode, d as SnapPoint, a as SnapPointDef } from './BottomSheetEngine-CAymrH-P.cjs';
|
|
3
3
|
export { OverlayEdge, OverlayOptions, OverlayState } from './overlay.cjs';
|
|
4
4
|
import 'react';
|
|
5
|
-
import './transform-
|
|
5
|
+
import './transform-jVfAikE9.cjs';
|
package/dist/preact.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { BottomSheet, BottomSheetFromConfig, BottomSheetFromConfigProps, BottomSheetHandle, BottomSheetProps, HeaderProp, Overlay, OverlayHandle, OverlayProps, SheetConfig, UseBottomSheetRefs, UseBottomSheetReturn, UseOverlayReturn, useBottomSheet, useOverlay } from './react.js';
|
|
2
|
-
export { E as EngineState,
|
|
2
|
+
export { E as EngineState, b as SheetMode, d as SnapPoint, a as SnapPointDef } from './BottomSheetEngine-DcqcuudN.js';
|
|
3
3
|
export { OverlayEdge, OverlayOptions, OverlayState } from './overlay.js';
|
|
4
4
|
import 'react';
|
|
5
|
-
import './transform-
|
|
5
|
+
import './transform-jVfAikE9.js';
|
package/dist/preact.js
CHANGED