UNPKG

@blossom-carousel/vue

Version:

A native-scroll-first carousel component for Vue.

487 lines (486 loc) 13.8 kB
import { Comment as e, Fragment as t, createBlock as n, createElementBlock as r, createElementVNode as i, createTextVNode as a, defineComponent as o, getCurrentInstance as s, inject as c, isVNode as l, mergeProps as u, onBeforeUnmount as d, onMounted as f, openBlock as p, provide as m, ref as h, renderList as g, renderSlot as _, resolveDynamicComponent as v, shallowRef as y, toRef as b, unref as x, useAttrs as ee, useSlots as S, watch as te, watchEffect as ne, withCtx as C } from "vue"; //#region ../navigation/src/snap.ts function w(e) { return getComputedStyle(e).direction === "rtl"; } function T(e, t) { return t ? -e.scrollLeft : e.scrollLeft; } function E(e) { if (!e || e === "none") return "none"; let t = e.trim().split(/\s+/), n = t.length === 2 ? t[1] : t[0]; return n === "center" || n === "end" ? n : "start"; } function D(e) { let t = E(e); return t === "none" ? "start" : t; } function O(e, t, n, r, i, a, o, s = getComputedStyle(t)) { let c = t.getBoundingClientRect(), l = (o ? r.right - c.right : c.left - r.left) + T(e, o), u = parseFloat(s.scrollMarginInlineStart) || 0, d = parseFloat(s.scrollMarginInlineEnd) || 0, f = l - u, p = l + t.clientWidth + d, m = e.clientWidth; switch (n) { case "end": return p - m + a; case "center": return (f + p) / 2 - m / 2; default: return f - i; } } function re(e) { return e.map((e) => ({ el: e, align: D(getComputedStyle(e).scrollSnapAlign) })); } function k(e) { let t = e.getBoundingClientRect(), n = getComputedStyle(e), r = n.direction === "rtl", i = parseFloat(n.scrollPaddingInlineStart) || 0, a = parseFloat(n.scrollPaddingInlineEnd) || 0, o = Math.max(e.scrollWidth - e.clientWidth, 0), s = document.createTreeWalker(e, NodeFilter.SHOW_ELEMENT), c = [], l = s.nextNode(); for (; l;) { let n = l, u = getComputedStyle(n), d = E(u.scrollSnapAlign); if (d !== "none") { let s = O(e, n, d, t, i, a, r, u); c.push({ el: n, align: d, x: Math.min(Math.max(s, 0), o) }); } l = s.nextNode(); } return c.sort((e, t) => e.x - t.x), c.reduce((e, t) => ((e.length === 0 || Math.abs(e[e.length - 1].x - t.x) > 1) && e.push(t), e), []); } //#endregion //#region ../navigation/src/cache.ts var A = /* @__PURE__ */ new WeakMap(); function ie(e, t) { A.set(e, t); } function j(e) { return A.get(e); } function ae(e) { A.delete(e); } //#endregion //#region ../navigation/src/scroll.ts var M = .85, N = 1; function P(e, t) { let n = Math.max(e.scrollWidth - e.clientWidth, 0), r = Math.abs(e.scrollLeft); return t === "prev" ? r > N : r < n - N; } function oe(e, t, n) { if (n === "next") { for (let n = 0; n < e.length; n++) if (e[n].x > t + N && e[n].el.isConnected) return e[n]; } else for (let n = e.length - 1; n >= 0; n--) if (e[n].x < t - N && e[n].el.isConnected) return e[n]; return null; } function F(e, t) { let n = j(e), r = n?.pages ?? k(e), i = n?.rtl ?? w(e); if (r.length) { let n = oe(r, T(e, i), t); if (!n) return; n.el.scrollIntoView({ block: "nearest", inline: n.align, behavior: "smooth" }); return; } let a = e.clientWidth * M * (t === "next" ? 1 : -1); e.scrollBy({ left: i ? -a : a, behavior: "smooth" }); } var se = "[data-blossom-slide]", I = 1; function L(e) { return Array.from(e.querySelectorAll(se)); } function R(e, t) { let n = e.clientWidth, r = e.scrollWidth - n, i = e.getBoundingClientRect(), a = getComputedStyle(e), o = a.direction === "rtl", s = parseFloat(a.scrollPaddingInlineStart) || 0, c = parseFloat(a.scrollPaddingInlineEnd) || 0, l = t.map((t) => { let n = getComputedStyle(t), r = E(n.scrollSnapAlign); return O(e, t, r === "none" ? "start" : r, i, s, c, o, n); }); if (r > 0) { let e = Math.min(n / 8, r / 2), t = l.filter((t) => t < e); if (t.length > 0) { let n = Math.min(...t), r = e - n; if (r > 0) for (let t = 0; t < l.length; t++) l[t] < e && (l[t] = (l[t] - n) / r * e); } let i = r - e, a = l.filter((e) => e > i); if (a.length > 0) { let t = Math.max(...a) - i; if (t > 0) for (let n = 0; n < l.length; n++) l[n] > i && (l[n] = (l[n] - i) / t * e + i); } } return l; } function ce(e, t, n, r = [...e].sort((e, t) => e - t)) { if (e.length === 0) return -1; if (e.length === 1) return 0; let i = n / 2, a = null; for (let e = 0; e < r.length; e++) { let n = r[e], o = e > 0 ? r[e - 1] : -Infinity; (n <= t + I || o < t - i && n < t + i) && (a = n); } a === null && (a = r[0]); for (let t = 0; t < e.length; t++) if (Math.abs(e[t] - a) <= I) return t; return 0; } function le(e, t, n) { if (t.length === 0) return -1; if (t.length === 1) return 0; let r = e.clientWidth; if (e.scrollWidth - r <= 0) return 0; let i = n?.rtl ?? w(e); return ce(n?.activePositions ?? R(e, t), T(e, i), r, n?.sortedActivePositions); } //#endregion //#region ../navigation/src/commands.ts var z = "--blossom-", B = { prev: `${z}prev`, next: `${z}next`, gotoPrefix: `${z}goto-` }, V = Symbol.for("blossom-carousel.navigation.registry"); function H(e, t) { if (t === B.prev) F(e, "prev"); else if (t === B.next) F(e, "next"); else if (t.startsWith(B.gotoPrefix)) { let n = Number.parseInt(t.slice(B.gotoPrefix.length), 10); if (Number.isNaN(n) || n < 0) return; let r = j(e)?.markers[n], i = r?.el.isConnected ? r : void 0, a = i?.el ?? L(e)[n]; if (!a) return; let o = i?.align ?? D(getComputedStyle(a).scrollSnapAlign); a.scrollIntoView({ block: "nearest", inline: o, behavior: "smooth" }); } } function U(e) { K(); let t = e, n = t[V]; if (n) return n.count++, () => W(t); let r = (t) => { let n = t.command ?? t.detail?.command; typeof n == "string" && n.startsWith(z) && H(e, n); }; return e.addEventListener("command", r), t[V] = { count: 1, handler: r }, () => W(t); } function W(e) { let t = e[V]; t && (t.count--, t.count <= 0 && (e.removeEventListener("command", t.handler), delete e[V])); } var G = !1; function K() { G || typeof document > "u" || (G = !0, !(typeof HTMLButtonElement < "u" && "commandForElement" in HTMLButtonElement.prototype) && document.addEventListener("click", (e) => { let t = e.target?.closest?.("button[commandfor]"); if (!t) return; let n = t.getAttribute("commandfor"), r = t.getAttribute("command"); !n || !r || document.getElementById(n)?.dispatchEvent(new CustomEvent("command", { bubbles: !1, detail: { command: r } })); })); } //#endregion //#region ../navigation/src/observe.ts function ue(e) { let t = j(e), n = t?.targets ?? L(e); return { count: n.length, activeIndex: le(e, n, t), canPrev: P(e, "prev"), canNext: P(e, "next") }; } function de(e) { let t = L(e), n = R(e, t); ie(e, { targets: t, markers: re(t), pages: k(e), activePositions: n, sortedActivePositions: [...n].sort((e, t) => e - t), rtl: w(e) }); } var q = Symbol.for("blossom-carousel.navigation.observers"); function fe(e) { let t = /* @__PURE__ */ new Set(), n = 0, r = !0, i = () => { cancelAnimationFrame(n), n = requestAnimationFrame(() => { r && (r = !1, de(e)); let n = ue(e); for (let e of t) e(n); }); }, a = () => i(), o = () => { r = !0, i(); }; e.addEventListener("scroll", a, { passive: !0 }), e.addEventListener("scrollend", a, { passive: !0 }); let s = new ResizeObserver(o); s.observe(e); let c = new MutationObserver(o); return c.observe(e, { childList: !0, subtree: !0 }), { subscribers: t, flush: i, teardown: () => { cancelAnimationFrame(n), e.removeEventListener("scroll", a), e.removeEventListener("scrollend", a), s.disconnect(), c.disconnect(), ae(e); } }; } function pe(e, t) { let n = e, r = n[q] ?? fe(e); return n[q] = r, r.subscribers.add(t), r.flush(), () => { let e = n[q]; e && (e.subscribers.delete(t), e.subscribers.size === 0 && (e.teardown(), delete n[q])); }; } //#endregion //#region src/useNavigation.ts var J = Symbol.for("blossom-carousel.slideCounts"); function Y(e) { let t = e; return t[J] || (t[J] = /* @__PURE__ */ new Map()), t[J]; } function me(e, t, n) { Y(e).set(t, n); } function he(e, t) { return t ? Y(e).get(t) ?? 0 : 0; } var ge = { activeIndex: -1, count: 0, canPrev: !1, canNext: !1 }; function X(e) { let t = s()?.appContext.app; function n(e) { let n = t ? he(t, e) : 0; return { ...ge, count: n }; } let r = h(n(e.value)), i = null, a = !1; function o() { i?.(), i = null; } function c(e) { if (o(), r.value = n(e), !e || typeof document > "u") return; let t = document.getElementById(e); if (!t) return; let a = U(t), s = pe(t, (e) => { r.value = e; }); i = () => { a(), s(); }; } return te(e, (e) => { if (a) { c(e); return; } r.value = n(e); }, { flush: "sync" }), f(() => { a = !0, c(e.value); }), d(o), r; } //#endregion //#region src/BlossomCarousel.vue?vue&type=script&setup=true&lang.ts function _e(e) { return e ? "data-blossom-slide" in e || "dataBlossomSlide" in e : !1; } function ve(e) { let { children: t } = e; if (!t) return []; if (Array.isArray(t)) return t.filter(l); if (typeof t == "object" && "default" in t) { let e = t.default; if (typeof e == "function") return e().filter(l); } return []; } function Z(n) { let r = 0; for (let i of n) !l(i) || i.type === e || (_e(i.props) && r++, (i.type === t || i.children) && (r += Z(ve(i)))); return r; } function ye(e) { return Z(e); } //#endregion //#region src/BlossomCarousel.vue var be = /* @__PURE__ */ o({ __name: "BlossomCarousel", props: { as: { type: String, default: "div" }, repeat: { type: Boolean, default: !1 }, load: { type: String, default: "conditional" } }, setup(e, { expose: t }) { let r = e, i = null, a = y(null), o = ee(), c = S(), l = s().appContext.app; return ne(() => { let e = o.id; if (!e) return; let t = c.default?.() ?? []; me(l, e, ye(t)); }), t({ el: a, prev: () => i?.prev({ align: "center" }), next: () => i?.next({ align: "center" }) }), f(async () => { if (!window.matchMedia("(hover: hover) and (pointer: fine)").matches && r.load !== "always") return; let { Blossom: e } = await import("@blossom-carousel/core"); a.value && (i = e(a.value, { repeat: r.repeat }), i.init()); }), d(() => { i?.destroy(); }), (t, r) => (p(), n(v(e.as), { ref_key: "root", ref: a, "blossom-carousel": "true" }, { default: C(() => [_(t.$slots, "default")]), _: 3 }, 512)); } }), xe = [ "command", "commandfor", "disabled", "aria-controls" ], Se = /*@__PURE__*/ o({ __name: "BlossomPrev", props: { for: {} }, setup(e) { let t = e, n = B.prev, i = X(b(t, "for")); return (e, o) => (p(), r("button", { "blossom-prev": "", type: "button", command: x(n), commandfor: t.for, disabled: !x(i).canPrev, "aria-controls": t.for, "aria-label": "Previous slide" }, [_(e.$slots, "default", {}, () => [o[0] ||= a("‹", -1)], !0)], 8, xe)); } }), Q = (e, t) => { let n = e.__vccOpts || e; for (let [e, r] of t) n[e] = r; return n; }, Ce = /*#__PURE__*/ Q(Se, [["__scopeId", "data-v-9ec68549"]]), we = [ "command", "commandfor", "disabled", "aria-controls" ], Te = /*#__PURE__*/ Q(/* @__PURE__ */ o({ __name: "BlossomNext", props: { for: {} }, setup(e) { let t = e, n = B.next, i = X(b(t, "for")); return (e, o) => (p(), r("button", { "blossom-next": "", type: "button", command: x(n), commandfor: t.for, disabled: !x(i).canNext, "aria-controls": t.for, "aria-label": "Next slide" }, [_(e.$slots, "default", {}, () => [o[0] ||= a("›", -1)], !0)], 8, we)); } }), [["__scopeId", "data-v-a15f52e2"]]), $ = Symbol("blossom-dot"), Ee = /* @__PURE__ */ o({ __name: "DotScope", props: { index: {}, active: { type: Boolean }, forId: {} }, setup(e) { return m($, e), (e, t) => _(e.$slots, "default"); } }), De = [ "command", "commandfor", "aria-controls", "aria-current", "aria-label" ], Oe = /* @__PURE__ */ o({ inheritAttrs: !1, __name: "BlossomDots", props: { for: {} }, setup(a) { let o = a, s = S(), c = B.gotoPrefix, l = X(b(o, "for")); function d() { return (s.default?.({ index: 0, active: !1 }) ?? []).some((t) => t.type !== e); } return (e, a) => (p(), r("div", u({ "data-blossom-dots": "", role: "group", "aria-label": "Choose slide to display" }, e.$attrs), [d() ? (p(!0), r(t, { key: 0 }, g(x(l).count, (t, r) => (p(), n(Ee, { key: `dot-${r}`, index: r, active: x(l).activeIndex === r, "for-id": o.for }, { default: C(() => [_(e.$slots, "default", { index: r, active: x(l).activeIndex === r })]), _: 2 }, 1032, [ "index", "active", "for-id" ]))), 128)) : (p(!0), r(t, { key: 1 }, g(x(l).count, (e, t) => (p(), r("button", { key: `dot-${t}`, type: "button", "data-blossom-dot": "", command: `${x(c)}${t}`, commandfor: o.for, "aria-controls": o.for, "aria-current": x(l).activeIndex === t, "aria-label": `Go to slide ${t + 1}` }, [...a[0] ||= [i("span", { "data-blossom-dot-marker": "" }, null, -1)]], 8, De))), 128))], 16)); } }), ke = [ "command", "commandfor", "aria-controls", "aria-current", "aria-label" ], Ae = /* @__PURE__ */ o({ inheritAttrs: !1, __name: "BlossomDot", setup(e) { let t = c($); return t || console.warn("[BlossomDot] Must be used inside a BlossomDots slot."), (e, n) => (p(), r("button", u({ type: "button", "data-blossom-dot": "", command: `${x(B).gotoPrefix}${x(t)?.index ?? 0}`, commandfor: x(t)?.forId ?? "", "aria-controls": x(t)?.forId, "aria-current": x(t)?.active ?? !1, "aria-label": x(t) ? `Go to slide ${x(t).index + 1}` : void 0 }, e.$attrs), [_(e.$slots, "default")], 16, ke)); } }); //#endregion export { be as BlossomCarousel, Ae as BlossomDot, Oe as BlossomDots, Te as BlossomNext, Ce as BlossomPrev };