@blossom-carousel/vue
Version:
A native-scroll-first carousel component for Vue.
487 lines (486 loc) • 13.8 kB
JavaScript
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 };