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