UNPKG

vue-directional-carousel

Version:

This is a Vue 3 component that allows you to set the direction of image movement not only vertically and horizontally but also in different directions (left, right, up, down).

471 lines (470 loc) 17.9 kB
import { defineComponent as k, ref as h, computed as B, openBlock as g, createElementBlock as p, normalizeStyle as w, Fragment as M, renderList as q, createElementVNode as E, unref as r, watch as R, onMounted as $, onBeforeUnmount as Y, onScopeDispose as K, createBlock as V, createCommentVNode as N, renderSlot as z, mergeProps as J } from "vue"; const AA = (A) => { var o; const l = []; (o = A.items) != null && o.length || l.push({ prop: "items", message: "At least one item is required" }), A.interval != null && A.interval < 0 && l.push({ prop: "interval", message: "Interval must be a non-negative number" }), A.duration != null && A.duration <= 0 && l.push({ prop: "duration", message: "Duration must be greater than 0" }); const t = /^(\d+(\.\d+)?)(px|%|rem|em|vh|vw)$/; return A.width && !t.test(A.width) && l.push({ prop: "width", message: "Width must be a valid CSS dimension (e.g., 100%, 200px)" }), A.height && !t.test(A.height) && l.push({ prop: "height", message: "Height must be a valid CSS dimension (e.g., 300px, 50vh)" }), l; }, eA = { style: { padding: "0px 4px 0px 4px" } }, tA = ["onClick", "aria-label", "aria-current"], oA = /* @__PURE__ */ k({ __name: "DotButtons", props: { currentIndex: { default: 0 }, itemCount: { default: 0 }, showDots: { type: Boolean, default: !1 } }, emits: ["click-dot-button"], setup(A, { emit: l }) { const t = l, o = A, v = h({ display: "flex", alignItems: "center", justifyContent: "center", flexWrap: "wrap" }), a = (u) => { t("click-dot-button", u - 1); }, i = B( () => new Array(o.itemCount).fill({ border: "none", borderRadius: "50%", backgroundColor: "gray", cursor: "pointer" }).map((u, e) => { const s = e === (o.itemCount > o.currentIndex ? o.currentIndex : 0); return { ...u, opacity: s ? "1" : "0.5", height: s ? "8px" : "6px", paddingInline: s ? "4px" : "3px" }; }) ); return (u, e) => (g(), p("div", { style: w(v.value) }, [ (g(!0), p(M, null, q(u.itemCount, (s) => (g(), p("div", eA, [ E("button", { style: w(i.value[s - 1]), onClick: (m) => a(s), "aria-label": `Go to slide ${s}`, "aria-current": s - 1 === u.currentIndex }, null, 12, tA) ]))), 256)) ], 4)); } }); function j() { const A = h({ zIndex: 1, display: "flex", justifyContent: "center", alignItems: "center", padding: "5px" }), l = h({ height: "20px", border: "none", color: "silver", borderRadius: "10px" }); return { buttonWrapperStyle: A, buttonStyle: l, onMouseDownButton: () => { l.value.color = "gray"; }, onMouseUpButton: () => { l.value.color = "silver"; } }; } const F = "data:image/png;base64,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pr/pb/qb/qa/6W/6m/42U1utv/7666+//vrrr7/++uuvv/7666+//vrrr7/++uuvv/7666+//vrrr7/++uuvv/7666+//vrrr7/++uuvv/7662+vZv8J/fXXX3/99ddff/31119//fXXX3/99ddff/2buDX666+//vrrr7/++uuvv/7666+//vrrr7/++uuvv/7666+//vrrr7/++uuvv/7666+//vrrr7/++uuvv/7666+//qa/6W/6m/6mv+lv+pv+pr/pb/rbjG2t/vrrr7/++uuvv/7666+//vrrr7/++uuvv/7666+//vrrr7/++uuvv/7666+//rOz/z399ddff/31119//fXXX3/99ddff/31119//fXXX3/99ddff/31119//fXXX3/9Z+PW6a+//vrrr7/++uuvv/7666+//vrrr7/++uuvv/7666+//vrrr7/++uuvv/7666+//vrrr7/++uuvv/7666+//vqb/qa/6W/6m/6mv+lv+pv+pr/N6Lr1119//fXXX/+4ef5n9pbc0T9586/qH73T+kfvoP7R63uif/IW39c/esf1j17XlP5+AtA/dque6h+9i/pHr1v/7B3TP3u39I/eCv2z96X+2WvpH723pvSP3hb9s7dH/+wd1j97Q/pn70f9szesf/bG9M/ebf2zd03/7F3SP3sn9M9ev/7Z269/9rbrn71l+odvVP/sDeifvV79s9cxrn/2TumfvR36h78G3NA/e9v0z97cEf2zt0n/8LX0z16P/uHr1z/8+8Dz+mdvwaj+2Vt+V//sbXisf/b2ti1gss/VGvV+0ER7/cdWulmz1tXWr4Vai1ysaVvYxlNjhzrdq3mbd2Sa+R/tc6xmbv20Hhhwwct/c7f1+ktf/XtcqdGvAztf+PjgkY1O1PR17j43+fz64ye995OxNzZ/+9ez9UcHejtcJmgffPrV4Nkrfzy4f7N15uiBXUtdZCb2HyDq9i+SDCuqAAAAAElFTkSuQmCC", nA = ["src"], lA = /* @__PURE__ */ k({ __name: "PrevButton", emits: ["click-prev-button"], setup(A, { emit: l }) { const t = l, { buttonWrapperStyle: o, buttonStyle: v, onMouseDownButton: a } = j(), i = () => { t("click-prev-button"); }; return (u, e) => (g(), p("div", { style: w(r(o)) }, [ E("button", { class: "prev-button", style: w(r(v)), onClick: i, onMousedown: e[0] || (e[0] = //@ts-ignore (...s) => r(a) && r(a)(...s)), "aria-label": "Previous slide" }, [ E("img", { alt: "left arrow", src: r(F), style: { width: "10px", borderRadius: "50%", transform: "scaleX(-1)" } }, null, 8, nA) ], 36) ], 4)); } }), rA = ["src"], sA = /* @__PURE__ */ k({ __name: "NextButton", emits: ["click-next-button"], setup(A, { emit: l }) { const t = l, { buttonWrapperStyle: o, buttonStyle: v, onMouseUpButton: a } = j(), i = () => { t("click-next-button"); }; return (u, e) => (g(), p("div", { style: w(r(o)) }, [ E("button", { class: "next-button", style: w(r(v)), onClick: i, onMouseup: e[0] || (e[0] = //@ts-ignore (...s) => r(a) && r(a)(...s)), "aria-label": "Next slide" }, [ E("img", { alt: "left arrow", src: r(F), style: { width: "10px", borderRadius: "3px" } }, null, 8, rA) ], 36) ], 4)); } }), aA = 1e3, iA = "translate"; function uA(A, l, t, o, v) { const a = h(!1), i = B(() => { const f = A.direction; return { isHorizontal: ["left", "right"].includes(f), isReverse: ["up", "left"].includes(f), flexDirection: { left: "row", right: "row-reverse", up: "column", down: "column-reverse" }[f] }; }), u = B( () => i.value.isHorizontal ? o.value : v.value ), e = B(() => { const f = l.value.length, C = t.value % f; return i.value.isReverse ? C : f - (C + 1 % f); }), s = B(() => e.value * u.value), m = B(() => { const f = i.value.isHorizontal ? "X" : "Y"; return `${iA}${f}(-${s.value}px)`; }), y = h(`transform ${(A.duration ?? aA) / 1e3}s ease`), Q = h(y.value); return { itemsTranslate: m, defaultTransition: y, transition: Q, isTransitioning: a, directionProps: i }; } function vA(A, l, t, o, v, a, i) { const u = h(null), e = h(!1), s = () => { o.value = ++o.value % t.value.length, a.value = v.value; }, m = () => { i.value || (o.value >= t.value.length - 1 ? (i.value = !0, a.value = "", o.value = 0, requestAnimationFrame(() => { requestAnimationFrame(() => { a.value = v.value, o.value = 1, setTimeout(() => { i.value = !1; }, A.duration || 1e3); }); })) : s()); }, y = () => { u.value !== null && (window.clearInterval(u.value), u.value = null); }, Q = () => { y(), A.interval && A.items.length > 1 && !e.value && (u.value = window.setInterval(m, A.interval)); }, f = () => { m(), A.interval && A.items.length > 1 && Q(); }, C = B( () => (o.value - 1 + t.value.length) % t.value.length ), x = () => { i.value || (o.value === 0 ? (i.value = !0, a.value = "", o.value = C.value, setTimeout(() => { o.value = C.value, a.value = v.value, setTimeout(() => { i.value = !1; }, A.duration || 1e3); }, 100)) : o.value = C.value, A.interval && A.items.length > 1 && Q()); }, D = (b) => { i.value || (o.value = b, A.interval && Q()); }; R( () => [ A.interval, A.direction, A.items.length, A.pauseAutoplayOnHover ], () => { Q(); }, { deep: !0 } ); const S = () => { A.pauseAutoplayOnHover && (e.value = !0, y()); }, P = () => { A.pauseAutoplayOnHover && A.items.length > 1 && (e.value = !1, Q()); }; return $(() => { a.value = "", A.interval && A.items.length > 1 && Q(); }), Y(() => { y(); }), K(() => { y(); }), { clickNext: f, clickPrev: x, clickDot: D, pauseSlide: S, resumeSlide: P }; } function cA(A, l) { const t = h(/* @__PURE__ */ new Map()), o = h(/* @__PURE__ */ new Map()), v = h(/* @__PURE__ */ new Set()), a = (e) => { t.value.set(e, !1), o.value.set(e, !1), v.value.add(e); }, i = (e) => { t.value.set(e, !1), o.value.set(e, !0); }, u = (e) => { if (!e || v.value.has(e)) return; t.value.set(e, !0); const s = new Image(); s.onload = () => a(e), s.onerror = () => i(e), s.src = e; }; return A && l && R([A, l], () => { const e = l.value[A.value], s = (A.value + 1) % l.value.length, m = l.value[s]; e != null && e.src && u(e.src), m != null && m.src && u(m.src); }, { immediate: !0 }), { loadingStates: t, errorStates: o, loadedImages: v, handleImageLoad: a, handleImageError: i, preloadImage: u }; } const dA = ["aria-hidden"], fA = { class: "carousel-image-container", style: { width: "100%", height: "100%" } }, gA = { key: 0, class: "carousel-image-loading" }, mA = { key: 1, class: "carousel-image-error" }, BA = ["src", "alt", "onLoad", "onError"], hA = /* @__PURE__ */ k({ __name: "DirectionalCarousel", props: { width: { default: "100%" }, height: { default: "300px" }, items: {}, direction: { default: "right" }, duration: { default: 1e3 }, showPrev: { type: Boolean, default: !0 }, showNext: { type: Boolean, default: !0 }, showDots: { type: Boolean, default: !0 }, interval: { default: 0 }, arrowOptions: {}, pauseAutoplayOnHover: { type: Boolean, default: !1 } }, setup(A, { expose: l }) { const t = A; R( () => ({ items: t.items, interval: t.interval, duration: t.duration, width: t.width, height: t.height }), () => { const n = AA(t); if (n.length > 0 && (n.forEach((c) => { console.error(`DirectionalCarousel: ${c.prop} - ${c.message}`); }), n.some((c) => c.prop === "items"))) throw new Error("DirectionalCarousel: Invalid props - check console for details"); }, { immediate: !0 } ); const v = h(null), a = B( () => t.items.map((n) => typeof n == "string" ? { src: n } : n) ), i = B(() => t.items.length), u = B(() => [...a.value, a.value[0]]), e = h(0), s = B(() => { var n; return ((n = v.value) == null ? void 0 : n.offsetWidth) ?? 0; }), m = B(() => { var n; return ((n = v.value) == null ? void 0 : n.offsetHeight) ?? 0; }), { itemsTranslate: y, defaultTransition: Q, transition: f, isTransitioning: C, directionProps: x } = uA(t, u, e, s, m), { clickNext: D, clickPrev: S, clickDot: P, pauseSlide: b, resumeSlide: I } = vA( t, a, u, e, Q, f, C ), U = B(() => ({ overflow: "hidden", width: s.value + "px", height: m.value + "px", display: "flex", justifyContent: "center", alignItems: "center" })), { loadingStates: X, errorStates: O, loadedImages: T, preloadImage: pA } = cA(e, a), _ = (n) => { X.value.set(n, !1), T.value.add(n); }, G = (n) => { X.value.set(n, !1), O.value.set(n, !0); }; return l({ currentIndex: e }), (n, c) => { var W, L; return g(), p("div", { style: w({ overflow: "hidden", display: "flex", flexDirection: "column", width: t.width, height: t.height }) }, [ E("div", { class: "slider", style: { overflow: "hidden", position: "relative", display: "flex", width: "100%", height: "100%" }, onMouseenter: c[0] || (c[0] = //@ts-ignore (...d) => r(b) && r(b)(...d)), onFocusin: c[1] || (c[1] = //@ts-ignore (...d) => r(b) && r(b)(...d)), onMouseleave: c[2] || (c[2] = //@ts-ignore (...d) => r(I) && r(I)(...d)), onFocusout: c[3] || (c[3] = //@ts-ignore (...d) => r(I) && r(I)(...d)) }, [ (W = n.arrowOptions) != null && W.prev.show || n.showPrev ? (g(), V(lA, { key: 0, onClickPrevButton: r(S) }, null, 8, ["onClickPrevButton"])) : N("", !0), E("div", { ref_key: "carouselContainer", ref: v, style: { overflow: "hidden", position: "relative", flex: 1 } }, [ E("div", { class: "wrapper-carousel-items", style: w({ display: "inline-flex", position: "relative", flexDirection: r(x).flexDirection, transform: r(y), transition: r(f), pointerEvents: r(C) ? "none" : "auto" }) }, [ (g(!0), p(M, null, q(u.value, (d, H) => (g(), p("div", { key: H, style: w(U.value), class: "carousel-item", "aria-hidden": e.value !== H }, [ z(n.$slots, "item", J({ ref_for: !0 }, d, { style: { width: n.width, height: n.height } }), () => [ E("div", fA, [ r(X).get(d.src) ? (g(), p("div", gA, [ z(n.$slots, "loading", {}, () => [ c[4] || (c[4] = E("div", { class: "loading-spinner" }, null, -1)) ], !0) ])) : r(O).get(d.src) ? (g(), p("div", mA, [ z(n.$slots, "error", {}, () => [ c[5] || (c[5] = E("div", { class: "error-fallback" }, [ E("span", null, "Failed to load image") ], -1)) ], !0) ])) : (g(), p("img", { key: 2, src: d.src, alt: `Slide image ${H + 1} of ${i.value}`, style: w({ width: "100%", opacity: r(T).has(d.src) ? 1 : 0, transition: "opacity 0.2s" }), onLoad: (Z) => _(d.src), onError: (Z) => G(d.src) }, null, 44, BA)) ]) ], !0) ], 12, dA))), 128)) ], 4) ], 512), (L = n.arrowOptions) != null && L.next.show || n.showNext ? (g(), V(sA, { key: 1, onClickNextButton: r(D) }, null, 8, ["onClickNextButton"])) : N("", !0) ], 32), n.showDots ? (g(), V(oA, { key: 0, "current-index": e.value, "item-count": i.value, "show-dots": n.showDots, onClickDotButton: r(P) }, null, 8, ["current-index", "item-count", "show-dots", "onClickDotButton"])) : N("", !0) ], 4); }; } }), EA = (A, l) => { const t = A.__vccOpts || A; for (const [o, v] of l) t[o] = v; return t; }, wA = /* @__PURE__ */ EA(hA, [["__scopeId", "data-v-e58f5c3d"]]); export { wA as default };