UNPKG

react-light-marquee

Version:

What goes around comes around! An ode to the HTML marquee element.

231 lines (227 loc) 8.44 kB
"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/index.ts var src_exports = {}; __export(src_exports, { default: () => Marquee_default }); module.exports = __toCommonJS(src_exports); // src/Marquee/index.tsx var import_react = require("react"); // src/Marquee/utils.ts var getWrapperClassName = (marqueeId) => { return `${marqueeId}_wrapper`; }; var getPlayStateVariable = (marqueeId) => { return `--${marqueeId}_play_state`; }; var getWrapperStyles = (direction, rotateInDeg) => { return { width: "100%", height: "100%", display: "flex", flexDirection: direction === "left" || direction === "right" ? "row" : "column", overflow: "hidden", listStyle: "none", ...rotateInDeg && { transform: rotateInDeg } }; }; var handleInitialSlide = (initialSlideIndex, slides) => { if (!initialSlideIndex) return slides; return slides.slice(initialSlideIndex).concat(slides.slice(0, initialSlideIndex)); }; var generateSlideStyles = (wrapperClassName, marqueeId, slideIndex, translateProp, rotateInDeg, animationConfigs) => { const animations = []; const styles = []; animationConfigs.forEach((animation, index) => { const keyFrameId = `${marqueeId}_keyframe_slide${slideIndex}_${index}`; styles.push(`@keyframes ${keyFrameId} { 0% { transform: ${translateProp}(${animation.from}) ${rotateInDeg}; } 100% { transform: ${translateProp}(${animation.to}) ${rotateInDeg}; } } `); animations.push( `${animation.duration}ms linear ${animation.delay}ms ${animation.count} ${keyFrameId}` ); }); styles.push(`.${wrapperClassName} > :nth-child(${slideIndex + 1}) { animation: ${animations.join(",")}; animation-play-state: var(${getPlayStateVariable(marqueeId)}); }`); return styles; }; var getTranslateProp = (direction) => { return direction === "left" || direction === "right" ? "translateX" : "translateY"; }; var getRotateInDeg = (direction) => { let deg = 0; let rotateStyle = ""; if (direction === "right") { deg = 180; rotateStyle = "rotateY"; } if (direction === "down") { deg = 180; rotateStyle = "rotateX"; } return deg ? `${rotateStyle}(${deg}deg)` : ""; }; var getDistanceBetweenEdges = (direction, startRect, endRect) => { let distance = startRect.bottom - endRect.top; if (direction === "left") distance = endRect.right - startRect.left; else if (direction === "right") distance = startRect.right - endRect.left; else if (direction === "up") distance = endRect.bottom - startRect.top; return distance; }; var getContentSize = (children, direction) => { const firstChildRect = children[0].getBoundingClientRect(); const lastChildRect = children[children.length - 1].getBoundingClientRect(); return getDistanceBetweenEdges(direction, firstChildRect, lastChildRect); }; var getSlideEndingEdge = (parentElement, slideElement, direction) => { const parentRect = parentElement.getBoundingClientRect(); const slideRect = slideElement.getBoundingClientRect(); return getDistanceBetweenEdges(direction, parentRect, slideRect); }; var getNodeSize = (node, direction) => { const rect = node.getBoundingClientRect(); return direction === "left" || direction === "right" ? rect.right - rect.left : rect.bottom - rect.top; }; var handleReplication = (wrapper, direction, children) => { const wrapperSize = getNodeSize(wrapper, direction); const finalSlides = [...children]; const slideEdges = []; const childElements = Array.from(wrapper.children); const initialSlidesCount = childElements.length; let contentSize = getContentSize(childElements, direction); let needsReplication = false; childElements.forEach((slide) => { slideEdges.push(getSlideEndingEdge(wrapper, slide, direction)); needsReplication || (needsReplication = contentSize - getNodeSize(slide, direction) < wrapperSize); }); if (needsReplication) { let replicationCount = 1; if (contentSize <= wrapperSize) { replicationCount = Math.floor(wrapperSize / contentSize + 1); } for (let i = 1; i <= replicationCount; i++) { finalSlides.push(...children); const offset = contentSize * i; for (let j = 0; j < initialSlidesCount; j++) { slideEdges.push(slideEdges[j] + offset); } } contentSize = (replicationCount + 1) * contentSize; } return { finalSlides, slideEdges, contentSize }; }; // src/Marquee/index.tsx var import_jsx_runtime = require("react/jsx-runtime"); var Marquee = ({ id, speed = 50, play = true, children = [], direction = "left", initialSlideIndex = 0, pauseOnHover = false }) => { const wrapperRef = (0, import_react.useRef)(null); const [marqueeId] = (0, import_react.useState)(`marquee_${id || (Math.random() + "").slice(2)}`); const [rotateInDeg] = (0, import_react.useState)(getRotateInDeg(direction)); const [slides, setSlides] = (0, import_react.useState)( () => handleInitialSlide(initialSlideIndex, children) ); (0, import_react.useEffect)(() => { const wrapper = wrapperRef?.current; if (!wrapper || !marqueeId) return; var styleElement = document.createElement("style"); styleElement.setAttribute("type", "text/css"); styleElement.setAttribute("id", marqueeId); document.head.appendChild(styleElement); const styleSheet = styleElement.sheet; const playStateVariable = getPlayStateVariable(marqueeId); const wrapperClassName = getWrapperClassName(marqueeId); const insertRule = (rule) => !!rule && styleSheet?.insertRule?.(rule, 0); [ `:root { ${playStateVariable}: ${play ? "running" : "paused"}; }`, pauseOnHover ? `.${wrapperClassName}:hover { ${playStateVariable}: paused; }` : "" ].forEach(insertRule); const translateProp = getTranslateProp(direction); const { slideEdges, finalSlides, contentSize } = handleReplication( wrapper, direction, slides ); const totalAnimationDuration = contentSize / speed * 1e3 | 0; setSlides(finalSlides); slideEdges.forEach((slideEndingEdge, index) => { const firstAnimationDuration = slideEndingEdge / speed * 1e3 | 0; const animationConfigs = [ { from: "0px", to: `${-slideEndingEdge}px`, delay: 0, duration: firstAnimationDuration, count: 1 }, { from: `${contentSize - slideEndingEdge}px`, to: `${-slideEndingEdge}px`, delay: firstAnimationDuration, duration: totalAnimationDuration, count: "infinite" } ]; generateSlideStyles( wrapperClassName, marqueeId, index, translateProp, rotateInDeg, animationConfigs ).forEach(insertRule); }); return () => { document.documentElement.style.removeProperty(playStateVariable); document.head.removeChild(styleElement); }; }, []); (0, import_react.useEffect)(() => { const wrapper = wrapperRef?.current; if (!wrapper || !marqueeId) return; const playStateVariable = getPlayStateVariable(marqueeId); document.documentElement.style.setProperty(playStateVariable, play ? "running" : "paused"); }, [play]); if (!children.length) { return null; } return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", { ref: wrapperRef, className: getWrapperClassName(marqueeId), style: getWrapperStyles(direction, rotateInDeg), children: slides.map((slide, slideIndex) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("li", { children: slide }, slideIndex)) }); }; Marquee.displayName = "Marquee"; var Marquee_default = Marquee; //# sourceMappingURL=index.js.map