@blossom-carousel/web
Version:
A native-scroll-first carousel component for the web.
336 lines (335 loc) • 10.9 kB
JavaScript
var At = Object.defineProperty;
var It = (e, s, x) => s in e ? At(e, s, { enumerable: !0, configurable: !0, writable: !0, value: x }) : e[s] = x;
var at = (e, s, x) => (It(e, typeof s != "symbol" ? s + "" : s, x), x);
const Mt = (e, s) => {
let x = !0;
const v = { x: 0, y: 0 }, c = { x: 0, y: 0 }, u = { x: 0, y: 0 }, P = new Proxy(
{ x: 0, y: 0 },
{
set(t, n, l) {
return t[n] === l || (t[n] = l, (t.x >= 10 || t.y >= 10) && L(!0)), !0;
}
}
), i = new Proxy(
{ x: !1, y: !1 },
{
set(t, n, l) {
return t[n] === l || (t[n] = l, t.x || t.y ? (e.setAttribute("has-overflow", "true"), e.addEventListener("pointerdown", Z), e.addEventListener("wheel", et, { passive: !1 })) : (e.removeAttribute("has-overflow"), e.removeEventListener("pointerdown", Z), e.removeEventListener("wheel", et))), !0;
}
}
);
let w = 300, m = null, d = !1, h = 300, f = 300, j = 300, G = 300;
const V = { start: 0, end: 0 }, W = { start: 0, end: 0 };
let R = [], S = null, X = null, D = null, F = !1, Y, b = 1, g = { target: null, x: 0 };
function ut() {
e == null || e.setAttribute("blossom-carousel", "true"), S = (e == null ? void 0 : e.querySelectorAll("a[href]")) || null, S == null || S.forEach((l) => {
l.addEventListener("click", J);
}), window.addEventListener("keydown", nt), e.addEventListener("scroll", Q), X = new ResizeObserver(K), X.observe(e), D = new MutationObserver(ft), D.observe(e, {
attributes: !1,
childList: !0,
subtree: !1
});
const t = window.matchMedia(
"(hover: hover) and (pointer: fine)"
).matches;
b = e.closest('[dir="rtl"]') ? -1 : 1;
const { scrollSnapType: n } = window.getComputedStyle(e);
F = n !== "none", e.style.setProperty("--snap-type", n), t && (e.style["scroll-snap-type"] = "none"), e.setAttribute("has-repeat", s != null && s.repeat ? "true" : "false"), Y = bt((l) => {
(l === e || e.contains(l)) && L(!1);
});
}
function dt() {
e.removeAttribute("blossom-carousel"), X == null || X.disconnect(), D == null || D.disconnect(), m && cancelAnimationFrame(m), window.removeEventListener("keydown", nt), e.removeEventListener("scroll", Q), S == null || S.forEach((t) => {
t.removeEventListener("click", J);
}), Y == null || Y();
}
function J(t) {
P.x > 10 && t.preventDefault();
}
function K() {
if (!e)
return;
const t = "ontouchmove" in window;
h = e.scrollWidth, f = e.clientWidth, j = e.scrollHeight, G = e.clientHeight;
const n = window.getComputedStyle(e);
i.x = !t && h > f && ["auto", "scroll"].includes(n.getPropertyValue("overflow-x")), i.y = !t && j > G && ["auto", "scroll"].includes(n.getPropertyValue("overflow-y")), V.end = parseInt(n.paddingInlineEnd) || 0, V.start = parseInt(n.paddingInlineStart) || 0, W.start = parseInt(n.scrollPaddingInlineStart) || 0, W.end = parseInt(n.scrollPaddingInlineEnd) || 0, b = e.closest('[dir="rtl"]') ? -1 : 1, w = (h - f - 4) * b, R = F ? pt(e) : [], s != null && s.repeat && q(null, null);
}
function ft() {
K();
}
function pt(t) {
let n = [], l = 0;
const a = (r) => {
if (l++, l > 100)
return;
const y = window.getComputedStyle(r).scrollSnapAlign;
if (y !== "none") {
n.push({
align: y,
el: r
});
return;
}
const C = r.children;
if (C.length !== 0)
for (let k of C)
a(k);
};
a(t);
const U = t.getBoundingClientRect();
return n.map(({ el: r, align: y }, C) => {
const k = r.getBoundingClientRect(), p = r.clientWidth, A = k.left - U.left + t.scrollLeft;
switch (y) {
case "start":
return { target: r, x: A - W.start };
case "end":
return {
target: r,
x: A + p - f + W.end
};
case "center":
return {
target: r,
x: A + p * 0.5 - f / 2
};
default:
return null;
}
}).filter((r) => r !== null).reduce((r, y) => ((r.length === 0 || r[r.length - 1].x !== y.x) && r.push(y), r), []);
}
function Q() {
if (s != null && s.repeat) {
q(null, null);
return;
}
if (d || !e)
return;
const t = e.scrollLeft;
if (t < 0) {
const n = t * -1;
z(n);
} else if (t > h - f) {
const n = t * -1 + h - f;
z(n);
}
}
const o = {
x: 0,
y: 0
};
function Z(t) {
e && (i.x && (o.x = e.scrollLeft, v.x = t.clientX, u.x = 0), i.y && (o.y = e.scrollTop, v.y = t.clientY, u.y = 0), P.x = 0, d = !0, window.addEventListener("pointermove", _), window.addEventListener("pointerup", tt));
}
function _(t) {
if (t.preventDefault(), i.x) {
const n = v.x - t.clientX;
c.x += n, u.x += n, v.x = t.clientX, P.x += Math.abs(n);
}
if (i.y) {
const n = v.y - t.clientY;
c.y += n, u.y += n, v.y = t.clientY, P.y += Math.abs(n);
}
}
function tt() {
window.removeEventListener("pointermove", _), window.removeEventListener("pointerup", tt), d = !1, !(P.x <= 10) && (i.x && (u.x *= 2), i.y && (u.y *= 2), xt(), Et());
}
function et(t) {
if (Math.abs(t.deltaX) > Math.abs(t.deltaY)) {
if (L(!1), d || !e)
return;
i.x && (o.x = e.scrollLeft), i.y && (o.y = e.scrollTop);
}
}
function nt(t) {
["ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown"].includes(t.key) && L(!1);
}
function xt() {
const t = it({ axis: "x" });
t.x !== g.x && st(t), g = t;
const n = (ct(
t.x,
Math.min((h - f) * b, 0),
Math.max((h - f) * b, 0)
) - c.x) * (1 - E) * (1 / E);
u.x = n;
}
function q(t, n) {
if (!e)
return;
const l = n ?? e.scrollLeft, a = V.start - l, U = l - (h - f - V.end), r = Array.from(e.children);
let y = 0;
for (let p = r.length - 1; p >= r.length / 2; p--) {
const A = y > a ? 0 : -(h - f);
y += r[p].clientWidth, r[p].style.translate = `${A}px 0`;
}
let C = 0;
for (let p = 0; p < r.length / 2; p++) {
const A = C > U ? 0 : h - f;
C += r[p].clientWidth, r[p].style.translate = `${A}px 0`;
}
if (d)
return;
const k = l > w ? 4 : l < 4 ? w : null;
k && (T = !0, e.scrollTo({
left: k,
behavior: "instant"
}));
}
function lt(t) {
O && t.stopPropagation();
}
const E = 0.72, H = 0.12;
let O = !1;
function L(t) {
e && (t && !O ? ($ = performance.now(), i.x && (c.x = e.scrollLeft), i.y && (c.y = e.scrollTop), e.addEventListener("scrollend", lt, {
capture: !0,
passive: !1
}), m || (m = requestAnimationFrame(rt))) : t || (m && cancelAnimationFrame(m), m = null, e.removeEventListener("scrollend", lt)), O = t, x = !t, e.setAttribute("has-snap", x ? "true" : "false"));
}
let I = 0, $ = 0;
function rt(t) {
if (m = requestAnimationFrame(rt), I = t - $, !!e) {
if (i.x && (u.x *= E, d ? o.x = B(o.x, c.x, E, I) : (c.x += u.x, o.x = B(o.x, c.x, H, I))), i.y && (u.y *= E, d ? o.y = B(o.y, c.y, E, I) : (c.y += u.y, o.y = B(o.y, c.y, H, I))), s != null && s.repeat && (o.x > w && (o.x = c.x = 4), o.x < 4 && (o.x = c.x = w)), T = !0, e.scrollTo({
left: o.x,
top: o.y,
behavior: "instant"
}), d && F) {
const n = it({ axis: "x" });
n.x !== g.x && (g = n, st(n));
}
!d && N(u.x, 8) === 0 && (L(!1), yt(), F && vt()), s != null && s.repeat ? q(null, o.x) : ht(N(o.x, 2)), $ = t;
}
}
let M = 0;
function ht(t) {
if (!e)
return;
const n = w;
let l = 0;
if (t * b <= 0 ? l = d ? t * -0.2 : 0 : t * b > n * b && (l = d ? (t - n) * -0.2 : 0), M = B(
M,
l,
d ? 0.8 : H,
I
), Math.abs(M) > 0.01) {
if (z(M).defaultPrevented)
return;
e.style.transform = `translateX(${N(M, 3)}px)`;
return;
}
e.style.transform = "", M = 0;
}
function z(t) {
const n = new CustomEvent("overscroll", {
bubbles: !0,
cancelable: !0,
detail: { left: t }
});
return e == null || e.dispatchEvent(n), n;
}
const ot = new Event("scrollend", {
bubbles: !0,
cancelable: !0
});
function yt() {
return e == null || e.dispatchEvent(ot), ot;
}
function vt() {
const t = new CustomEvent("scrollsnapchange", {
bubbles: !0,
cancelable: !0,
detail: {
snapTargetInline: g.target,
snapTargetBlock: g.target
}
});
return e == null || e.dispatchEvent(t), t;
}
function st(t) {
const n = new CustomEvent("scrollsnapchanging", {
bubbles: !0,
cancelable: !0,
detail: {
snapTargetInline: (t || g).target,
snapTargetBlock: (t || g).target
}
});
return e == null || e.dispatchEvent(n), n;
}
let T = !1;
const wt = e.scrollTo.bind(e);
e.scrollTo = function(t) {
T === !0 || L(!1), T = !1, wt(t);
};
const mt = e.scrollBy.bind(e);
e.scrollBy = function(t) {
T === !0 || L(!1), T = !1, mt(t);
};
function bt(t) {
const n = [], l = Element.prototype.scrollIntoView;
return l && (Element.prototype.scrollIntoView = function(a) {
return t(this, "scrollIntoView", [a]), l.call(this, a);
}, n.push(() => {
Element.prototype.scrollIntoView = l;
})), () => n.forEach((a) => a());
}
function gt({ axis: t = "x" }) {
return c[t] + u[t] / (1 - E);
}
function it({ axis: t = "x" }) {
const n = gt({ axis: t });
return R.length ? R.reduce(
(l, a) => Math.abs(a.x - n) < Math.abs(l.x - n) ? a : l
) : {
target: null,
x: ct(n, Math.min(w, 0), Math.max(w, 0))
};
}
function Et() {
const t = (n) => {
n.preventDefault(), n.stopPropagation(), window.removeEventListener("click", t, !0);
};
window.addEventListener("click", t, !0);
}
function Lt(t, n, l) {
return (1 - l) * t + l * n;
}
function B(t, n, l, a) {
return Lt(t, n, 1 - Math.exp(Math.log(1 - l) * (a / 16.666666666666668)));
}
function ct(t, n, l) {
return t < n ? n : t > l ? l : t;
}
function N(t, n = 0) {
const l = Math.pow(10, n);
return Math.round(t * l) / l;
}
return {
snap: x,
hasOverflow: i,
init: ut,
destroy: dt
};
};
class Tt extends HTMLElement {
constructor() {
super();
at(this, "carouselInstance");
const x = this.attachShadow({ mode: "open" });
this.setAttribute("blossom-carousel", "true");
const v = document.createElement("slot");
x.appendChild(v);
}
connectedCallback() {
this.carouselInstance = Mt(this, {
repeat: this.hasAttribute("repeat")
}), this.carouselInstance.init();
}
disconnectedCallback() {
this.carouselInstance.destroy();
}
}
customElements.define("blossom-carousel", Tt);
export {
Tt as BlossomCarousel
};