UNPKG

@valdiszz53/vue-spring-bottom-sheet

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