sliderfy
Version:
A lightweight, simple, and customizable React slider.
154 lines (153 loc) • 6.43 kB
JavaScript
import { jsxs, jsx } from "react/jsx-runtime";
import { useState, useRef, useCallback, useEffect } from "react";
let swipeStart = 0;
let isSwiping = false;
const throttle = (func, ms) => {
let lastCall = 0;
return function(...args) {
const now = Date.now();
if (now - lastCall >= ms) {
lastCall = now;
return func(...args);
}
};
};
const Sliderfy = ({
sliderfyClassName = "sliderfy",
children,
slidesPerView = 1,
slidesPerGroup = 1,
spaceBetween = 0,
transitionDuration = 1e3,
allowTouchMove = true,
autoplay = {
delay: 0,
reverseDirection: false
},
vertical = false,
renderDot = null
}) => {
const slidesArray = Array.isArray(children) ? children : [children];
const arrLen = slidesArray.length * 4;
const dotsCount = slidesArray.length;
const m = Math;
const { delay, reverseDirection } = autoplay;
const [slideIndex, setSlideIndex] = useState(0);
const autoplayInterval = useRef(null);
const sliderRef = useRef(null);
const [sliderSize, setSliderSize] = useState({ w: 0, h: 0 });
slidesPerGroup = m.max(1, m.min(slidesPerView = m.max(1, m.min(slidesPerView, arrLen / 4 - 1)), slidesPerGroup));
const changeSlide = useCallback(
throttle((direction) => {
clearInterval(autoplayInterval.current ?? void 0);
setSlideIndex((prevIndex) => (prevIndex + slidesPerGroup * direction + arrLen) % arrLen);
const slider = sliderRef.current;
const parent = slider == null ? void 0 : slider.parentElement;
if (!slider || !parent) return;
const parentSize = vertical ? parent.clientHeight : parent.clientWidth;
const offsetPerSlide = (parentSize - spaceBetween * slidesPerView) / slidesPerView;
const fullOffset = (offsetPerSlide + spaceBetween) * direction * slidesPerGroup;
slider.style.transitionDuration = "0ms";
slider.style.transform = `translate${vertical ? "Y" : "X"}(${fullOffset}px)`;
requestAnimationFrame(() => {
slider.style.transitionDuration = `${transitionDuration}ms`;
slider.style.transform = `translate${vertical ? "Y" : "X"}(0)`;
});
}, transitionDuration),
[slidesPerGroup, slidesPerView, spaceBetween, vertical, sliderSize, transitionDuration]
);
const generateSlides = () => {
var _a;
const parent = (_a = sliderRef.current) == null ? void 0 : _a.parentElement;
const parentSize = vertical ? (parent == null ? void 0 : parent.clientHeight) ?? 1 : (parent == null ? void 0 : parent.clientWidth) ?? 1;
const slideSize = (parentSize - spaceBetween * slidesPerView) / slidesPerView / parentSize * 100;
return Array.from({ length: slidesPerView + slidesPerGroup * 2 + 2 }, (_, i) => {
const index = (slideIndex + i - slidesPerGroup + arrLen - 1) % arrLen;
return /* @__PURE__ */ jsx(
"div",
{
className: `sliderfy__slide ${sliderfyClassName}__slide${i >= slidesPerGroup + 1 && i < slidesPerGroup + slidesPerView + 1 ? ` ${sliderfyClassName}__slide--active` : ""}`,
style: {
[vertical ? "height" : "width"]: `${slideSize}%`
},
children: slidesArray[index % (arrLen / 4)].props.children
},
index
);
});
};
const generateDots = () => renderDot && /* @__PURE__ */ jsx("ul", { className: `sliderfy__dots ${sliderfyClassName}__dots`, children: Array.from(
{ length: dotsCount },
(_, i) => renderDot(
i,
`sliderfy__dot ${sliderfyClassName}__dot ${slideIndex % dotsCount === i ? `sliderfy__dot--active ${sliderfyClassName}__dot--active` : ""}`
)
) });
const generateArrows = () => ["prev", "next"].map((direction, i) => /* @__PURE__ */ jsx(
"div",
{
className: `sliderfy__arrow sliderfy__arrow--${direction} ${sliderfyClassName}__arrow ${sliderfyClassName}__arrow--${direction}`,
onClick: () => changeSlide(i ? 1 : -1)
},
direction
));
useEffect(() => {
if (sliderRef.current) {
setSliderSize({
w: sliderRef.current.clientWidth,
h: sliderRef.current.clientHeight
});
}
}, []);
useEffect(() => {
sliderRef.current.style.transitionDuration = `${transitionDuration}`;
sliderRef.current.style.gap = `${spaceBetween}px`;
}, [spaceBetween, transitionDuration]);
useEffect(() => {
if (delay) {
autoplayInterval.current = setInterval(() => {
console.log(sliderfyClassName + " " + Math.round(Math.random() % 10 * 10));
changeSlide(reverseDirection ? -1 : 1);
}, Math.max(delay, transitionDuration + 200));
return () => clearInterval(autoplayInterval.current ?? void 0);
}
return () => clearInterval(autoplayInterval.current ?? void 0);
}, [delay, reverseDirection, transitionDuration, changeSlide, sliderSize, slideIndex]);
useEffect(() => {
const sliderEl = sliderRef.current;
if (!allowTouchMove || !sliderEl) return;
const disableDrag = (event) => event.preventDefault();
const preventClickOnSwipe = (event) => isSwiping && event.preventDefault();
const handlePointerDown = (event) => {
isSwiping = false;
swipeStart = vertical ? event.clientY : event.clientX;
};
const handlePointerUp = (event) => {
const delta = swipeStart - (vertical ? event.clientY : event.clientX);
if (m.abs(delta) > 20) {
changeSlide(delta > 0 ? 1 : -1);
isSwiping = true;
}
};
const events = [
["click", preventClickOnSwipe],
["dragstart", disableDrag],
["pointerdown", handlePointerDown],
["pointerup", handlePointerUp]
];
if (allowTouchMove) {
events.forEach(([e, h]) => sliderEl == null ? void 0 : sliderEl.addEventListener(e, h));
return () => events.forEach(([e, h]) => sliderEl == null ? void 0 : sliderEl.removeEventListener(e, h));
}
}, [allowTouchMove, vertical, autoplay, sliderSize, changeSlide, m]);
if (!children) return;
return /* @__PURE__ */ jsxs("div", { className: `sliderfy sliderfy-${vertical ? "vertical" : "horizontal"} ${sliderfyClassName}`, children: [
/* @__PURE__ */ jsx("div", { className: `sliderfy__container ${sliderfyClassName}__container`, children: /* @__PURE__ */ jsx("div", { className: `sliderfy__wrapper ${sliderfyClassName}__wrapper`, ref: sliderRef, children: generateSlides() }) }),
generateArrows(),
generateDots()
] });
};
export {
Sliderfy as default
};
//# sourceMappingURL=sliderfy.js.map