UNPKG

@douxcode/vue-spring-bottom-sheet

Version:
422 lines (421 loc) 15.1 kB
import { ref as C, computed as _, defineComponent as Be, watch as q, onMounted as Ie, toRefs as Ee, nextTick as N, onUnmounted as _e, createBlock as te, openBlock as ae, Teleport as De, createElementVNode as ne, createVNode as $, unref as m, withCtx as E, createCommentVNode as ge, normalizeStyle as He, normalizeClass as oe, renderSlot as le } from "vue"; import { useMotionValue as me, animate as B, AnimatePresence as ye, Motion as A } from "motion-v"; import { useVModel as Ve, useWindowSize as Fe, useElementBounding as Q, useScrollLock as be } from "@vueuse/core"; import { useFocusTrap as Re } from "@vueuse/integrations/useFocusTrap"; function G(n, a) { const o = parseFloat(n); return a * o / 100; } function $e(n, a, o) { const t = C(0), s = _(() => n.value.map((f) => typeof f == "string" ? G(f, o.value) : f)), b = _(() => Math.min(...s.value)), r = _(() => Math.max(...s.value)), p = _(() => { const f = s.value.reduce( (h, O) => Math.abs(O - a.value) < Math.abs(h - a.value) ? O : h ); return s.value.indexOf(f); }); return { currentSnapPointIndex: t, flattenedSnapPoints: s, minSnapPoint: b, maxSnapPoint: r, closestSnapPointIndex: p }; } function Ae(n, a, o) { let t = (s) => n(s, ...a); return o === void 0 ? t : Object.assign(t, { lazy: o, lazyArgs: a }); } function We(n, a, o) { let t = n.length - a.length; if (t === 0) return n(...a); if (t === 1) return Ae(n, a, o); throw new Error("Wrong number of arguments"); } function je(n, { triggerAt: a = "end", minQuietPeriodMs: o, maxBurstDurationMs: t, minGapMs: s, reducer: b = ze }) { let r, p, f, h, O = () => { let i = f; i !== void 0 && (f = void 0, n(i), s !== void 0 && (p = setTimeout(D, s))); }, D = () => { clearTimeout(p), p = void 0, r === void 0 && O(); }, H = () => { clearTimeout(r), r = void 0, h = void 0, p === void 0 && O(); }; return { call: (...i) => { let c = r === void 0 && p === void 0; if ((a !== "start" || c) && (f = b(f, ...i)), !(r === void 0 && !c)) { if (o !== void 0 || t !== void 0 || s === void 0) { clearTimeout(r); let P = Date.now(); h ?? (h = P); let W = t === void 0 ? o ?? 0 : Math.min(o ?? t, t - (P - h)); r = setTimeout(H, W); } a !== "end" && c && O(); } }, cancel: () => { clearTimeout(r), r = void 0, h = void 0, clearTimeout(p), p = void 0, f = void 0; }, flush: () => { H(), D(); }, get isIdle() { return r === void 0 && p === void 0; } }; } var ze = () => ""; function y(...n) { return We(Le, n); } var Le = (n, { min: a, max: o }) => a !== void 0 && n < a ? a : o !== void 0 && n > o ? o : n; function qe(n, a, o) { return Math.max(a, Math.min(n, o)); } function Ne(n, a) { return Math.pow(n, a * 5); } function Se(n, a, o) { return a === 0 || Math.abs(a) === 1 / 0 ? Ne(n, o) : n * a * o / (a + o * n); } function ue(n, a, o, t = 0.15) { return t === 0 ? qe(n, a, o) : n < a ? -Se(a - n, o - a, t) + a : n > o ? +Se(n - o, o - a, t) + o : n; } const Qe = { "data-vsbs-container": "" }, Ge = /* @__PURE__ */ Be({ __name: "BottomSheet", props: { duration: { default: 250 }, snapPoints: {}, initialSnapPoint: {}, blocking: { type: Boolean, default: !0 }, canSwipeClose: { type: Boolean, default: !0 }, swipeCloseThreshold: {}, canBackdropClose: { type: Boolean, default: !0 }, expandOnContentDrag: { type: Boolean, default: !0 }, modelValue: { type: Boolean }, teleportTo: { default: "body" }, teleportDefer: { type: Boolean, default: !1 }, headerClass: {}, contentClass: {}, footerClass: {} }, emits: ["opened", "opening-started", "closed", "closing-started", "ready", "dragging-up", "dragging-down", "snapped", "instinctHeight", "update:modelValue"], setup(n, { expose: a, emit: o }) { const t = n, s = o, b = Ve(t, "modelValue", s, { passive: !0 }); q(b, (e) => { e && X(); }), Ie(() => { b.value && X(); }); const r = C(), p = C(null), f = C(null), h = C(null), O = C(null), D = C(null), H = C(null), i = C(t.expandOnContentDrag), { height: c } = Fe(), { height: P } = Q(r), { height: W } = Q(p), { height: se } = Q(D), { height: re } = Q(f), K = _({ get() { return y( Math.ceil(se.value + W.value + re.value), { max: c.value } ); }, set(e) { [W.value, se.value, re.value] = e; } }), l = C(0), v = C(0), w = me(0), S = me(0), { snapPoints: Ce } = Ee(t), d = _(() => Ce.value ?? [K.value]), { flattenedSnapPoints: U, currentSnapPointIndex: I, closestSnapPointIndex: V, minSnapPoint: M, maxSnapPoint: F } = $e(d, l, c); let T; const j = be(document.body), z = be(document.documentElement), Y = Re([r, H], { immediate: !1, fallbackFocus: () => { var e; return ((e = r.value) == null ? void 0 : e.$el) || document.body; } }); function ie(e) { i.value = !0, ve(e); } function ve(e) { i.value && e.preventDefault(); } const de = (e) => { e.key === "Escape" && L(); }, Pe = () => { t.canBackdropClose && L(); }; let J = !1; const X = async () => { if (J) return; b.value = !0, J = !0, s("opening-started"), t.blocking && (j.value = !0, z.value = !0), await N(); const e = r.value.$el; P.value = e.getBoundingClientRect().height; const u = e.querySelector("[data-vsbs-content]"), g = e.querySelector("[data-vsbs-header]"), k = e.querySelector("[data-vsbs-footer]"); if (K.value = [ g.getBoundingClientRect().height, u.getBoundingClientRect().height, k.getBoundingClientRect().height ], await N(), I.value = U.value.findIndex( (x) => x === M.value ), t.initialSnapPoint) { const x = t.initialSnapPoint; if (x < 0 || x >= d.value.length) { console.warn("Index out of bounds"); return; } let R; typeof d.value[x] == "number" ? R = y(d.value[x], { max: c.value }) : R = G(d.value[x], c.value), l.value = R; } else l.value = y(M.value, { max: c.value }); v.value = l.value, w.jump(l.value), S.jump(l.value), requestAnimationFrame(() => { T = B(w, l.value, { duration: t.duration / 1e3, ease: "easeInOut" }), T = B(S, 0, { duration: t.duration / 1e3, ease: "easeInOut", onComplete: () => { t.blocking && (s("opened"), Y.activate()); } }); }), window.addEventListener("keydown", de), J = !1; }; let Z = !1; const L = () => { Z || (b.value = !1, Z = !0, s("closing-started"), t.blocking && (j.value = !1, z.value = !1), window.removeEventListener("keydown", de), t.blocking && Y.deactivate(), setTimeout(() => { s("closed"), Z = !1; }, t.duration)); }, ce = (e) => { if (!d.value) return; if (e < 0 || e >= d.value.length) { console.warn("Index out of bounds"); return; } I.value = e; let u; typeof d.value[e] == "number" ? u = y(d.value[e], { max: c.value }) : u = G(d.value[e], c.value), l.value = u, T = B(w, l.value, { duration: t.duration / 1e3, ease: "easeInOut", onComplete: () => s("snapped", d.value.indexOf(d.value[e])) }); }; function fe(e) { e > 0 ? s("dragging-down") : e < 0 && s("dragging-up"); } const pe = () => { l.value = P.value, v.value = S.get(), w.jump(l.value), S.jump(v.value); }, he = async (e, u) => { await N(), r.value && (v.value <= 0 && (l.value -= u.delta.y), l.value <= M.value && (l.value = M.value, v.value += u.delta.y, S.set( t.canSwipeClose ? y(v.value, { min: 0 }) : y(ue(v.value, -P.value, 0, 0.5), { min: 0 }) )), w.set( y(ue(l.value, 0, F.value, 0.25), { min: 0, max: c.value }) ), fe(u.delta.y)); }, ee = () => { if (t.canSwipeClose) { let u = l.value / 2; t.swipeCloseThreshold && typeof t.swipeCloseThreshold == "number" && (u = t.swipeCloseThreshold), t.swipeCloseThreshold && typeof t.swipeCloseThreshold == "string" && t.swipeCloseThreshold.includes("%") && (u = l.value * (Number(t.swipeCloseThreshold.replace("%", "")) / 100)), v.value > u && (v.value = l.value); } else v.value = 0; T = B(S, v.value, { duration: t.duration / 1e3, ease: "easeInOut" }), v.value === l.value && (v.value = 0, L()), I.value = V.value; let e; typeof d.value[V.value] == "number" ? e = y(d.value[V.value], { max: c.value }) : e = G( d.value[V.value], c.value ), l.value = e, T = B(w, l.value, { duration: t.duration / 1e3, ease: "easeInOut", onComplete: () => s( "snapped", d.value.indexOf(d.value[V.value]) ) }), T = B(S, 0, { duration: t.duration / 1e3, ease: "easeInOut" }); }, we = (e, u) => { if (l.value = P.value, v.value = S.get(), T && T.stop(), !h.value) return; const g = h.value.scrollTop === 0, k = u.delta.y > 0, x = U.value.length === 1, R = 0.5 > Math.abs(l.value - F.value); if (x) { if (!t.expandOnContentDrag) { i.value = !1; return; } S.get() === 0 && g && k && (i.value = !0), S.get() === 0 && g && !k && (i.value = !1); } else { if (!t.expandOnContentDrag) { i.value = !1; return; } i.value = !0, R && (k && g && (i.value = !0), !k && g && (i.value = !1), g || (i.value = !1)); } }, Te = async (e, u) => { if (await N(), !t.expandOnContentDrag) { i.value = !1; return; } if (!r.value) return; v.value === 0 && i.value && t.expandOnContentDrag && (l.value -= u.delta.y), l.value <= M.value && (l.value = M.value, i.value && t.expandOnContentDrag && (v.value += u.delta.y), v.value = y(v.value, { min: 0, max: M.value }), S.set( t.canSwipeClose ? y(v.value, { min: 0 }) : y(ue(v.value, -P.value, 0, 0.5), { min: 0 }) )), l.value > F.value && (l.value = F.value), l.value = y(l.value, { max: c.value }), U.value.length === 1 || l.value === F.value && (i.value = !1), w.set(l.value), fe(u.delta.y); }, ke = () => { t.blocking || (j.value = !0, z.value = !0); }, xe = () => { t.blocking || (j.value = !1, z.value = !1); }, Oe = () => { if (!h.value) return; const e = h.value.scrollTop === 0; i.value = e; }, Me = je((e) => ce(e), { minQuietPeriodMs: t.duration, reducer: (e, u) => u }); return q(d, (e, u) => { if (b.value === !1 || !e || !u) return; const g = e[I.value], k = u[I.value]; typeof g != "string" && typeof k != "string" && (l.value = y(g, { max: c.value }), g !== k && (T = B(w, l.value, { duration: t.duration / 1e3, ease: "easeInOut" }))); }), q(c, () => { Me.call(I.value); }), q(K, (e) => { s("instinctHeight", e); }), _e(() => { Y.deactivate(); }), a({ open: X, close: L, snapToPoint: ce }), (e, u) => (ae(), te(De, { to: e.teleportTo, defer: e.teleportDefer }, [ ne("div", Qe, [ $(m(ye), null, { default: E(() => [ m(b) && e.blocking ? (ae(), te(m(A), { key: 0, ref_key: "backdrop", ref: H, "data-vsbs-backdrop": "", onClick: u[0] || (u[0] = (g) => Pe()), transition: { ease: "easeInOut", duration: e.duration / 1e3 }, initial: { opacity: 0 }, animate: { opacity: 1 }, exit: { opacity: 0 } }, null, 8, ["transition"])) : ge("", !0) ]), _: 1 }), $(m(ye), null, { default: E(() => [ m(b) ? (ae(), te(m(A), { key: 0, ref_key: "sheet", ref: r, exit: { y: "100%", height: m(P) }, initial: !1, style: He({ y: m(S), height: m(w) }), "data-vsbs-shadow": !e.blocking, "data-vsbs-sheet-show": m(b), "aria-modal": "true", "data-vsbs-sheet": "", tabindex: "-1", onTouchstart: ke, onTouchend: xe }, { default: E(() => [ $(m(A), { ref_key: "sheetHeader", ref: p, "data-vsbs-header": "", onPanStart: pe, onPan: he, onPanEnd: ee, onTouchmove: ie, class: oe(e.headerClass) }, { default: E(() => [ le(e.$slots, "header", {}, void 0, !0) ]), _: 3 }, 8, ["class"]), ne("div", { ref_key: "sheetScroll", ref: h, "data-vsbs-scroll": "", onScrollend: Oe }, [ $(m(A), { ref_key: "sheetContentWrapper", ref: O, "data-vsbs-content-wrapper": "", onPanStart: we, onPan: Te, onPanEnd: ee, onTouchmove: ve }, { default: E(() => [ ne("div", { ref_key: "sheetContent", ref: D, "data-vsbs-content": "", class: oe(e.contentClass) }, [ le(e.$slots, "default", {}, void 0, !0) ], 2) ]), _: 3 }, 512) ], 544), $(m(A), { ref_key: "sheetFooter", ref: f, "data-vsbs-footer": "", onPanStart: pe, onPan: he, onPanEnd: ee, onTouchmove: ie, class: oe(e.footerClass) }, { default: E(() => [ le(e.$slots, "footer", {}, void 0, !0) ]), _: 3 }, 8, ["class"]) ]), _: 3 }, 8, ["exit", "style", "data-vsbs-shadow", "data-vsbs-sheet-show"])) : ge("", !0) ]), _: 3 }) ]) ], 8, ["to", "defer"])); } }), Ke = (n, a) => { const o = n.__vccOpts || n; for (const [t, s] of a) o[t] = s; return o; }, Ze = /* @__PURE__ */ Ke(Ge, [["__scopeId", "data-v-ccadc172"]]); export { Ze as default };