lazer-slider 1.0.8 → 1.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -280,11 +280,11 @@ var injectMarqueeKeyframes = () => {
280
280
  style.id = "lazer-marquee-keyframes";
281
281
  style.textContent = `
282
282
  @keyframes lazer-marquee-scroll {
283
- from {
283
+ 0% {
284
284
  transform: translateX(0);
285
285
  }
286
- to {
287
- transform: translateX(calc(-1 * var(--lazer-marquee-distance) * 1px));
286
+ 100% {
287
+ transform: translateX(-50%);
288
288
  }
289
289
  }
290
290
  `;
@@ -314,15 +314,24 @@ var cleanupMarqueeClones = (marqueeState) => {
314
314
  marqueeState.initialized = false;
315
315
  };
316
316
  var setupMarqueeCss = (settings, state) => {
317
- const scrollWidth = settings.feed.scrollWidth;
318
- const speed = settings.marqueeSpeed ?? 50;
319
- const direction = settings.marqueeDirection ?? "left";
320
- const distance = scrollWidth / 2;
321
- settings.feed.style.setProperty("--lazer-marquee-distance", String(distance));
322
- const duration = distance / speed;
323
- const animationDirection = direction === "right" ? "reverse" : "normal";
324
- settings.feed.style.animation = `lazer-marquee-scroll ${duration}s linear infinite ${animationDirection}`;
325
- settings.feed.style.animationPlayState = state.marqueePaused ? "paused" : "running";
317
+ injectMarqueeKeyframes();
318
+ requestAnimationFrame(() => {
319
+ const scrollWidth = settings.feed.scrollWidth;
320
+ const speed = settings.marqueeSpeed ?? 50;
321
+ const direction = settings.marqueeDirection ?? "left";
322
+ const distance = scrollWidth / 2;
323
+ if (distance <= 0 || speed <= 0) {
324
+ console.warn("[lazer-slider] Invalid marquee values:", { distance, speed, scrollWidth });
325
+ return;
326
+ }
327
+ const duration = distance / speed;
328
+ const animationDirection = direction === "right" ? "reverse" : "normal";
329
+ const animationValue = `lazer-marquee-scroll ${duration}s linear infinite ${animationDirection}`;
330
+ settings.feed.style.animation = "none";
331
+ void settings.feed.offsetWidth;
332
+ settings.feed.style.animation = animationValue;
333
+ settings.feed.style.animationPlayState = state.marqueePaused ? "paused" : "running";
334
+ });
326
335
  };
327
336
  var startMarquee = (settings, state) => {
328
337
  setupMarqueeCss(settings, state);
@@ -332,7 +341,6 @@ var stopMarquee = (state, settings) => {
332
341
  settings.feed.style.animationPlayState = "";
333
342
  settings.feed.style.transform = "";
334
343
  settings.feed.style.willChange = "";
335
- settings.feed.style.removeProperty("--lazer-marquee-distance");
336
344
  };
337
345
  var pauseMarquee = (state, settings) => {
338
346
  state.marqueePaused = true;
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/core/easing.ts","../src/core/accessibility.ts","../src/core/drag.ts","../src/core/marquee.ts","../src/core/slider.ts"],"sourcesContent":["// Main slider factory\nexport { createSlider } from './core/slider.js'\n\n// Types\nexport type {\n SliderSettings,\n SliderState,\n SliderDirection,\n MarqueeDirection,\n ScrollStartParams,\n ScrollParams,\n EasingFunction,\n Slider,\n DragState\n} from './core/types.js'\n\n// Easing functions\nexport {\n easeOutExpo,\n easeOutCubic,\n easeInOutCubic,\n easeOutQuad,\n linear\n} from './core/easing.js'\n","import type { EasingFunction } from './types.js'\n\n/**\n * Exponential ease-out - starts fast, decelerates smoothly\n * Best for scroll animations as it feels natural and responsive\n */\nexport const easeOutExpo: EasingFunction = (t) =>\n t === 1 ? 1 : 1 - Math.pow(2, -10 * t)\n\n/**\n * Cubic ease-out - smoother deceleration than exponential\n */\nexport const easeOutCubic: EasingFunction = (t) => 1 - Math.pow(1 - t, 3)\n\n/**\n * Cubic ease-in-out - smooth acceleration and deceleration\n */\nexport const easeInOutCubic: EasingFunction = (t) =>\n t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2\n\n/**\n * Quadratic ease-out - gentle deceleration\n */\nexport const easeOutQuad: EasingFunction = (t) => 1 - (1 - t) * (1 - t)\n\n/**\n * Linear - no easing, constant speed\n */\nexport const linear: EasingFunction = (t) => t\n","import type { SliderSettings } from './types.js'\n\n/**\n * Generate a unique ID for the slider\n */\nexport const generateSliderId = (): string =>\n `slider-${Math.random().toString(36).substring(2, 9)}`\n\n/**\n * Initialize ARIA attributes for accessibility\n */\nexport const initAria = (settings: SliderSettings): void => {\n const { feed, prevSlideButton, nextSlideButton, thumbs, slides } = settings\n\n // Ensure feed has an ID for aria-controls references\n if (!feed.id) {\n feed.id = generateSliderId()\n }\n\n // Set up feed as a scrollable region\n feed.setAttribute('role', 'region')\n feed.setAttribute('aria-label', 'Carousel')\n feed.setAttribute('aria-roledescription', 'carousel')\n\n // Remove tabindex to allow natural tab order\n feed.removeAttribute('tabindex')\n\n // Set up slides with proper roles\n slides.forEach((slide, index) => {\n slide.setAttribute('role', 'group')\n slide.setAttribute('aria-roledescription', 'slide')\n slide.setAttribute('aria-label', `Slide ${index + 1} of ${slides.length}`)\n })\n\n // Set up previous button\n if (prevSlideButton) {\n prevSlideButton.setAttribute('aria-label', 'Previous slide')\n prevSlideButton.setAttribute('aria-controls', feed.id)\n prevSlideButton.setAttribute('tabindex', '0')\n if (prevSlideButton.tagName !== 'BUTTON') {\n prevSlideButton.setAttribute('role', 'button')\n }\n }\n\n // Set up next button\n if (nextSlideButton) {\n nextSlideButton.setAttribute('aria-label', 'Next slide')\n nextSlideButton.setAttribute('aria-controls', feed.id)\n nextSlideButton.setAttribute('tabindex', '0')\n if (nextSlideButton.tagName !== 'BUTTON') {\n nextSlideButton.setAttribute('role', 'button')\n }\n }\n\n // Set up thumbnail/dot navigation\n if (thumbs?.length) {\n thumbs.forEach((thumb, index) => {\n if (thumb.tagName !== 'BUTTON') {\n thumb.setAttribute('role', 'button')\n }\n thumb.setAttribute('aria-label', `Go to slide ${index + 1}`)\n thumb.setAttribute('tabindex', '0')\n thumb.setAttribute('aria-controls', feed.id)\n })\n }\n}\n\n/**\n * Toggle visibility of navigation controls with proper ARIA handling\n */\nexport const toggleControlVisibility = (\n button: HTMLElement | null | undefined,\n shouldShow: boolean,\n feedElement?: HTMLElement\n): void => {\n if (!button) return\n\n // If hiding the button while it has focus, move focus to feed\n if (!shouldShow && button === document.activeElement && feedElement) {\n feedElement.focus()\n }\n\n // Update visual state with transition\n const transition = 'opacity 0.3s ease'\n const opacity = shouldShow ? '1' : '0'\n\n Object.assign(button.style, {\n opacity,\n transition,\n pointerEvents: shouldShow ? 'auto' : 'none'\n })\n\n // Update ARIA state\n if (!shouldShow) {\n button.setAttribute('aria-hidden', 'true')\n button.setAttribute('tabindex', '-1')\n } else {\n button.removeAttribute('aria-hidden')\n button.setAttribute('tabindex', '0')\n }\n\n // Hide from layout after transition if not visible\n if (!shouldShow) {\n setTimeout(() => {\n if (button.style.opacity === '0') {\n button.style.visibility = 'hidden'\n }\n }, 300)\n } else {\n button.style.visibility = 'visible'\n }\n}\n\n/**\n * Update active state on thumbnail elements\n */\nexport const updateActiveThumb = (\n thumbs: HTMLElement[] | undefined,\n currentIndex: number,\n activeClass: string = 'active'\n): void => {\n if (!thumbs?.length) return\n\n thumbs.forEach((thumb, index) => {\n const isActive = index === currentIndex\n thumb.classList.toggle(activeClass, isActive)\n thumb.setAttribute('aria-selected', isActive.toString())\n })\n}\n\n/**\n * Set up keyboard navigation for the slider\n */\nexport const setupKeyboardNavigation = (\n feed: HTMLElement,\n onPrev: () => void,\n onNext: () => void,\n abortSignal: AbortSignal\n): void => {\n feed.addEventListener(\n 'keydown',\n (event: KeyboardEvent) => {\n switch (event.key) {\n case 'ArrowLeft':\n event.preventDefault()\n onPrev()\n break\n case 'ArrowRight':\n event.preventDefault()\n onNext()\n break\n }\n },\n { signal: abortSignal }\n )\n\n // Make feed focusable for keyboard navigation\n if (!feed.hasAttribute('tabindex')) {\n feed.setAttribute('tabindex', '0')\n }\n}\n","import type { DragState, EasingFunction } from './types.js'\nimport { easeOutCubic } from './easing.js'\n\n/**\n * Configuration for drag behavior\n */\ninterface DragConfig {\n /** Feed element to enable dragging on */\n feed: HTMLElement\n /** Slide elements for snap-to-slide calculation */\n slides: HTMLElement[]\n /** AbortSignal for cleanup */\n abortSignal: AbortSignal\n /** Callback to smooth scroll to a position */\n smoothScrollTo: (target: number, easing?: EasingFunction) => void\n /** Callback when drag ends (for updating state) */\n onDragEnd?: () => void\n}\n\n/**\n * Create initial drag state\n */\nexport const createDragState = (): DragState => ({\n isDragging: false,\n startX: 0,\n startScrollLeft: 0,\n velocity: 0,\n lastX: 0,\n lastTime: 0,\n momentumId: null\n})\n\n/**\n * Find the nearest slide to snap to after drag\n */\nconst findNearestSlide = (\n feed: HTMLElement,\n slides: HTMLElement[]\n): HTMLElement | null => {\n const feedRect = feed.getBoundingClientRect()\n const feedCenter = feedRect.left + feedRect.width / 2\n\n let nearestSlide: HTMLElement | null = null\n let minDistance = Infinity\n\n for (const slide of slides) {\n if (slide.offsetParent === null) continue // Skip hidden slides\n\n const slideRect = slide.getBoundingClientRect()\n const slideCenter = slideRect.left + slideRect.width / 2\n const distance = Math.abs(feedCenter - slideCenter)\n\n if (distance < minDistance) {\n minDistance = distance\n nearestSlide = slide\n }\n }\n\n return nearestSlide\n}\n\n/**\n * Apply momentum scrolling after drag release\n */\nconst applyMomentum = (\n state: DragState,\n feed: HTMLElement,\n slides: HTMLElement[],\n smoothScrollTo: (target: number, easing?: EasingFunction) => void,\n onDragEnd?: () => void\n): void => {\n const friction = 0.95\n const minVelocity = 0.5\n\n const animate = () => {\n if (Math.abs(state.velocity) < minVelocity) {\n state.momentumId = null\n\n // Snap to nearest slide\n const nearestSlide = findNearestSlide(feed, slides)\n if (nearestSlide) {\n smoothScrollTo(nearestSlide.offsetLeft, easeOutCubic)\n }\n\n onDragEnd?.()\n return\n }\n\n feed.scrollLeft += state.velocity\n state.velocity *= friction\n\n state.momentumId = requestAnimationFrame(animate)\n }\n\n state.momentumId = requestAnimationFrame(animate)\n}\n\n/**\n * Get X position from mouse or touch event\n */\nconst getEventX = (event: MouseEvent | TouchEvent): number => {\n if ('touches' in event) {\n return event.touches[0]?.clientX ?? 0\n }\n return event.clientX\n}\n\n/**\n * Set up drag-to-scroll functionality\n */\nexport const setupDragToScroll = (config: DragConfig): DragState => {\n const { feed, slides, abortSignal, smoothScrollTo, onDragEnd } = config\n const state = createDragState()\n\n const handleDragStart = (event: MouseEvent | TouchEvent) => {\n // Cancel any ongoing momentum animation\n if (state.momentumId !== null) {\n cancelAnimationFrame(state.momentumId)\n state.momentumId = null\n }\n\n state.isDragging = true\n state.startX = getEventX(event)\n state.startScrollLeft = feed.scrollLeft\n state.velocity = 0\n state.lastX = state.startX\n state.lastTime = performance.now()\n\n // Visual feedback\n feed.style.cursor = 'grabbing'\n feed.style.userSelect = 'none'\n\n // Prevent default for mouse to avoid text selection\n if (event.type === 'mousedown') {\n event.preventDefault()\n }\n }\n\n const handleDragMove = (event: MouseEvent | TouchEvent) => {\n if (!state.isDragging) return\n\n const currentX = getEventX(event)\n const currentTime = performance.now()\n const deltaX = state.startX - currentX\n const deltaTime = currentTime - state.lastTime\n\n // Update scroll position\n feed.scrollLeft = state.startScrollLeft + deltaX\n\n // Calculate velocity for momentum\n if (deltaTime > 0) {\n state.velocity = (state.lastX - currentX) / deltaTime * 16 // Normalize to ~60fps\n }\n\n state.lastX = currentX\n state.lastTime = currentTime\n\n // Prevent default to stop page scrolling on touch\n if (event.type === 'touchmove') {\n event.preventDefault()\n }\n }\n\n const handleDragEnd = () => {\n if (!state.isDragging) return\n\n state.isDragging = false\n\n // Reset visual feedback\n feed.style.cursor = 'grab'\n feed.style.userSelect = ''\n\n // Apply momentum if velocity is significant\n if (Math.abs(state.velocity) > 1) {\n applyMomentum(state, feed, slides, smoothScrollTo, onDragEnd)\n } else {\n // Snap to nearest slide immediately\n const nearestSlide = findNearestSlide(feed, slides)\n if (nearestSlide) {\n smoothScrollTo(nearestSlide.offsetLeft, easeOutCubic)\n }\n onDragEnd?.()\n }\n }\n\n // Set initial cursor\n feed.style.cursor = 'grab'\n\n // Mouse events\n feed.addEventListener('mousedown', handleDragStart, { signal: abortSignal })\n document.addEventListener('mousemove', handleDragMove, { signal: abortSignal })\n document.addEventListener('mouseup', handleDragEnd, { signal: abortSignal })\n\n // Touch events\n feed.addEventListener('touchstart', handleDragStart, {\n passive: true,\n signal: abortSignal\n })\n feed.addEventListener('touchmove', handleDragMove, {\n passive: false, // Need to prevent default\n signal: abortSignal\n })\n feed.addEventListener('touchend', handleDragEnd, { signal: abortSignal })\n\n // Handle mouse leaving the window\n document.addEventListener('mouseleave', handleDragEnd, { signal: abortSignal })\n\n return state\n}\n\n/**\n * Clean up drag state (cancel any pending animations)\n */\nexport const cleanupDrag = (state: DragState): void => {\n if (state.momentumId !== null) {\n cancelAnimationFrame(state.momentumId)\n state.momentumId = null\n }\n}\n","import type { SliderSettings, SliderState } from './types.js'\n\n/**\n * Marquee state - tracks cloned slides for marquee mode\n */\nexport interface MarqueeState {\n initialized: boolean\n clonedSlides: HTMLElement[]\n styleElement: HTMLStyleElement | null\n}\n\n/**\n * Create initial marquee state\n */\nexport const createMarqueeState = (): MarqueeState => ({\n initialized: false,\n clonedSlides: [],\n styleElement: null\n})\n\n/**\n * Inject CSS keyframes for smooth marquee animation\n */\nconst injectMarqueeKeyframes = (): HTMLStyleElement => {\n // Check if keyframes already exist\n const existingStyle = document.getElementById('lazer-marquee-keyframes')\n if (existingStyle) {\n return existingStyle as HTMLStyleElement\n }\n\n const style = document.createElement('style')\n style.id = 'lazer-marquee-keyframes'\n style.textContent = `\n @keyframes lazer-marquee-scroll {\n from {\n transform: translateX(0);\n }\n to {\n transform: translateX(calc(-1 * var(--lazer-marquee-distance) * 1px));\n }\n }\n `\n document.head.appendChild(style)\n return style\n}\n\n/**\n * Clone all slides and append for seamless marquee looping\n */\nexport const setupMarqueeClones = (\n settings: SliderSettings,\n marqueeState: MarqueeState\n): void => {\n if (marqueeState.initialized) return\n\n // Inject CSS keyframes\n marqueeState.styleElement = injectMarqueeKeyframes()\n\n // Ensure feed has proper CSS for transform animation\n // Note: Parent container should have overflow: hidden for marquee effect\n settings.feed.style.display = 'flex'\n settings.feed.style.willChange = 'transform'\n\n // Clone all original slides and append to create seamless loop\n settings.slides.forEach((slide) => {\n const clone = slide.cloneNode(true) as HTMLElement\n clone.setAttribute('data-lazer-marquee-clone', 'true')\n clone.setAttribute('aria-hidden', 'true')\n settings.feed.appendChild(clone)\n marqueeState.clonedSlides.push(clone)\n })\n\n marqueeState.initialized = true\n}\n\n/**\n * Remove marquee cloned slides from the DOM\n */\nexport const cleanupMarqueeClones = (marqueeState: MarqueeState): void => {\n if (!marqueeState.initialized) return\n\n marqueeState.clonedSlides.forEach((clone) => {\n clone.remove()\n })\n\n marqueeState.clonedSlides = []\n marqueeState.initialized = false\n}\n\n/**\n * Setup marquee CSS animation for smooth hardware-accelerated scrolling\n */\nconst setupMarqueeCss = (\n settings: SliderSettings,\n state: SliderState\n): void => {\n const scrollWidth = settings.feed.scrollWidth\n const speed = settings.marqueeSpeed ?? 50 // pixels per second\n const direction = settings.marqueeDirection ?? 'left'\n const distance = scrollWidth / 2\n\n // Set CSS custom property for the exact pixel distance\n settings.feed.style.setProperty('--lazer-marquee-distance', String(distance))\n\n // Calculate duration: distance / speed\n const duration = distance / speed\n const animationDirection = direction === 'right' ? 'reverse' : 'normal'\n\n // Apply smooth CSS animation with hardware acceleration\n settings.feed.style.animation = `lazer-marquee-scroll ${duration}s linear infinite ${animationDirection}`\n settings.feed.style.animationPlayState = state.marqueePaused ? 'paused' : 'running'\n}\n\n/**\n * Start marquee animation using smooth CSS animations\n */\nexport const startMarquee = (\n settings: SliderSettings,\n state: SliderState\n): void => {\n // Setup CSS animation for smooth hardware-accelerated scrolling\n setupMarqueeCss(settings, state)\n}\n\n/**\n * Stop marquee animation\n */\nexport const stopMarquee = (state: SliderState, settings: SliderSettings): void => {\n // Remove animation\n settings.feed.style.animation = ''\n settings.feed.style.animationPlayState = ''\n\n // Reset transform and CSS custom property\n settings.feed.style.transform = ''\n settings.feed.style.willChange = ''\n settings.feed.style.removeProperty('--lazer-marquee-distance')\n}\n\n/**\n * Pause marquee animation (temporary, resumes on mouse leave)\n */\nexport const pauseMarquee = (state: SliderState, settings: SliderSettings): void => {\n state.marqueePaused = true\n settings.feed.style.animationPlayState = 'paused'\n}\n\n/**\n * Resume marquee animation after pause\n */\nexport const resumeMarquee = (state: SliderState, settings: SliderSettings): void => {\n state.marqueePaused = false\n settings.feed.style.animationPlayState = 'running'\n}\n\n/**\n * Setup marquee mode (clones + animation)\n */\nexport const setupMarquee = (\n settings: SliderSettings,\n state: SliderState,\n marqueeState: MarqueeState\n): void => {\n if (!settings.marquee) return\n\n setupMarqueeClones(settings, marqueeState)\n startMarquee(settings, state)\n}\n\n/**\n * Attach marquee pause-on-hover event listeners\n */\nexport const attachMarqueeEventListeners = (\n settings: SliderSettings,\n state: SliderState,\n signal: AbortSignal\n): void => {\n if (!settings.marquee || settings.pauseOnHover === false) return\n\n settings.feed.addEventListener(\n 'mouseenter',\n () => pauseMarquee(state, settings),\n { signal }\n )\n settings.feed.addEventListener(\n 'mouseleave',\n () => resumeMarquee(state, settings),\n { signal }\n )\n settings.feed.addEventListener(\n 'touchstart',\n () => pauseMarquee(state, settings),\n { passive: true, signal }\n )\n settings.feed.addEventListener(\n 'touchend',\n () => resumeMarquee(state, settings),\n { signal }\n )\n}\n","import type {\n SliderSettings,\n SliderState,\n SliderDirection,\n EasingFunction,\n Slider,\n DragState\n} from './types.js'\nimport { easeOutExpo } from './easing.js'\nimport {\n initAria,\n toggleControlVisibility,\n updateActiveThumb,\n setupKeyboardNavigation\n} from './accessibility.js'\nimport { setupDragToScroll, cleanupDrag } from './drag.js'\nimport {\n createMarqueeState,\n setupMarquee,\n startMarquee,\n stopMarquee,\n pauseMarquee,\n resumeMarquee,\n cleanupMarqueeClones,\n attachMarqueeEventListeners\n} from './marquee.js'\n\n/**\n * Animation duration configuration\n */\nconst ANIMATION = {\n MIN_DURATION: 400,\n MAX_DURATION: 1000,\n SPEED_FACTOR: 1.5,\n SCROLL_END_DELAY: 50,\n THUMB_UPDATE_DELAY: 500\n} as const\n\n/**\n * Desktop breakpoint for responsive slidesPerScroll\n */\nconst DESKTOP_BREAKPOINT = '(min-width: 64rem)'\n\n/**\n * Create a slider instance\n */\nexport const createSlider = (settings: SliderSettings): Slider => {\n // Validate required settings\n if (!settings.feed) {\n throw new Error('lazer-slider: feed element is required')\n }\n\n if (!settings.slides?.length) {\n throw new Error('lazer-slider: slides array is required and must not be empty')\n }\n\n // Initialize state\n const state: SliderState = {\n currentSlideIndex: 0,\n isScrolling: false,\n ticking: false,\n cachedFeedRect: null,\n lastWidth: 0,\n updateThumbTimeout: null,\n scrollEndTimeout: null,\n abortController: new AbortController(),\n autoplayIntervalId: null,\n autoplayPaused: false,\n marqueeAnimationId: null,\n marqueePaused: false,\n marqueeLastTimestamp: 0\n }\n\n // Drag state (initialized if drag is enabled)\n let dragState: DragState | null = null\n\n // Loop state - tracks cloned slides for infinite loop\n const loopState = {\n initialized: false,\n clonedSlides: [] as HTMLElement[],\n realSlides: [...settings.slides] as HTMLElement[],\n clonesPerSide: 0\n }\n\n // Marquee state - tracks cloned slides for marquee mode\n const marqueeState = createMarqueeState()\n\n // Default easing function\n const easing = settings.easing ?? easeOutExpo\n\n /**\n * Get cached feed bounding rect (invalidated on resize)\n */\n const getFeedRect = (): DOMRect => {\n const currentWidth = settings.feed.clientWidth\n if (!state.cachedFeedRect || state.lastWidth !== currentWidth) {\n state.cachedFeedRect = settings.feed.getBoundingClientRect()\n state.lastWidth = currentWidth\n }\n return state.cachedFeedRect\n }\n\n /**\n * Get only visible slides (not hidden via CSS)\n */\n const getVisibleSlides = (): HTMLElement[] => {\n return settings.slides.filter((slide) => slide.offsetParent !== null)\n }\n\n /**\n * Check if we're on desktop based on breakpoint\n */\n const isDesktop = (): boolean => {\n return window.matchMedia(DESKTOP_BREAKPOINT).matches\n }\n\n /**\n * Get the number of slides to clone per side for infinite loop\n */\n const getLoopClonesCount = (): number => {\n const perView = isDesktop()\n ? settings.desktopSlidesPerView\n : settings.mobileSlidesPerView\n\n // Clone at least 1 slide, or the number of visible slides\n if (!perView || perView === 'auto') {\n return 1\n }\n return Math.ceil(perView)\n }\n\n /**\n * Setup cloned slides for infinite loop functionality\n * Clones first N slides to the end and last N slides to the beginning\n */\n const setupLoopClones = (): void => {\n if (!settings.loop || loopState.initialized) return\n\n const realSlides = loopState.realSlides\n const clonesCount = getLoopClonesCount()\n loopState.clonesPerSide = clonesCount\n\n // Clone last N slides and prepend to beginning\n for (let i = realSlides.length - clonesCount; i < realSlides.length; i++) {\n const slide = realSlides[i]\n if (!slide) continue\n\n const clone = slide.cloneNode(true) as HTMLElement\n clone.setAttribute('data-lazer-clone', 'prepend')\n clone.setAttribute('aria-hidden', 'true')\n settings.feed.insertBefore(clone, settings.feed.firstChild)\n loopState.clonedSlides.push(clone)\n }\n\n // Clone first N slides and append to end\n for (let i = 0; i < clonesCount; i++) {\n const slide = realSlides[i]\n if (!slide) continue\n\n const clone = slide.cloneNode(true) as HTMLElement\n clone.setAttribute('data-lazer-clone', 'append')\n clone.setAttribute('aria-hidden', 'true')\n settings.feed.appendChild(clone)\n loopState.clonedSlides.push(clone)\n }\n\n // Set initial scroll position to first real slide (after prepended clones)\n requestAnimationFrame(() => {\n const firstRealSlide = realSlides[0]\n if (firstRealSlide) {\n settings.feed.scrollLeft = firstRealSlide.offsetLeft\n }\n })\n\n loopState.initialized = true\n }\n\n /**\n * Handle repositioning when reaching clone boundaries\n * Silently jumps to the corresponding real slide without animation\n */\n const handleLoopReposition = (direction: SliderDirection): void => {\n if (!settings.loop || !loopState.initialized) return\n\n const realSlides = loopState.realSlides\n const totalRealSlides = realSlides.length\n\n if (direction === 'next') {\n const firstRealSlide = realSlides[0]\n if (firstRealSlide) {\n settings.feed.scrollLeft = firstRealSlide.offsetLeft\n }\n state.currentSlideIndex = 0\n } else {\n const lastRealSlide = realSlides[totalRealSlides - 1]\n if (lastRealSlide) {\n settings.feed.scrollLeft = lastRealSlide.offsetLeft\n }\n state.currentSlideIndex = totalRealSlides - 1\n }\n }\n\n /**\n * Remove cloned slides from the DOM\n */\n const cleanupLoopClones = (): void => {\n if (!loopState.initialized) return\n\n loopState.clonedSlides.forEach((clone) => {\n clone.remove()\n })\n\n loopState.clonedSlides = []\n loopState.initialized = false\n loopState.clonesPerSide = 0\n }\n\n /**\n * Apply slide widths based on slidesPerView settings\n */\n const applySlideWidths = (): void => {\n const perView = isDesktop()\n ? settings.desktopSlidesPerView\n : settings.mobileSlidesPerView\n\n const gap = settings.slideGap ?? 0\n\n // Set gap on the feed container\n if (gap > 0) {\n settings.feed.style.gap = `${gap}px`\n }\n\n // If 'auto' or not set, let CSS handle widths (natural sizing)\n if (!perView || perView === 'auto') {\n // Reset any previously set widths\n settings.slides.forEach((slide) => {\n slide.style.flex = ''\n slide.style.minWidth = ''\n })\n return\n }\n\n const totalGapWidth = gap * (perView - 1)\n const slideWidth = `calc((100% - ${totalGapWidth}px) / ${perView})`\n\n settings.slides.forEach((slide) => {\n slide.style.flex = `0 0 ${slideWidth}`\n slide.style.minWidth = slideWidth\n })\n }\n\n /**\n * Update scrollbar thumb width based on visible content\n */\n const updateScrollbar = (): void => {\n if (!settings.scrollbarThumb) return\n\n const feedRect = getFeedRect()\n const thumbWidth = (feedRect.width / settings.feed.scrollWidth) * 100\n settings.scrollbarThumb.style.width = `${thumbWidth}%`\n }\n\n /**\n * Update scrollbar thumb position based on scroll\n */\n const updateScrollbarPosition = (): void => {\n if (!settings.scrollbarThumb || !settings.scrollbarTrack) return\n\n const trackWidth = settings.scrollbarTrack.getBoundingClientRect().width\n const thumbWidth = settings.scrollbarThumb.getBoundingClientRect().width\n const totalTransform = trackWidth - thumbWidth\n\n const maxScroll = settings.feed.scrollWidth - settings.feed.clientWidth\n const scrollProgress = maxScroll > 0 ? settings.feed.scrollLeft / maxScroll : 0\n\n settings.scrollbarThumb.style.transform = `translateX(${totalTransform * scrollProgress}px)`\n }\n\n /**\n * Update visibility of navigation controls based on scroll position\n */\n const updateControlsVisibility = (): void => {\n const feedRect = getFeedRect()\n const isAtStart = settings.feed.scrollLeft <= 1 // Allow 1px tolerance\n const isAtEnd =\n settings.feed.scrollLeft + feedRect.width >= settings.feed.scrollWidth - 1\n const shouldHideScrollbar = settings.feed.scrollWidth <= feedRect.width\n\n // Hide/show scrollbar track\n if (settings.scrollbarTrack) {\n settings.scrollbarTrack.style.display = shouldHideScrollbar ? 'none' : 'block'\n }\n\n // When loop is enabled, always show both buttons (unless scrollbar should be hidden)\n if (settings.loop) {\n toggleControlVisibility(\n settings.prevSlideButton,\n !shouldHideScrollbar,\n settings.feed\n )\n toggleControlVisibility(\n settings.nextSlideButton,\n !shouldHideScrollbar,\n settings.feed\n )\n return\n }\n\n // Hide/show navigation buttons based on position\n toggleControlVisibility(\n settings.prevSlideButton,\n !isAtStart && !shouldHideScrollbar,\n settings.feed\n )\n toggleControlVisibility(\n settings.nextSlideButton,\n !isAtEnd && !shouldHideScrollbar,\n settings.feed\n )\n }\n\n /**\n * Calculate current slide index based on viewport position\n */\n const updateCurrentSlideIndex = (): void => {\n const feedRect = getFeedRect()\n\n // Use real slides for index calculation when loop is enabled\n const slidesToCheck = loopState.initialized\n ? loopState.realSlides\n : getVisibleSlides()\n\n // Find slides that are visible in the viewport\n const viewportVisibleSlides = slidesToCheck.filter((slide) => {\n const slideRect = slide.getBoundingClientRect()\n const tolerance = 20 // px tolerance for edge detection\n return (\n slideRect.right > feedRect.left + tolerance &&\n slideRect.left < feedRect.right - tolerance\n )\n })\n\n if (viewportVisibleSlides.length && viewportVisibleSlides[0]) {\n const newIndex = slidesToCheck.indexOf(viewportVisibleSlides[0])\n if (newIndex !== -1) {\n state.currentSlideIndex = newIndex\n settings.onScroll?.({\n currentScroll: settings.feed.scrollLeft,\n currentSlideIndex: state.currentSlideIndex\n })\n }\n }\n }\n\n /**\n * Smooth scroll to a target position using requestAnimationFrame\n */\n const smoothScrollTo = (\n target: number,\n customEasing: EasingFunction = easing,\n onComplete?: () => void\n ): void => {\n const start = settings.feed.scrollLeft\n const distance = Math.abs(target - start)\n\n // Dynamic duration based on distance\n const duration = Math.min(\n ANIMATION.MAX_DURATION,\n Math.max(ANIMATION.MIN_DURATION, distance / ANIMATION.SPEED_FACTOR)\n )\n\n const startTime = performance.now()\n\n const animateScroll = (currentTime: number): void => {\n const elapsed = (currentTime - startTime) / duration\n const progress = Math.min(elapsed, 1)\n const ease = customEasing(progress)\n\n settings.feed.scrollLeft = start + (target - start) * ease\n\n if (progress < 1) {\n requestAnimationFrame(animateScroll)\n } else {\n settings.feed.scrollLeft = target\n onComplete?.()\n }\n }\n\n requestAnimationFrame(animateScroll)\n }\n\n /**\n * Handle thumbnail click navigation\n */\n const handleThumbClick = (thumb: HTMLElement): void => {\n if (!settings.thumbs) return\n\n const index = settings.thumbs.indexOf(thumb)\n if (index === -1 || !settings.slides[index]) return\n\n state.currentSlideIndex = index\n updateActiveThumb(settings.thumbs, index)\n state.isScrolling = true\n\n // Clear existing timeout\n if (state.updateThumbTimeout) {\n clearTimeout(state.updateThumbTimeout)\n }\n\n // Reset scrolling flag after animation\n state.updateThumbTimeout = setTimeout(() => {\n state.isScrolling = false\n }, ANIMATION.THUMB_UPDATE_DELAY)\n\n smoothScrollTo(settings.slides[index].offsetLeft)\n }\n\n /**\n * Handle navigation button click (prev/next)\n */\n const handleNavButtonClick = (direction: SliderDirection): void => {\n const realSlides = loopState.initialized ? loopState.realSlides : getVisibleSlides()\n const slidesToScroll = isDesktop()\n ? settings.desktopSlidesPerScroll ?? 1\n : settings.mobileSlidesPerScroll ?? 1\n const totalRealSlides = realSlides.length\n\n // Update current index first\n updateCurrentSlideIndex()\n\n let targetSlide: HTMLElement | undefined\n let needsReposition = false\n\n if (direction === 'prev') {\n if (settings.loop && loopState.initialized && state.currentSlideIndex === 0) {\n\n const prependedClones = loopState.clonedSlides.filter(\n (clone) => clone.getAttribute('data-lazer-clone') === 'prepend'\n )\n targetSlide = prependedClones[prependedClones.length - 1]\n needsReposition = true\n } else {\n state.currentSlideIndex = Math.max(0, state.currentSlideIndex - slidesToScroll)\n targetSlide = realSlides[state.currentSlideIndex]\n }\n } else {\n if (settings.loop && loopState.initialized && state.currentSlideIndex >= totalRealSlides - 1) {\n const appendedClones = loopState.clonedSlides.filter(\n (clone) => clone.getAttribute('data-lazer-clone') === 'append'\n )\n targetSlide = appendedClones[0]\n needsReposition = true\n } else {\n state.currentSlideIndex = Math.min(\n totalRealSlides - 1,\n state.currentSlideIndex + slidesToScroll\n )\n targetSlide = realSlides[state.currentSlideIndex]\n }\n }\n\n if (!targetSlide) return\n\n // Fire scroll start callback\n settings.onScrollStart?.({\n currentScroll: settings.feed.scrollLeft,\n target: targetSlide,\n direction\n })\n\n if (needsReposition) {\n // Scroll to clone, then silently reposition to the real slide\n smoothScrollTo(targetSlide.offsetLeft, easing, () => {\n handleLoopReposition(direction)\n })\n } else {\n smoothScrollTo(targetSlide.offsetLeft)\n }\n }\n\n /**\n * Update all scroll-related state\n */\n const updateScrollPosition = (): void => {\n updateScrollbarPosition()\n updateControlsVisibility()\n updateCurrentSlideIndex()\n\n // Update thumbs only when not programmatically scrolling\n if (!state.isScrolling) {\n updateActiveThumb(settings.thumbs, state.currentSlideIndex)\n }\n\n // Clear existing timeout\n if (state.scrollEndTimeout) {\n clearTimeout(state.scrollEndTimeout)\n }\n\n // Fire scroll end callback after scroll settles\n state.scrollEndTimeout = setTimeout(() => {\n state.isScrolling = false\n settings.onScrollEnd?.({\n currentScroll: settings.feed.scrollLeft,\n currentSlideIndex: state.currentSlideIndex\n })\n }, ANIMATION.SCROLL_END_DELAY)\n }\n\n /**\n * Throttled scroll event handler\n */\n const handleFeedScroll = (): void => {\n if (!state.ticking) {\n requestAnimationFrame(() => {\n updateScrollPosition()\n state.ticking = false\n })\n state.ticking = true\n }\n }\n\n /**\n * Handle window resize\n */\n const handleWindowResize = (): void => {\n state.cachedFeedRect = null\n refresh()\n }\n\n /**\n * Attach all event listeners\n */\n const attachEventListeners = (): void => {\n const { signal } = state.abortController\n\n // Resize handler (not abortable, cleaned up manually)\n window.addEventListener('resize', handleWindowResize)\n\n // Scroll handler\n settings.feed.addEventListener('scroll', handleFeedScroll, {\n passive: true,\n signal\n })\n\n // Navigation button handlers\n if (settings.prevSlideButton) {\n settings.prevSlideButton.addEventListener(\n 'click',\n () => handleNavButtonClick('prev'),\n { signal }\n )\n }\n\n if (settings.nextSlideButton) {\n settings.nextSlideButton.addEventListener(\n 'click',\n () => handleNavButtonClick('next'),\n { signal }\n )\n }\n\n // Thumbnail handlers\n if (settings.thumbs?.length) {\n settings.thumbs[0]?.classList.add('active')\n settings.thumbs.forEach((thumb) => {\n thumb.addEventListener('click', () => handleThumbClick(thumb), { signal })\n })\n }\n\n // Keyboard navigation\n setupKeyboardNavigation(\n settings.feed,\n () => handleNavButtonClick('prev'),\n () => handleNavButtonClick('next'),\n signal\n )\n\n // Drag-to-scroll\n if (settings.enableDragToScroll) {\n dragState = setupDragToScroll({\n feed: settings.feed,\n slides: settings.slides,\n abortSignal: signal,\n smoothScrollTo,\n onDragEnd: () => {\n updateCurrentSlideIndex()\n updateActiveThumb(settings.thumbs, state.currentSlideIndex)\n }\n })\n }\n\n // Autoplay pause on hover/touch\n if (settings.autoplay && settings.pauseOnHover !== false) {\n settings.feed.addEventListener(\n 'mouseenter',\n pauseAutoplay,\n { signal }\n )\n settings.feed.addEventListener(\n 'mouseleave',\n resumeAutoplay,\n { signal }\n )\n settings.feed.addEventListener(\n 'touchstart',\n pauseAutoplay,\n { passive: true, signal }\n )\n settings.feed.addEventListener(\n 'touchend',\n resumeAutoplay,\n { signal }\n )\n }\n\n // Marquee pause on hover/touch\n attachMarqueeEventListeners(settings, state, signal)\n }\n\n /**\n * Start autoplay\n */\n const startAutoplay = (): void => {\n if (state.autoplayIntervalId) return;\n\n const interval = settings.autoplayInterval ?? 3000\n state.autoplayIntervalId = setInterval(() => {\n if (!state.autoplayPaused) {\n handleNavButtonClick('next')\n }\n }, interval)\n }\n\n /**\n * Stop autoplay\n */\n const stopAutoplay = (): void => {\n if (state.autoplayIntervalId) {\n clearInterval(state.autoplayIntervalId)\n state.autoplayIntervalId = null\n }\n }\n\n /**\n * Pause autoplay (temporary, resumes on mouse leave)\n */\n const pauseAutoplay = (): void => {\n state.autoplayPaused = true\n }\n\n /**\n * Resume autoplay after pause\n */\n const resumeAutoplay = (): void => {\n state.autoplayPaused = false\n }\n\n /**\n * Navigate to a specific slide by index\n */\n const goToIndex = (index: number): void => {\n // Use real slides when loop is enabled, otherwise use visible slides\n const slides = loopState.initialized ? loopState.realSlides : getVisibleSlides()\n const safeIndex = Math.max(0, Math.min(index, slides.length - 1))\n const targetSlide = slides[safeIndex]\n\n if (!targetSlide) return\n\n state.currentSlideIndex = safeIndex\n updateActiveThumb(settings.thumbs, safeIndex)\n smoothScrollTo(targetSlide.offsetLeft)\n }\n\n /**\n * Refresh slider state (recalculate dimensions, update controls)\n */\n const refresh = (): void => {\n state.cachedFeedRect = null\n applySlideWidths()\n updateScrollbar()\n updateControlsVisibility()\n\n // Recalculate marquee animation if in marquee mode\n if (settings.marquee && !state.marqueePaused) {\n startMarquee(settings, state)\n }\n }\n\n /**\n * Clean up all event listeners and timers\n */\n const unload = (): void => {\n // Stop autoplay and marquee\n stopAutoplay()\n stopMarquee(state, settings)\n\n // Abort all event listeners\n state.abortController.abort()\n\n // Remove resize listener\n window.removeEventListener('resize', handleWindowResize)\n\n // Clear timeouts\n if (state.updateThumbTimeout) {\n clearTimeout(state.updateThumbTimeout)\n }\n if (state.scrollEndTimeout) {\n clearTimeout(state.scrollEndTimeout)\n }\n\n // Clean up drag state\n if (dragState) {\n cleanupDrag(dragState)\n }\n\n // Clean up loop clones and marquee clones\n cleanupLoopClones()\n cleanupMarqueeClones(marqueeState)\n\n // Reset cached rect\n state.cachedFeedRect = null\n }\n\n // Initialize slider\n initAria(settings)\n applySlideWidths()\n\n // Marquee mode takes precedence over loop/autoplay\n if (settings.marquee) {\n setupMarquee(settings, state, marqueeState)\n } else {\n setupLoopClones()\n // Start autoplay if enabled (only in non-marquee mode)\n if (settings.autoplay) {\n startAutoplay()\n }\n }\n\n updateControlsVisibility()\n attachEventListeners()\n updateScrollbar()\n\n // Unified play/pause that works for both autoplay and marquee\n const play = (): void => {\n if (settings.marquee) {\n if (state.marqueePaused) {\n resumeMarquee(state, settings)\n } else {\n startMarquee(settings, state)\n }\n } else if (settings.autoplay) {\n startAutoplay()\n }\n }\n\n const pause = (): void => {\n if (settings.marquee) {\n pauseMarquee(state, settings)\n } else {\n stopAutoplay()\n }\n }\n\n return {\n goToIndex,\n refresh,\n unload,\n play,\n pause,\n next: () => handleNavButtonClick('next'),\n prev: () => handleNavButtonClick('prev')\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/index.ts","../src/core/easing.ts","../src/core/accessibility.ts","../src/core/drag.ts","../src/core/marquee.ts","../src/core/slider.ts"],"sourcesContent":["// Main slider factory\nexport { createSlider } from './core/slider.js'\n\n// Types\nexport type {\n SliderSettings,\n SliderState,\n SliderDirection,\n MarqueeDirection,\n ScrollStartParams,\n ScrollParams,\n EasingFunction,\n Slider,\n DragState\n} from './core/types.js'\n\n// Easing functions\nexport {\n easeOutExpo,\n easeOutCubic,\n easeInOutCubic,\n easeOutQuad,\n linear\n} from './core/easing.js'\n","import type { EasingFunction } from './types.js'\n\n/**\n * Exponential ease-out - starts fast, decelerates smoothly\n * Best for scroll animations as it feels natural and responsive\n */\nexport const easeOutExpo: EasingFunction = (t) =>\n t === 1 ? 1 : 1 - Math.pow(2, -10 * t)\n\n/**\n * Cubic ease-out - smoother deceleration than exponential\n */\nexport const easeOutCubic: EasingFunction = (t) => 1 - Math.pow(1 - t, 3)\n\n/**\n * Cubic ease-in-out - smooth acceleration and deceleration\n */\nexport const easeInOutCubic: EasingFunction = (t) =>\n t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2\n\n/**\n * Quadratic ease-out - gentle deceleration\n */\nexport const easeOutQuad: EasingFunction = (t) => 1 - (1 - t) * (1 - t)\n\n/**\n * Linear - no easing, constant speed\n */\nexport const linear: EasingFunction = (t) => t\n","import type { SliderSettings } from './types.js'\n\n/**\n * Generate a unique ID for the slider\n */\nexport const generateSliderId = (): string =>\n `slider-${Math.random().toString(36).substring(2, 9)}`\n\n/**\n * Initialize ARIA attributes for accessibility\n */\nexport const initAria = (settings: SliderSettings): void => {\n const { feed, prevSlideButton, nextSlideButton, thumbs, slides } = settings\n\n // Ensure feed has an ID for aria-controls references\n if (!feed.id) {\n feed.id = generateSliderId()\n }\n\n // Set up feed as a scrollable region\n feed.setAttribute('role', 'region')\n feed.setAttribute('aria-label', 'Carousel')\n feed.setAttribute('aria-roledescription', 'carousel')\n\n // Remove tabindex to allow natural tab order\n feed.removeAttribute('tabindex')\n\n // Set up slides with proper roles\n slides.forEach((slide, index) => {\n slide.setAttribute('role', 'group')\n slide.setAttribute('aria-roledescription', 'slide')\n slide.setAttribute('aria-label', `Slide ${index + 1} of ${slides.length}`)\n })\n\n // Set up previous button\n if (prevSlideButton) {\n prevSlideButton.setAttribute('aria-label', 'Previous slide')\n prevSlideButton.setAttribute('aria-controls', feed.id)\n prevSlideButton.setAttribute('tabindex', '0')\n if (prevSlideButton.tagName !== 'BUTTON') {\n prevSlideButton.setAttribute('role', 'button')\n }\n }\n\n // Set up next button\n if (nextSlideButton) {\n nextSlideButton.setAttribute('aria-label', 'Next slide')\n nextSlideButton.setAttribute('aria-controls', feed.id)\n nextSlideButton.setAttribute('tabindex', '0')\n if (nextSlideButton.tagName !== 'BUTTON') {\n nextSlideButton.setAttribute('role', 'button')\n }\n }\n\n // Set up thumbnail/dot navigation\n if (thumbs?.length) {\n thumbs.forEach((thumb, index) => {\n if (thumb.tagName !== 'BUTTON') {\n thumb.setAttribute('role', 'button')\n }\n thumb.setAttribute('aria-label', `Go to slide ${index + 1}`)\n thumb.setAttribute('tabindex', '0')\n thumb.setAttribute('aria-controls', feed.id)\n })\n }\n}\n\n/**\n * Toggle visibility of navigation controls with proper ARIA handling\n */\nexport const toggleControlVisibility = (\n button: HTMLElement | null | undefined,\n shouldShow: boolean,\n feedElement?: HTMLElement\n): void => {\n if (!button) return\n\n // If hiding the button while it has focus, move focus to feed\n if (!shouldShow && button === document.activeElement && feedElement) {\n feedElement.focus()\n }\n\n // Update visual state with transition\n const transition = 'opacity 0.3s ease'\n const opacity = shouldShow ? '1' : '0'\n\n Object.assign(button.style, {\n opacity,\n transition,\n pointerEvents: shouldShow ? 'auto' : 'none'\n })\n\n // Update ARIA state\n if (!shouldShow) {\n button.setAttribute('aria-hidden', 'true')\n button.setAttribute('tabindex', '-1')\n } else {\n button.removeAttribute('aria-hidden')\n button.setAttribute('tabindex', '0')\n }\n\n // Hide from layout after transition if not visible\n if (!shouldShow) {\n setTimeout(() => {\n if (button.style.opacity === '0') {\n button.style.visibility = 'hidden'\n }\n }, 300)\n } else {\n button.style.visibility = 'visible'\n }\n}\n\n/**\n * Update active state on thumbnail elements\n */\nexport const updateActiveThumb = (\n thumbs: HTMLElement[] | undefined,\n currentIndex: number,\n activeClass: string = 'active'\n): void => {\n if (!thumbs?.length) return\n\n thumbs.forEach((thumb, index) => {\n const isActive = index === currentIndex\n thumb.classList.toggle(activeClass, isActive)\n thumb.setAttribute('aria-selected', isActive.toString())\n })\n}\n\n/**\n * Set up keyboard navigation for the slider\n */\nexport const setupKeyboardNavigation = (\n feed: HTMLElement,\n onPrev: () => void,\n onNext: () => void,\n abortSignal: AbortSignal\n): void => {\n feed.addEventListener(\n 'keydown',\n (event: KeyboardEvent) => {\n switch (event.key) {\n case 'ArrowLeft':\n event.preventDefault()\n onPrev()\n break\n case 'ArrowRight':\n event.preventDefault()\n onNext()\n break\n }\n },\n { signal: abortSignal }\n )\n\n // Make feed focusable for keyboard navigation\n if (!feed.hasAttribute('tabindex')) {\n feed.setAttribute('tabindex', '0')\n }\n}\n","import type { DragState, EasingFunction } from './types.js'\nimport { easeOutCubic } from './easing.js'\n\n/**\n * Configuration for drag behavior\n */\ninterface DragConfig {\n /** Feed element to enable dragging on */\n feed: HTMLElement\n /** Slide elements for snap-to-slide calculation */\n slides: HTMLElement[]\n /** AbortSignal for cleanup */\n abortSignal: AbortSignal\n /** Callback to smooth scroll to a position */\n smoothScrollTo: (target: number, easing?: EasingFunction) => void\n /** Callback when drag ends (for updating state) */\n onDragEnd?: () => void\n}\n\n/**\n * Create initial drag state\n */\nexport const createDragState = (): DragState => ({\n isDragging: false,\n startX: 0,\n startScrollLeft: 0,\n velocity: 0,\n lastX: 0,\n lastTime: 0,\n momentumId: null\n})\n\n/**\n * Find the nearest slide to snap to after drag\n */\nconst findNearestSlide = (\n feed: HTMLElement,\n slides: HTMLElement[]\n): HTMLElement | null => {\n const feedRect = feed.getBoundingClientRect()\n const feedCenter = feedRect.left + feedRect.width / 2\n\n let nearestSlide: HTMLElement | null = null\n let minDistance = Infinity\n\n for (const slide of slides) {\n if (slide.offsetParent === null) continue // Skip hidden slides\n\n const slideRect = slide.getBoundingClientRect()\n const slideCenter = slideRect.left + slideRect.width / 2\n const distance = Math.abs(feedCenter - slideCenter)\n\n if (distance < minDistance) {\n minDistance = distance\n nearestSlide = slide\n }\n }\n\n return nearestSlide\n}\n\n/**\n * Apply momentum scrolling after drag release\n */\nconst applyMomentum = (\n state: DragState,\n feed: HTMLElement,\n slides: HTMLElement[],\n smoothScrollTo: (target: number, easing?: EasingFunction) => void,\n onDragEnd?: () => void\n): void => {\n const friction = 0.95\n const minVelocity = 0.5\n\n const animate = () => {\n if (Math.abs(state.velocity) < minVelocity) {\n state.momentumId = null\n\n // Snap to nearest slide\n const nearestSlide = findNearestSlide(feed, slides)\n if (nearestSlide) {\n smoothScrollTo(nearestSlide.offsetLeft, easeOutCubic)\n }\n\n onDragEnd?.()\n return\n }\n\n feed.scrollLeft += state.velocity\n state.velocity *= friction\n\n state.momentumId = requestAnimationFrame(animate)\n }\n\n state.momentumId = requestAnimationFrame(animate)\n}\n\n/**\n * Get X position from mouse or touch event\n */\nconst getEventX = (event: MouseEvent | TouchEvent): number => {\n if ('touches' in event) {\n return event.touches[0]?.clientX ?? 0\n }\n return event.clientX\n}\n\n/**\n * Set up drag-to-scroll functionality\n */\nexport const setupDragToScroll = (config: DragConfig): DragState => {\n const { feed, slides, abortSignal, smoothScrollTo, onDragEnd } = config\n const state = createDragState()\n\n const handleDragStart = (event: MouseEvent | TouchEvent) => {\n // Cancel any ongoing momentum animation\n if (state.momentumId !== null) {\n cancelAnimationFrame(state.momentumId)\n state.momentumId = null\n }\n\n state.isDragging = true\n state.startX = getEventX(event)\n state.startScrollLeft = feed.scrollLeft\n state.velocity = 0\n state.lastX = state.startX\n state.lastTime = performance.now()\n\n // Visual feedback\n feed.style.cursor = 'grabbing'\n feed.style.userSelect = 'none'\n\n // Prevent default for mouse to avoid text selection\n if (event.type === 'mousedown') {\n event.preventDefault()\n }\n }\n\n const handleDragMove = (event: MouseEvent | TouchEvent) => {\n if (!state.isDragging) return\n\n const currentX = getEventX(event)\n const currentTime = performance.now()\n const deltaX = state.startX - currentX\n const deltaTime = currentTime - state.lastTime\n\n // Update scroll position\n feed.scrollLeft = state.startScrollLeft + deltaX\n\n // Calculate velocity for momentum\n if (deltaTime > 0) {\n state.velocity = (state.lastX - currentX) / deltaTime * 16 // Normalize to ~60fps\n }\n\n state.lastX = currentX\n state.lastTime = currentTime\n\n // Prevent default to stop page scrolling on touch\n if (event.type === 'touchmove') {\n event.preventDefault()\n }\n }\n\n const handleDragEnd = () => {\n if (!state.isDragging) return\n\n state.isDragging = false\n\n // Reset visual feedback\n feed.style.cursor = 'grab'\n feed.style.userSelect = ''\n\n // Apply momentum if velocity is significant\n if (Math.abs(state.velocity) > 1) {\n applyMomentum(state, feed, slides, smoothScrollTo, onDragEnd)\n } else {\n // Snap to nearest slide immediately\n const nearestSlide = findNearestSlide(feed, slides)\n if (nearestSlide) {\n smoothScrollTo(nearestSlide.offsetLeft, easeOutCubic)\n }\n onDragEnd?.()\n }\n }\n\n // Set initial cursor\n feed.style.cursor = 'grab'\n\n // Mouse events\n feed.addEventListener('mousedown', handleDragStart, { signal: abortSignal })\n document.addEventListener('mousemove', handleDragMove, { signal: abortSignal })\n document.addEventListener('mouseup', handleDragEnd, { signal: abortSignal })\n\n // Touch events\n feed.addEventListener('touchstart', handleDragStart, {\n passive: true,\n signal: abortSignal\n })\n feed.addEventListener('touchmove', handleDragMove, {\n passive: false, // Need to prevent default\n signal: abortSignal\n })\n feed.addEventListener('touchend', handleDragEnd, { signal: abortSignal })\n\n // Handle mouse leaving the window\n document.addEventListener('mouseleave', handleDragEnd, { signal: abortSignal })\n\n return state\n}\n\n/**\n * Clean up drag state (cancel any pending animations)\n */\nexport const cleanupDrag = (state: DragState): void => {\n if (state.momentumId !== null) {\n cancelAnimationFrame(state.momentumId)\n state.momentumId = null\n }\n}\n","import type { SliderSettings, SliderState } from './types.js'\n\n/**\n * Marquee state - tracks cloned slides for marquee mode\n */\nexport interface MarqueeState {\n initialized: boolean\n clonedSlides: HTMLElement[]\n styleElement: HTMLStyleElement | null\n}\n\n/**\n * Create initial marquee state\n */\nexport const createMarqueeState = (): MarqueeState => ({\n initialized: false,\n clonedSlides: [],\n styleElement: null\n})\n\n/**\n * Inject CSS keyframes for smooth marquee animation\n */\nconst injectMarqueeKeyframes = (): HTMLStyleElement => {\n // Check if keyframes already exist\n const existingStyle = document.getElementById('lazer-marquee-keyframes')\n if (existingStyle) {\n return existingStyle as HTMLStyleElement\n }\n\n const style = document.createElement('style')\n style.id = 'lazer-marquee-keyframes'\n style.textContent = `\n @keyframes lazer-marquee-scroll {\n 0% {\n transform: translateX(0);\n }\n 100% {\n transform: translateX(-50%);\n }\n }\n `\n document.head.appendChild(style)\n return style\n}\n\n/**\n * Clone all slides and append for seamless marquee looping\n */\nexport const setupMarqueeClones = (\n settings: SliderSettings,\n marqueeState: MarqueeState\n): void => {\n if (marqueeState.initialized) return\n\n // Inject CSS keyframes\n marqueeState.styleElement = injectMarqueeKeyframes()\n\n // Ensure feed has proper CSS for transform animation\n // Note: Parent container should have overflow: hidden for marquee effect\n settings.feed.style.display = 'flex'\n settings.feed.style.willChange = 'transform'\n\n // Clone all original slides and append to create seamless loop\n settings.slides.forEach((slide) => {\n const clone = slide.cloneNode(true) as HTMLElement\n clone.setAttribute('data-lazer-marquee-clone', 'true')\n clone.setAttribute('aria-hidden', 'true')\n settings.feed.appendChild(clone)\n marqueeState.clonedSlides.push(clone)\n })\n\n marqueeState.initialized = true\n}\n\n/**\n * Remove marquee cloned slides from the DOM\n */\nexport const cleanupMarqueeClones = (marqueeState: MarqueeState): void => {\n if (!marqueeState.initialized) return\n\n marqueeState.clonedSlides.forEach((clone) => {\n clone.remove()\n })\n\n marqueeState.clonedSlides = []\n marqueeState.initialized = false\n}\n\n/**\n * Setup marquee CSS animation for smooth hardware-accelerated scrolling\n */\nconst setupMarqueeCss = (\n settings: SliderSettings,\n state: SliderState\n): void => {\n // Ensure keyframes exist first\n injectMarqueeKeyframes()\n\n // Wait a frame to ensure keyframes are parsed and layout is complete\n requestAnimationFrame(() => {\n const scrollWidth = settings.feed.scrollWidth\n const speed = settings.marqueeSpeed ?? 50 // pixels per second\n const direction = settings.marqueeDirection ?? 'left'\n\n // Distance is half the scroll width (the original content width)\n const distance = scrollWidth / 2\n\n // Validate values\n if (distance <= 0 || speed <= 0) {\n console.warn('[lazer-slider] Invalid marquee values:', { distance, speed, scrollWidth })\n return\n }\n\n // Calculate duration: distance / speed (how long to scroll through original content)\n const duration = distance / speed\n const animationDirection = direction === 'right' ? 'reverse' : 'normal'\n\n // Apply animation - use percentage-based keyframes for seamless loop\n const animationValue = `lazer-marquee-scroll ${duration}s linear infinite ${animationDirection}`\n\n // Remove any existing animation first\n settings.feed.style.animation = 'none'\n\n // Force reflow to reset animation\n void settings.feed.offsetWidth\n\n // Apply new animation\n settings.feed.style.animation = animationValue\n settings.feed.style.animationPlayState = state.marqueePaused ? 'paused' : 'running'\n })\n}\n\n/**\n * Start marquee animation using smooth CSS animations\n */\nexport const startMarquee = (\n settings: SliderSettings,\n state: SliderState\n): void => {\n // Setup CSS animation for smooth hardware-accelerated scrolling\n setupMarqueeCss(settings, state)\n}\n\n/**\n * Stop marquee animation\n */\nexport const stopMarquee = (state: SliderState, settings: SliderSettings): void => {\n // Remove animation\n settings.feed.style.animation = ''\n settings.feed.style.animationPlayState = ''\n\n // Reset transform\n settings.feed.style.transform = ''\n settings.feed.style.willChange = ''\n}\n\n/**\n * Pause marquee animation (temporary, resumes on mouse leave)\n */\nexport const pauseMarquee = (state: SliderState, settings: SliderSettings): void => {\n state.marqueePaused = true\n settings.feed.style.animationPlayState = 'paused'\n}\n\n/**\n * Resume marquee animation after pause\n */\nexport const resumeMarquee = (state: SliderState, settings: SliderSettings): void => {\n state.marqueePaused = false\n settings.feed.style.animationPlayState = 'running'\n}\n\n/**\n * Setup marquee mode (clones + animation)\n */\nexport const setupMarquee = (\n settings: SliderSettings,\n state: SliderState,\n marqueeState: MarqueeState\n): void => {\n if (!settings.marquee) return\n\n setupMarqueeClones(settings, marqueeState)\n startMarquee(settings, state)\n}\n\n/**\n * Attach marquee pause-on-hover event listeners\n */\nexport const attachMarqueeEventListeners = (\n settings: SliderSettings,\n state: SliderState,\n signal: AbortSignal\n): void => {\n if (!settings.marquee || settings.pauseOnHover === false) return\n\n settings.feed.addEventListener(\n 'mouseenter',\n () => pauseMarquee(state, settings),\n { signal }\n )\n settings.feed.addEventListener(\n 'mouseleave',\n () => resumeMarquee(state, settings),\n { signal }\n )\n settings.feed.addEventListener(\n 'touchstart',\n () => pauseMarquee(state, settings),\n { passive: true, signal }\n )\n settings.feed.addEventListener(\n 'touchend',\n () => resumeMarquee(state, settings),\n { signal }\n )\n}\n","import type {\n SliderSettings,\n SliderState,\n SliderDirection,\n EasingFunction,\n Slider,\n DragState\n} from './types.js'\nimport { easeOutExpo } from './easing.js'\nimport {\n initAria,\n toggleControlVisibility,\n updateActiveThumb,\n setupKeyboardNavigation\n} from './accessibility.js'\nimport { setupDragToScroll, cleanupDrag } from './drag.js'\nimport {\n createMarqueeState,\n setupMarquee,\n startMarquee,\n stopMarquee,\n pauseMarquee,\n resumeMarquee,\n cleanupMarqueeClones,\n attachMarqueeEventListeners\n} from './marquee.js'\n\n/**\n * Animation duration configuration\n */\nconst ANIMATION = {\n MIN_DURATION: 400,\n MAX_DURATION: 1000,\n SPEED_FACTOR: 1.5,\n SCROLL_END_DELAY: 50,\n THUMB_UPDATE_DELAY: 500\n} as const\n\n/**\n * Desktop breakpoint for responsive slidesPerScroll\n */\nconst DESKTOP_BREAKPOINT = '(min-width: 64rem)'\n\n/**\n * Create a slider instance\n */\nexport const createSlider = (settings: SliderSettings): Slider => {\n // Validate required settings\n if (!settings.feed) {\n throw new Error('lazer-slider: feed element is required')\n }\n\n if (!settings.slides?.length) {\n throw new Error('lazer-slider: slides array is required and must not be empty')\n }\n\n // Initialize state\n const state: SliderState = {\n currentSlideIndex: 0,\n isScrolling: false,\n ticking: false,\n cachedFeedRect: null,\n lastWidth: 0,\n updateThumbTimeout: null,\n scrollEndTimeout: null,\n abortController: new AbortController(),\n autoplayIntervalId: null,\n autoplayPaused: false,\n marqueeAnimationId: null,\n marqueePaused: false,\n marqueeLastTimestamp: 0\n }\n\n // Drag state (initialized if drag is enabled)\n let dragState: DragState | null = null\n\n // Loop state - tracks cloned slides for infinite loop\n const loopState = {\n initialized: false,\n clonedSlides: [] as HTMLElement[],\n realSlides: [...settings.slides] as HTMLElement[],\n clonesPerSide: 0\n }\n\n // Marquee state - tracks cloned slides for marquee mode\n const marqueeState = createMarqueeState()\n\n // Default easing function\n const easing = settings.easing ?? easeOutExpo\n\n /**\n * Get cached feed bounding rect (invalidated on resize)\n */\n const getFeedRect = (): DOMRect => {\n const currentWidth = settings.feed.clientWidth\n if (!state.cachedFeedRect || state.lastWidth !== currentWidth) {\n state.cachedFeedRect = settings.feed.getBoundingClientRect()\n state.lastWidth = currentWidth\n }\n return state.cachedFeedRect\n }\n\n /**\n * Get only visible slides (not hidden via CSS)\n */\n const getVisibleSlides = (): HTMLElement[] => {\n return settings.slides.filter((slide) => slide.offsetParent !== null)\n }\n\n /**\n * Check if we're on desktop based on breakpoint\n */\n const isDesktop = (): boolean => {\n return window.matchMedia(DESKTOP_BREAKPOINT).matches\n }\n\n /**\n * Get the number of slides to clone per side for infinite loop\n */\n const getLoopClonesCount = (): number => {\n const perView = isDesktop()\n ? settings.desktopSlidesPerView\n : settings.mobileSlidesPerView\n\n // Clone at least 1 slide, or the number of visible slides\n if (!perView || perView === 'auto') {\n return 1\n }\n return Math.ceil(perView)\n }\n\n /**\n * Setup cloned slides for infinite loop functionality\n * Clones first N slides to the end and last N slides to the beginning\n */\n const setupLoopClones = (): void => {\n if (!settings.loop || loopState.initialized) return\n\n const realSlides = loopState.realSlides\n const clonesCount = getLoopClonesCount()\n loopState.clonesPerSide = clonesCount\n\n // Clone last N slides and prepend to beginning\n for (let i = realSlides.length - clonesCount; i < realSlides.length; i++) {\n const slide = realSlides[i]\n if (!slide) continue\n\n const clone = slide.cloneNode(true) as HTMLElement\n clone.setAttribute('data-lazer-clone', 'prepend')\n clone.setAttribute('aria-hidden', 'true')\n settings.feed.insertBefore(clone, settings.feed.firstChild)\n loopState.clonedSlides.push(clone)\n }\n\n // Clone first N slides and append to end\n for (let i = 0; i < clonesCount; i++) {\n const slide = realSlides[i]\n if (!slide) continue\n\n const clone = slide.cloneNode(true) as HTMLElement\n clone.setAttribute('data-lazer-clone', 'append')\n clone.setAttribute('aria-hidden', 'true')\n settings.feed.appendChild(clone)\n loopState.clonedSlides.push(clone)\n }\n\n // Set initial scroll position to first real slide (after prepended clones)\n requestAnimationFrame(() => {\n const firstRealSlide = realSlides[0]\n if (firstRealSlide) {\n settings.feed.scrollLeft = firstRealSlide.offsetLeft\n }\n })\n\n loopState.initialized = true\n }\n\n /**\n * Handle repositioning when reaching clone boundaries\n * Silently jumps to the corresponding real slide without animation\n */\n const handleLoopReposition = (direction: SliderDirection): void => {\n if (!settings.loop || !loopState.initialized) return\n\n const realSlides = loopState.realSlides\n const totalRealSlides = realSlides.length\n\n if (direction === 'next') {\n const firstRealSlide = realSlides[0]\n if (firstRealSlide) {\n settings.feed.scrollLeft = firstRealSlide.offsetLeft\n }\n state.currentSlideIndex = 0\n } else {\n const lastRealSlide = realSlides[totalRealSlides - 1]\n if (lastRealSlide) {\n settings.feed.scrollLeft = lastRealSlide.offsetLeft\n }\n state.currentSlideIndex = totalRealSlides - 1\n }\n }\n\n /**\n * Remove cloned slides from the DOM\n */\n const cleanupLoopClones = (): void => {\n if (!loopState.initialized) return\n\n loopState.clonedSlides.forEach((clone) => {\n clone.remove()\n })\n\n loopState.clonedSlides = []\n loopState.initialized = false\n loopState.clonesPerSide = 0\n }\n\n /**\n * Apply slide widths based on slidesPerView settings\n */\n const applySlideWidths = (): void => {\n const perView = isDesktop()\n ? settings.desktopSlidesPerView\n : settings.mobileSlidesPerView\n\n const gap = settings.slideGap ?? 0\n\n // Set gap on the feed container\n if (gap > 0) {\n settings.feed.style.gap = `${gap}px`\n }\n\n // If 'auto' or not set, let CSS handle widths (natural sizing)\n if (!perView || perView === 'auto') {\n // Reset any previously set widths\n settings.slides.forEach((slide) => {\n slide.style.flex = ''\n slide.style.minWidth = ''\n })\n return\n }\n\n const totalGapWidth = gap * (perView - 1)\n const slideWidth = `calc((100% - ${totalGapWidth}px) / ${perView})`\n\n settings.slides.forEach((slide) => {\n slide.style.flex = `0 0 ${slideWidth}`\n slide.style.minWidth = slideWidth\n })\n }\n\n /**\n * Update scrollbar thumb width based on visible content\n */\n const updateScrollbar = (): void => {\n if (!settings.scrollbarThumb) return\n\n const feedRect = getFeedRect()\n const thumbWidth = (feedRect.width / settings.feed.scrollWidth) * 100\n settings.scrollbarThumb.style.width = `${thumbWidth}%`\n }\n\n /**\n * Update scrollbar thumb position based on scroll\n */\n const updateScrollbarPosition = (): void => {\n if (!settings.scrollbarThumb || !settings.scrollbarTrack) return\n\n const trackWidth = settings.scrollbarTrack.getBoundingClientRect().width\n const thumbWidth = settings.scrollbarThumb.getBoundingClientRect().width\n const totalTransform = trackWidth - thumbWidth\n\n const maxScroll = settings.feed.scrollWidth - settings.feed.clientWidth\n const scrollProgress = maxScroll > 0 ? settings.feed.scrollLeft / maxScroll : 0\n\n settings.scrollbarThumb.style.transform = `translateX(${totalTransform * scrollProgress}px)`\n }\n\n /**\n * Update visibility of navigation controls based on scroll position\n */\n const updateControlsVisibility = (): void => {\n const feedRect = getFeedRect()\n const isAtStart = settings.feed.scrollLeft <= 1 // Allow 1px tolerance\n const isAtEnd =\n settings.feed.scrollLeft + feedRect.width >= settings.feed.scrollWidth - 1\n const shouldHideScrollbar = settings.feed.scrollWidth <= feedRect.width\n\n // Hide/show scrollbar track\n if (settings.scrollbarTrack) {\n settings.scrollbarTrack.style.display = shouldHideScrollbar ? 'none' : 'block'\n }\n\n // When loop is enabled, always show both buttons (unless scrollbar should be hidden)\n if (settings.loop) {\n toggleControlVisibility(\n settings.prevSlideButton,\n !shouldHideScrollbar,\n settings.feed\n )\n toggleControlVisibility(\n settings.nextSlideButton,\n !shouldHideScrollbar,\n settings.feed\n )\n return\n }\n\n // Hide/show navigation buttons based on position\n toggleControlVisibility(\n settings.prevSlideButton,\n !isAtStart && !shouldHideScrollbar,\n settings.feed\n )\n toggleControlVisibility(\n settings.nextSlideButton,\n !isAtEnd && !shouldHideScrollbar,\n settings.feed\n )\n }\n\n /**\n * Calculate current slide index based on viewport position\n */\n const updateCurrentSlideIndex = (): void => {\n const feedRect = getFeedRect()\n\n // Use real slides for index calculation when loop is enabled\n const slidesToCheck = loopState.initialized\n ? loopState.realSlides\n : getVisibleSlides()\n\n // Find slides that are visible in the viewport\n const viewportVisibleSlides = slidesToCheck.filter((slide) => {\n const slideRect = slide.getBoundingClientRect()\n const tolerance = 20 // px tolerance for edge detection\n return (\n slideRect.right > feedRect.left + tolerance &&\n slideRect.left < feedRect.right - tolerance\n )\n })\n\n if (viewportVisibleSlides.length && viewportVisibleSlides[0]) {\n const newIndex = slidesToCheck.indexOf(viewportVisibleSlides[0])\n if (newIndex !== -1) {\n state.currentSlideIndex = newIndex\n settings.onScroll?.({\n currentScroll: settings.feed.scrollLeft,\n currentSlideIndex: state.currentSlideIndex\n })\n }\n }\n }\n\n /**\n * Smooth scroll to a target position using requestAnimationFrame\n */\n const smoothScrollTo = (\n target: number,\n customEasing: EasingFunction = easing,\n onComplete?: () => void\n ): void => {\n const start = settings.feed.scrollLeft\n const distance = Math.abs(target - start)\n\n // Dynamic duration based on distance\n const duration = Math.min(\n ANIMATION.MAX_DURATION,\n Math.max(ANIMATION.MIN_DURATION, distance / ANIMATION.SPEED_FACTOR)\n )\n\n const startTime = performance.now()\n\n const animateScroll = (currentTime: number): void => {\n const elapsed = (currentTime - startTime) / duration\n const progress = Math.min(elapsed, 1)\n const ease = customEasing(progress)\n\n settings.feed.scrollLeft = start + (target - start) * ease\n\n if (progress < 1) {\n requestAnimationFrame(animateScroll)\n } else {\n settings.feed.scrollLeft = target\n onComplete?.()\n }\n }\n\n requestAnimationFrame(animateScroll)\n }\n\n /**\n * Handle thumbnail click navigation\n */\n const handleThumbClick = (thumb: HTMLElement): void => {\n if (!settings.thumbs) return\n\n const index = settings.thumbs.indexOf(thumb)\n if (index === -1 || !settings.slides[index]) return\n\n state.currentSlideIndex = index\n updateActiveThumb(settings.thumbs, index)\n state.isScrolling = true\n\n // Clear existing timeout\n if (state.updateThumbTimeout) {\n clearTimeout(state.updateThumbTimeout)\n }\n\n // Reset scrolling flag after animation\n state.updateThumbTimeout = setTimeout(() => {\n state.isScrolling = false\n }, ANIMATION.THUMB_UPDATE_DELAY)\n\n smoothScrollTo(settings.slides[index].offsetLeft)\n }\n\n /**\n * Handle navigation button click (prev/next)\n */\n const handleNavButtonClick = (direction: SliderDirection): void => {\n const realSlides = loopState.initialized ? loopState.realSlides : getVisibleSlides()\n const slidesToScroll = isDesktop()\n ? settings.desktopSlidesPerScroll ?? 1\n : settings.mobileSlidesPerScroll ?? 1\n const totalRealSlides = realSlides.length\n\n // Update current index first\n updateCurrentSlideIndex()\n\n let targetSlide: HTMLElement | undefined\n let needsReposition = false\n\n if (direction === 'prev') {\n if (settings.loop && loopState.initialized && state.currentSlideIndex === 0) {\n\n const prependedClones = loopState.clonedSlides.filter(\n (clone) => clone.getAttribute('data-lazer-clone') === 'prepend'\n )\n targetSlide = prependedClones[prependedClones.length - 1]\n needsReposition = true\n } else {\n state.currentSlideIndex = Math.max(0, state.currentSlideIndex - slidesToScroll)\n targetSlide = realSlides[state.currentSlideIndex]\n }\n } else {\n if (settings.loop && loopState.initialized && state.currentSlideIndex >= totalRealSlides - 1) {\n const appendedClones = loopState.clonedSlides.filter(\n (clone) => clone.getAttribute('data-lazer-clone') === 'append'\n )\n targetSlide = appendedClones[0]\n needsReposition = true\n } else {\n state.currentSlideIndex = Math.min(\n totalRealSlides - 1,\n state.currentSlideIndex + slidesToScroll\n )\n targetSlide = realSlides[state.currentSlideIndex]\n }\n }\n\n if (!targetSlide) return\n\n // Fire scroll start callback\n settings.onScrollStart?.({\n currentScroll: settings.feed.scrollLeft,\n target: targetSlide,\n direction\n })\n\n if (needsReposition) {\n // Scroll to clone, then silently reposition to the real slide\n smoothScrollTo(targetSlide.offsetLeft, easing, () => {\n handleLoopReposition(direction)\n })\n } else {\n smoothScrollTo(targetSlide.offsetLeft)\n }\n }\n\n /**\n * Update all scroll-related state\n */\n const updateScrollPosition = (): void => {\n updateScrollbarPosition()\n updateControlsVisibility()\n updateCurrentSlideIndex()\n\n // Update thumbs only when not programmatically scrolling\n if (!state.isScrolling) {\n updateActiveThumb(settings.thumbs, state.currentSlideIndex)\n }\n\n // Clear existing timeout\n if (state.scrollEndTimeout) {\n clearTimeout(state.scrollEndTimeout)\n }\n\n // Fire scroll end callback after scroll settles\n state.scrollEndTimeout = setTimeout(() => {\n state.isScrolling = false\n settings.onScrollEnd?.({\n currentScroll: settings.feed.scrollLeft,\n currentSlideIndex: state.currentSlideIndex\n })\n }, ANIMATION.SCROLL_END_DELAY)\n }\n\n /**\n * Throttled scroll event handler\n */\n const handleFeedScroll = (): void => {\n if (!state.ticking) {\n requestAnimationFrame(() => {\n updateScrollPosition()\n state.ticking = false\n })\n state.ticking = true\n }\n }\n\n /**\n * Handle window resize\n */\n const handleWindowResize = (): void => {\n state.cachedFeedRect = null\n refresh()\n }\n\n /**\n * Attach all event listeners\n */\n const attachEventListeners = (): void => {\n const { signal } = state.abortController\n\n // Resize handler (not abortable, cleaned up manually)\n window.addEventListener('resize', handleWindowResize)\n\n // Scroll handler\n settings.feed.addEventListener('scroll', handleFeedScroll, {\n passive: true,\n signal\n })\n\n // Navigation button handlers\n if (settings.prevSlideButton) {\n settings.prevSlideButton.addEventListener(\n 'click',\n () => handleNavButtonClick('prev'),\n { signal }\n )\n }\n\n if (settings.nextSlideButton) {\n settings.nextSlideButton.addEventListener(\n 'click',\n () => handleNavButtonClick('next'),\n { signal }\n )\n }\n\n // Thumbnail handlers\n if (settings.thumbs?.length) {\n settings.thumbs[0]?.classList.add('active')\n settings.thumbs.forEach((thumb) => {\n thumb.addEventListener('click', () => handleThumbClick(thumb), { signal })\n })\n }\n\n // Keyboard navigation\n setupKeyboardNavigation(\n settings.feed,\n () => handleNavButtonClick('prev'),\n () => handleNavButtonClick('next'),\n signal\n )\n\n // Drag-to-scroll\n if (settings.enableDragToScroll) {\n dragState = setupDragToScroll({\n feed: settings.feed,\n slides: settings.slides,\n abortSignal: signal,\n smoothScrollTo,\n onDragEnd: () => {\n updateCurrentSlideIndex()\n updateActiveThumb(settings.thumbs, state.currentSlideIndex)\n }\n })\n }\n\n // Autoplay pause on hover/touch\n if (settings.autoplay && settings.pauseOnHover !== false) {\n settings.feed.addEventListener(\n 'mouseenter',\n pauseAutoplay,\n { signal }\n )\n settings.feed.addEventListener(\n 'mouseleave',\n resumeAutoplay,\n { signal }\n )\n settings.feed.addEventListener(\n 'touchstart',\n pauseAutoplay,\n { passive: true, signal }\n )\n settings.feed.addEventListener(\n 'touchend',\n resumeAutoplay,\n { signal }\n )\n }\n\n // Marquee pause on hover/touch\n attachMarqueeEventListeners(settings, state, signal)\n }\n\n /**\n * Start autoplay\n */\n const startAutoplay = (): void => {\n if (state.autoplayIntervalId) return;\n\n const interval = settings.autoplayInterval ?? 3000\n state.autoplayIntervalId = setInterval(() => {\n if (!state.autoplayPaused) {\n handleNavButtonClick('next')\n }\n }, interval)\n }\n\n /**\n * Stop autoplay\n */\n const stopAutoplay = (): void => {\n if (state.autoplayIntervalId) {\n clearInterval(state.autoplayIntervalId)\n state.autoplayIntervalId = null\n }\n }\n\n /**\n * Pause autoplay (temporary, resumes on mouse leave)\n */\n const pauseAutoplay = (): void => {\n state.autoplayPaused = true\n }\n\n /**\n * Resume autoplay after pause\n */\n const resumeAutoplay = (): void => {\n state.autoplayPaused = false\n }\n\n /**\n * Navigate to a specific slide by index\n */\n const goToIndex = (index: number): void => {\n // Use real slides when loop is enabled, otherwise use visible slides\n const slides = loopState.initialized ? loopState.realSlides : getVisibleSlides()\n const safeIndex = Math.max(0, Math.min(index, slides.length - 1))\n const targetSlide = slides[safeIndex]\n\n if (!targetSlide) return\n\n state.currentSlideIndex = safeIndex\n updateActiveThumb(settings.thumbs, safeIndex)\n smoothScrollTo(targetSlide.offsetLeft)\n }\n\n /**\n * Refresh slider state (recalculate dimensions, update controls)\n */\n const refresh = (): void => {\n state.cachedFeedRect = null\n applySlideWidths()\n updateScrollbar()\n updateControlsVisibility()\n\n // Recalculate marquee animation if in marquee mode\n if (settings.marquee && !state.marqueePaused) {\n startMarquee(settings, state)\n }\n }\n\n /**\n * Clean up all event listeners and timers\n */\n const unload = (): void => {\n // Stop autoplay and marquee\n stopAutoplay()\n stopMarquee(state, settings)\n\n // Abort all event listeners\n state.abortController.abort()\n\n // Remove resize listener\n window.removeEventListener('resize', handleWindowResize)\n\n // Clear timeouts\n if (state.updateThumbTimeout) {\n clearTimeout(state.updateThumbTimeout)\n }\n if (state.scrollEndTimeout) {\n clearTimeout(state.scrollEndTimeout)\n }\n\n // Clean up drag state\n if (dragState) {\n cleanupDrag(dragState)\n }\n\n // Clean up loop clones and marquee clones\n cleanupLoopClones()\n cleanupMarqueeClones(marqueeState)\n\n // Reset cached rect\n state.cachedFeedRect = null\n }\n\n // Initialize slider\n initAria(settings)\n applySlideWidths()\n\n // Marquee mode takes precedence over loop/autoplay\n if (settings.marquee) {\n setupMarquee(settings, state, marqueeState)\n } else {\n setupLoopClones()\n // Start autoplay if enabled (only in non-marquee mode)\n if (settings.autoplay) {\n startAutoplay()\n }\n }\n\n updateControlsVisibility()\n attachEventListeners()\n updateScrollbar()\n\n // Unified play/pause that works for both autoplay and marquee\n const play = (): void => {\n if (settings.marquee) {\n if (state.marqueePaused) {\n resumeMarquee(state, settings)\n } else {\n startMarquee(settings, state)\n }\n } else if (settings.autoplay) {\n startAutoplay()\n }\n }\n\n const pause = (): void => {\n if (settings.marquee) {\n pauseMarquee(state, settings)\n } else {\n stopAutoplay()\n }\n }\n\n return {\n goToIndex,\n refresh,\n unload,\n play,\n pause,\n next: () => handleNavButtonClick('next'),\n prev: () => handleNavButtonClick('prev')\n }\n}\n"],"mappings":"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","names":[]}
package/dist/index.js CHANGED
@@ -249,11 +249,11 @@ var injectMarqueeKeyframes = () => {
249
249
  style.id = "lazer-marquee-keyframes";
250
250
  style.textContent = `
251
251
  @keyframes lazer-marquee-scroll {
252
- from {
252
+ 0% {
253
253
  transform: translateX(0);
254
254
  }
255
- to {
256
- transform: translateX(calc(-1 * var(--lazer-marquee-distance) * 1px));
255
+ 100% {
256
+ transform: translateX(-50%);
257
257
  }
258
258
  }
259
259
  `;
@@ -283,15 +283,24 @@ var cleanupMarqueeClones = (marqueeState) => {
283
283
  marqueeState.initialized = false;
284
284
  };
285
285
  var setupMarqueeCss = (settings, state) => {
286
- const scrollWidth = settings.feed.scrollWidth;
287
- const speed = settings.marqueeSpeed ?? 50;
288
- const direction = settings.marqueeDirection ?? "left";
289
- const distance = scrollWidth / 2;
290
- settings.feed.style.setProperty("--lazer-marquee-distance", String(distance));
291
- const duration = distance / speed;
292
- const animationDirection = direction === "right" ? "reverse" : "normal";
293
- settings.feed.style.animation = `lazer-marquee-scroll ${duration}s linear infinite ${animationDirection}`;
294
- settings.feed.style.animationPlayState = state.marqueePaused ? "paused" : "running";
286
+ injectMarqueeKeyframes();
287
+ requestAnimationFrame(() => {
288
+ const scrollWidth = settings.feed.scrollWidth;
289
+ const speed = settings.marqueeSpeed ?? 50;
290
+ const direction = settings.marqueeDirection ?? "left";
291
+ const distance = scrollWidth / 2;
292
+ if (distance <= 0 || speed <= 0) {
293
+ console.warn("[lazer-slider] Invalid marquee values:", { distance, speed, scrollWidth });
294
+ return;
295
+ }
296
+ const duration = distance / speed;
297
+ const animationDirection = direction === "right" ? "reverse" : "normal";
298
+ const animationValue = `lazer-marquee-scroll ${duration}s linear infinite ${animationDirection}`;
299
+ settings.feed.style.animation = "none";
300
+ void settings.feed.offsetWidth;
301
+ settings.feed.style.animation = animationValue;
302
+ settings.feed.style.animationPlayState = state.marqueePaused ? "paused" : "running";
303
+ });
295
304
  };
296
305
  var startMarquee = (settings, state) => {
297
306
  setupMarqueeCss(settings, state);
@@ -301,7 +310,6 @@ var stopMarquee = (state, settings) => {
301
310
  settings.feed.style.animationPlayState = "";
302
311
  settings.feed.style.transform = "";
303
312
  settings.feed.style.willChange = "";
304
- settings.feed.style.removeProperty("--lazer-marquee-distance");
305
313
  };
306
314
  var pauseMarquee = (state, settings) => {
307
315
  state.marqueePaused = true;
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/core/easing.ts","../src/core/accessibility.ts","../src/core/drag.ts","../src/core/marquee.ts","../src/core/slider.ts"],"sourcesContent":["import type { EasingFunction } from './types.js'\n\n/**\n * Exponential ease-out - starts fast, decelerates smoothly\n * Best for scroll animations as it feels natural and responsive\n */\nexport const easeOutExpo: EasingFunction = (t) =>\n t === 1 ? 1 : 1 - Math.pow(2, -10 * t)\n\n/**\n * Cubic ease-out - smoother deceleration than exponential\n */\nexport const easeOutCubic: EasingFunction = (t) => 1 - Math.pow(1 - t, 3)\n\n/**\n * Cubic ease-in-out - smooth acceleration and deceleration\n */\nexport const easeInOutCubic: EasingFunction = (t) =>\n t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2\n\n/**\n * Quadratic ease-out - gentle deceleration\n */\nexport const easeOutQuad: EasingFunction = (t) => 1 - (1 - t) * (1 - t)\n\n/**\n * Linear - no easing, constant speed\n */\nexport const linear: EasingFunction = (t) => t\n","import type { SliderSettings } from './types.js'\n\n/**\n * Generate a unique ID for the slider\n */\nexport const generateSliderId = (): string =>\n `slider-${Math.random().toString(36).substring(2, 9)}`\n\n/**\n * Initialize ARIA attributes for accessibility\n */\nexport const initAria = (settings: SliderSettings): void => {\n const { feed, prevSlideButton, nextSlideButton, thumbs, slides } = settings\n\n // Ensure feed has an ID for aria-controls references\n if (!feed.id) {\n feed.id = generateSliderId()\n }\n\n // Set up feed as a scrollable region\n feed.setAttribute('role', 'region')\n feed.setAttribute('aria-label', 'Carousel')\n feed.setAttribute('aria-roledescription', 'carousel')\n\n // Remove tabindex to allow natural tab order\n feed.removeAttribute('tabindex')\n\n // Set up slides with proper roles\n slides.forEach((slide, index) => {\n slide.setAttribute('role', 'group')\n slide.setAttribute('aria-roledescription', 'slide')\n slide.setAttribute('aria-label', `Slide ${index + 1} of ${slides.length}`)\n })\n\n // Set up previous button\n if (prevSlideButton) {\n prevSlideButton.setAttribute('aria-label', 'Previous slide')\n prevSlideButton.setAttribute('aria-controls', feed.id)\n prevSlideButton.setAttribute('tabindex', '0')\n if (prevSlideButton.tagName !== 'BUTTON') {\n prevSlideButton.setAttribute('role', 'button')\n }\n }\n\n // Set up next button\n if (nextSlideButton) {\n nextSlideButton.setAttribute('aria-label', 'Next slide')\n nextSlideButton.setAttribute('aria-controls', feed.id)\n nextSlideButton.setAttribute('tabindex', '0')\n if (nextSlideButton.tagName !== 'BUTTON') {\n nextSlideButton.setAttribute('role', 'button')\n }\n }\n\n // Set up thumbnail/dot navigation\n if (thumbs?.length) {\n thumbs.forEach((thumb, index) => {\n if (thumb.tagName !== 'BUTTON') {\n thumb.setAttribute('role', 'button')\n }\n thumb.setAttribute('aria-label', `Go to slide ${index + 1}`)\n thumb.setAttribute('tabindex', '0')\n thumb.setAttribute('aria-controls', feed.id)\n })\n }\n}\n\n/**\n * Toggle visibility of navigation controls with proper ARIA handling\n */\nexport const toggleControlVisibility = (\n button: HTMLElement | null | undefined,\n shouldShow: boolean,\n feedElement?: HTMLElement\n): void => {\n if (!button) return\n\n // If hiding the button while it has focus, move focus to feed\n if (!shouldShow && button === document.activeElement && feedElement) {\n feedElement.focus()\n }\n\n // Update visual state with transition\n const transition = 'opacity 0.3s ease'\n const opacity = shouldShow ? '1' : '0'\n\n Object.assign(button.style, {\n opacity,\n transition,\n pointerEvents: shouldShow ? 'auto' : 'none'\n })\n\n // Update ARIA state\n if (!shouldShow) {\n button.setAttribute('aria-hidden', 'true')\n button.setAttribute('tabindex', '-1')\n } else {\n button.removeAttribute('aria-hidden')\n button.setAttribute('tabindex', '0')\n }\n\n // Hide from layout after transition if not visible\n if (!shouldShow) {\n setTimeout(() => {\n if (button.style.opacity === '0') {\n button.style.visibility = 'hidden'\n }\n }, 300)\n } else {\n button.style.visibility = 'visible'\n }\n}\n\n/**\n * Update active state on thumbnail elements\n */\nexport const updateActiveThumb = (\n thumbs: HTMLElement[] | undefined,\n currentIndex: number,\n activeClass: string = 'active'\n): void => {\n if (!thumbs?.length) return\n\n thumbs.forEach((thumb, index) => {\n const isActive = index === currentIndex\n thumb.classList.toggle(activeClass, isActive)\n thumb.setAttribute('aria-selected', isActive.toString())\n })\n}\n\n/**\n * Set up keyboard navigation for the slider\n */\nexport const setupKeyboardNavigation = (\n feed: HTMLElement,\n onPrev: () => void,\n onNext: () => void,\n abortSignal: AbortSignal\n): void => {\n feed.addEventListener(\n 'keydown',\n (event: KeyboardEvent) => {\n switch (event.key) {\n case 'ArrowLeft':\n event.preventDefault()\n onPrev()\n break\n case 'ArrowRight':\n event.preventDefault()\n onNext()\n break\n }\n },\n { signal: abortSignal }\n )\n\n // Make feed focusable for keyboard navigation\n if (!feed.hasAttribute('tabindex')) {\n feed.setAttribute('tabindex', '0')\n }\n}\n","import type { DragState, EasingFunction } from './types.js'\nimport { easeOutCubic } from './easing.js'\n\n/**\n * Configuration for drag behavior\n */\ninterface DragConfig {\n /** Feed element to enable dragging on */\n feed: HTMLElement\n /** Slide elements for snap-to-slide calculation */\n slides: HTMLElement[]\n /** AbortSignal for cleanup */\n abortSignal: AbortSignal\n /** Callback to smooth scroll to a position */\n smoothScrollTo: (target: number, easing?: EasingFunction) => void\n /** Callback when drag ends (for updating state) */\n onDragEnd?: () => void\n}\n\n/**\n * Create initial drag state\n */\nexport const createDragState = (): DragState => ({\n isDragging: false,\n startX: 0,\n startScrollLeft: 0,\n velocity: 0,\n lastX: 0,\n lastTime: 0,\n momentumId: null\n})\n\n/**\n * Find the nearest slide to snap to after drag\n */\nconst findNearestSlide = (\n feed: HTMLElement,\n slides: HTMLElement[]\n): HTMLElement | null => {\n const feedRect = feed.getBoundingClientRect()\n const feedCenter = feedRect.left + feedRect.width / 2\n\n let nearestSlide: HTMLElement | null = null\n let minDistance = Infinity\n\n for (const slide of slides) {\n if (slide.offsetParent === null) continue // Skip hidden slides\n\n const slideRect = slide.getBoundingClientRect()\n const slideCenter = slideRect.left + slideRect.width / 2\n const distance = Math.abs(feedCenter - slideCenter)\n\n if (distance < minDistance) {\n minDistance = distance\n nearestSlide = slide\n }\n }\n\n return nearestSlide\n}\n\n/**\n * Apply momentum scrolling after drag release\n */\nconst applyMomentum = (\n state: DragState,\n feed: HTMLElement,\n slides: HTMLElement[],\n smoothScrollTo: (target: number, easing?: EasingFunction) => void,\n onDragEnd?: () => void\n): void => {\n const friction = 0.95\n const minVelocity = 0.5\n\n const animate = () => {\n if (Math.abs(state.velocity) < minVelocity) {\n state.momentumId = null\n\n // Snap to nearest slide\n const nearestSlide = findNearestSlide(feed, slides)\n if (nearestSlide) {\n smoothScrollTo(nearestSlide.offsetLeft, easeOutCubic)\n }\n\n onDragEnd?.()\n return\n }\n\n feed.scrollLeft += state.velocity\n state.velocity *= friction\n\n state.momentumId = requestAnimationFrame(animate)\n }\n\n state.momentumId = requestAnimationFrame(animate)\n}\n\n/**\n * Get X position from mouse or touch event\n */\nconst getEventX = (event: MouseEvent | TouchEvent): number => {\n if ('touches' in event) {\n return event.touches[0]?.clientX ?? 0\n }\n return event.clientX\n}\n\n/**\n * Set up drag-to-scroll functionality\n */\nexport const setupDragToScroll = (config: DragConfig): DragState => {\n const { feed, slides, abortSignal, smoothScrollTo, onDragEnd } = config\n const state = createDragState()\n\n const handleDragStart = (event: MouseEvent | TouchEvent) => {\n // Cancel any ongoing momentum animation\n if (state.momentumId !== null) {\n cancelAnimationFrame(state.momentumId)\n state.momentumId = null\n }\n\n state.isDragging = true\n state.startX = getEventX(event)\n state.startScrollLeft = feed.scrollLeft\n state.velocity = 0\n state.lastX = state.startX\n state.lastTime = performance.now()\n\n // Visual feedback\n feed.style.cursor = 'grabbing'\n feed.style.userSelect = 'none'\n\n // Prevent default for mouse to avoid text selection\n if (event.type === 'mousedown') {\n event.preventDefault()\n }\n }\n\n const handleDragMove = (event: MouseEvent | TouchEvent) => {\n if (!state.isDragging) return\n\n const currentX = getEventX(event)\n const currentTime = performance.now()\n const deltaX = state.startX - currentX\n const deltaTime = currentTime - state.lastTime\n\n // Update scroll position\n feed.scrollLeft = state.startScrollLeft + deltaX\n\n // Calculate velocity for momentum\n if (deltaTime > 0) {\n state.velocity = (state.lastX - currentX) / deltaTime * 16 // Normalize to ~60fps\n }\n\n state.lastX = currentX\n state.lastTime = currentTime\n\n // Prevent default to stop page scrolling on touch\n if (event.type === 'touchmove') {\n event.preventDefault()\n }\n }\n\n const handleDragEnd = () => {\n if (!state.isDragging) return\n\n state.isDragging = false\n\n // Reset visual feedback\n feed.style.cursor = 'grab'\n feed.style.userSelect = ''\n\n // Apply momentum if velocity is significant\n if (Math.abs(state.velocity) > 1) {\n applyMomentum(state, feed, slides, smoothScrollTo, onDragEnd)\n } else {\n // Snap to nearest slide immediately\n const nearestSlide = findNearestSlide(feed, slides)\n if (nearestSlide) {\n smoothScrollTo(nearestSlide.offsetLeft, easeOutCubic)\n }\n onDragEnd?.()\n }\n }\n\n // Set initial cursor\n feed.style.cursor = 'grab'\n\n // Mouse events\n feed.addEventListener('mousedown', handleDragStart, { signal: abortSignal })\n document.addEventListener('mousemove', handleDragMove, { signal: abortSignal })\n document.addEventListener('mouseup', handleDragEnd, { signal: abortSignal })\n\n // Touch events\n feed.addEventListener('touchstart', handleDragStart, {\n passive: true,\n signal: abortSignal\n })\n feed.addEventListener('touchmove', handleDragMove, {\n passive: false, // Need to prevent default\n signal: abortSignal\n })\n feed.addEventListener('touchend', handleDragEnd, { signal: abortSignal })\n\n // Handle mouse leaving the window\n document.addEventListener('mouseleave', handleDragEnd, { signal: abortSignal })\n\n return state\n}\n\n/**\n * Clean up drag state (cancel any pending animations)\n */\nexport const cleanupDrag = (state: DragState): void => {\n if (state.momentumId !== null) {\n cancelAnimationFrame(state.momentumId)\n state.momentumId = null\n }\n}\n","import type { SliderSettings, SliderState } from './types.js'\n\n/**\n * Marquee state - tracks cloned slides for marquee mode\n */\nexport interface MarqueeState {\n initialized: boolean\n clonedSlides: HTMLElement[]\n styleElement: HTMLStyleElement | null\n}\n\n/**\n * Create initial marquee state\n */\nexport const createMarqueeState = (): MarqueeState => ({\n initialized: false,\n clonedSlides: [],\n styleElement: null\n})\n\n/**\n * Inject CSS keyframes for smooth marquee animation\n */\nconst injectMarqueeKeyframes = (): HTMLStyleElement => {\n // Check if keyframes already exist\n const existingStyle = document.getElementById('lazer-marquee-keyframes')\n if (existingStyle) {\n return existingStyle as HTMLStyleElement\n }\n\n const style = document.createElement('style')\n style.id = 'lazer-marquee-keyframes'\n style.textContent = `\n @keyframes lazer-marquee-scroll {\n from {\n transform: translateX(0);\n }\n to {\n transform: translateX(calc(-1 * var(--lazer-marquee-distance) * 1px));\n }\n }\n `\n document.head.appendChild(style)\n return style\n}\n\n/**\n * Clone all slides and append for seamless marquee looping\n */\nexport const setupMarqueeClones = (\n settings: SliderSettings,\n marqueeState: MarqueeState\n): void => {\n if (marqueeState.initialized) return\n\n // Inject CSS keyframes\n marqueeState.styleElement = injectMarqueeKeyframes()\n\n // Ensure feed has proper CSS for transform animation\n // Note: Parent container should have overflow: hidden for marquee effect\n settings.feed.style.display = 'flex'\n settings.feed.style.willChange = 'transform'\n\n // Clone all original slides and append to create seamless loop\n settings.slides.forEach((slide) => {\n const clone = slide.cloneNode(true) as HTMLElement\n clone.setAttribute('data-lazer-marquee-clone', 'true')\n clone.setAttribute('aria-hidden', 'true')\n settings.feed.appendChild(clone)\n marqueeState.clonedSlides.push(clone)\n })\n\n marqueeState.initialized = true\n}\n\n/**\n * Remove marquee cloned slides from the DOM\n */\nexport const cleanupMarqueeClones = (marqueeState: MarqueeState): void => {\n if (!marqueeState.initialized) return\n\n marqueeState.clonedSlides.forEach((clone) => {\n clone.remove()\n })\n\n marqueeState.clonedSlides = []\n marqueeState.initialized = false\n}\n\n/**\n * Setup marquee CSS animation for smooth hardware-accelerated scrolling\n */\nconst setupMarqueeCss = (\n settings: SliderSettings,\n state: SliderState\n): void => {\n const scrollWidth = settings.feed.scrollWidth\n const speed = settings.marqueeSpeed ?? 50 // pixels per second\n const direction = settings.marqueeDirection ?? 'left'\n const distance = scrollWidth / 2\n\n // Set CSS custom property for the exact pixel distance\n settings.feed.style.setProperty('--lazer-marquee-distance', String(distance))\n\n // Calculate duration: distance / speed\n const duration = distance / speed\n const animationDirection = direction === 'right' ? 'reverse' : 'normal'\n\n // Apply smooth CSS animation with hardware acceleration\n settings.feed.style.animation = `lazer-marquee-scroll ${duration}s linear infinite ${animationDirection}`\n settings.feed.style.animationPlayState = state.marqueePaused ? 'paused' : 'running'\n}\n\n/**\n * Start marquee animation using smooth CSS animations\n */\nexport const startMarquee = (\n settings: SliderSettings,\n state: SliderState\n): void => {\n // Setup CSS animation for smooth hardware-accelerated scrolling\n setupMarqueeCss(settings, state)\n}\n\n/**\n * Stop marquee animation\n */\nexport const stopMarquee = (state: SliderState, settings: SliderSettings): void => {\n // Remove animation\n settings.feed.style.animation = ''\n settings.feed.style.animationPlayState = ''\n\n // Reset transform and CSS custom property\n settings.feed.style.transform = ''\n settings.feed.style.willChange = ''\n settings.feed.style.removeProperty('--lazer-marquee-distance')\n}\n\n/**\n * Pause marquee animation (temporary, resumes on mouse leave)\n */\nexport const pauseMarquee = (state: SliderState, settings: SliderSettings): void => {\n state.marqueePaused = true\n settings.feed.style.animationPlayState = 'paused'\n}\n\n/**\n * Resume marquee animation after pause\n */\nexport const resumeMarquee = (state: SliderState, settings: SliderSettings): void => {\n state.marqueePaused = false\n settings.feed.style.animationPlayState = 'running'\n}\n\n/**\n * Setup marquee mode (clones + animation)\n */\nexport const setupMarquee = (\n settings: SliderSettings,\n state: SliderState,\n marqueeState: MarqueeState\n): void => {\n if (!settings.marquee) return\n\n setupMarqueeClones(settings, marqueeState)\n startMarquee(settings, state)\n}\n\n/**\n * Attach marquee pause-on-hover event listeners\n */\nexport const attachMarqueeEventListeners = (\n settings: SliderSettings,\n state: SliderState,\n signal: AbortSignal\n): void => {\n if (!settings.marquee || settings.pauseOnHover === false) return\n\n settings.feed.addEventListener(\n 'mouseenter',\n () => pauseMarquee(state, settings),\n { signal }\n )\n settings.feed.addEventListener(\n 'mouseleave',\n () => resumeMarquee(state, settings),\n { signal }\n )\n settings.feed.addEventListener(\n 'touchstart',\n () => pauseMarquee(state, settings),\n { passive: true, signal }\n )\n settings.feed.addEventListener(\n 'touchend',\n () => resumeMarquee(state, settings),\n { signal }\n )\n}\n","import type {\n SliderSettings,\n SliderState,\n SliderDirection,\n EasingFunction,\n Slider,\n DragState\n} from './types.js'\nimport { easeOutExpo } from './easing.js'\nimport {\n initAria,\n toggleControlVisibility,\n updateActiveThumb,\n setupKeyboardNavigation\n} from './accessibility.js'\nimport { setupDragToScroll, cleanupDrag } from './drag.js'\nimport {\n createMarqueeState,\n setupMarquee,\n startMarquee,\n stopMarquee,\n pauseMarquee,\n resumeMarquee,\n cleanupMarqueeClones,\n attachMarqueeEventListeners\n} from './marquee.js'\n\n/**\n * Animation duration configuration\n */\nconst ANIMATION = {\n MIN_DURATION: 400,\n MAX_DURATION: 1000,\n SPEED_FACTOR: 1.5,\n SCROLL_END_DELAY: 50,\n THUMB_UPDATE_DELAY: 500\n} as const\n\n/**\n * Desktop breakpoint for responsive slidesPerScroll\n */\nconst DESKTOP_BREAKPOINT = '(min-width: 64rem)'\n\n/**\n * Create a slider instance\n */\nexport const createSlider = (settings: SliderSettings): Slider => {\n // Validate required settings\n if (!settings.feed) {\n throw new Error('lazer-slider: feed element is required')\n }\n\n if (!settings.slides?.length) {\n throw new Error('lazer-slider: slides array is required and must not be empty')\n }\n\n // Initialize state\n const state: SliderState = {\n currentSlideIndex: 0,\n isScrolling: false,\n ticking: false,\n cachedFeedRect: null,\n lastWidth: 0,\n updateThumbTimeout: null,\n scrollEndTimeout: null,\n abortController: new AbortController(),\n autoplayIntervalId: null,\n autoplayPaused: false,\n marqueeAnimationId: null,\n marqueePaused: false,\n marqueeLastTimestamp: 0\n }\n\n // Drag state (initialized if drag is enabled)\n let dragState: DragState | null = null\n\n // Loop state - tracks cloned slides for infinite loop\n const loopState = {\n initialized: false,\n clonedSlides: [] as HTMLElement[],\n realSlides: [...settings.slides] as HTMLElement[],\n clonesPerSide: 0\n }\n\n // Marquee state - tracks cloned slides for marquee mode\n const marqueeState = createMarqueeState()\n\n // Default easing function\n const easing = settings.easing ?? easeOutExpo\n\n /**\n * Get cached feed bounding rect (invalidated on resize)\n */\n const getFeedRect = (): DOMRect => {\n const currentWidth = settings.feed.clientWidth\n if (!state.cachedFeedRect || state.lastWidth !== currentWidth) {\n state.cachedFeedRect = settings.feed.getBoundingClientRect()\n state.lastWidth = currentWidth\n }\n return state.cachedFeedRect\n }\n\n /**\n * Get only visible slides (not hidden via CSS)\n */\n const getVisibleSlides = (): HTMLElement[] => {\n return settings.slides.filter((slide) => slide.offsetParent !== null)\n }\n\n /**\n * Check if we're on desktop based on breakpoint\n */\n const isDesktop = (): boolean => {\n return window.matchMedia(DESKTOP_BREAKPOINT).matches\n }\n\n /**\n * Get the number of slides to clone per side for infinite loop\n */\n const getLoopClonesCount = (): number => {\n const perView = isDesktop()\n ? settings.desktopSlidesPerView\n : settings.mobileSlidesPerView\n\n // Clone at least 1 slide, or the number of visible slides\n if (!perView || perView === 'auto') {\n return 1\n }\n return Math.ceil(perView)\n }\n\n /**\n * Setup cloned slides for infinite loop functionality\n * Clones first N slides to the end and last N slides to the beginning\n */\n const setupLoopClones = (): void => {\n if (!settings.loop || loopState.initialized) return\n\n const realSlides = loopState.realSlides\n const clonesCount = getLoopClonesCount()\n loopState.clonesPerSide = clonesCount\n\n // Clone last N slides and prepend to beginning\n for (let i = realSlides.length - clonesCount; i < realSlides.length; i++) {\n const slide = realSlides[i]\n if (!slide) continue\n\n const clone = slide.cloneNode(true) as HTMLElement\n clone.setAttribute('data-lazer-clone', 'prepend')\n clone.setAttribute('aria-hidden', 'true')\n settings.feed.insertBefore(clone, settings.feed.firstChild)\n loopState.clonedSlides.push(clone)\n }\n\n // Clone first N slides and append to end\n for (let i = 0; i < clonesCount; i++) {\n const slide = realSlides[i]\n if (!slide) continue\n\n const clone = slide.cloneNode(true) as HTMLElement\n clone.setAttribute('data-lazer-clone', 'append')\n clone.setAttribute('aria-hidden', 'true')\n settings.feed.appendChild(clone)\n loopState.clonedSlides.push(clone)\n }\n\n // Set initial scroll position to first real slide (after prepended clones)\n requestAnimationFrame(() => {\n const firstRealSlide = realSlides[0]\n if (firstRealSlide) {\n settings.feed.scrollLeft = firstRealSlide.offsetLeft\n }\n })\n\n loopState.initialized = true\n }\n\n /**\n * Handle repositioning when reaching clone boundaries\n * Silently jumps to the corresponding real slide without animation\n */\n const handleLoopReposition = (direction: SliderDirection): void => {\n if (!settings.loop || !loopState.initialized) return\n\n const realSlides = loopState.realSlides\n const totalRealSlides = realSlides.length\n\n if (direction === 'next') {\n const firstRealSlide = realSlides[0]\n if (firstRealSlide) {\n settings.feed.scrollLeft = firstRealSlide.offsetLeft\n }\n state.currentSlideIndex = 0\n } else {\n const lastRealSlide = realSlides[totalRealSlides - 1]\n if (lastRealSlide) {\n settings.feed.scrollLeft = lastRealSlide.offsetLeft\n }\n state.currentSlideIndex = totalRealSlides - 1\n }\n }\n\n /**\n * Remove cloned slides from the DOM\n */\n const cleanupLoopClones = (): void => {\n if (!loopState.initialized) return\n\n loopState.clonedSlides.forEach((clone) => {\n clone.remove()\n })\n\n loopState.clonedSlides = []\n loopState.initialized = false\n loopState.clonesPerSide = 0\n }\n\n /**\n * Apply slide widths based on slidesPerView settings\n */\n const applySlideWidths = (): void => {\n const perView = isDesktop()\n ? settings.desktopSlidesPerView\n : settings.mobileSlidesPerView\n\n const gap = settings.slideGap ?? 0\n\n // Set gap on the feed container\n if (gap > 0) {\n settings.feed.style.gap = `${gap}px`\n }\n\n // If 'auto' or not set, let CSS handle widths (natural sizing)\n if (!perView || perView === 'auto') {\n // Reset any previously set widths\n settings.slides.forEach((slide) => {\n slide.style.flex = ''\n slide.style.minWidth = ''\n })\n return\n }\n\n const totalGapWidth = gap * (perView - 1)\n const slideWidth = `calc((100% - ${totalGapWidth}px) / ${perView})`\n\n settings.slides.forEach((slide) => {\n slide.style.flex = `0 0 ${slideWidth}`\n slide.style.minWidth = slideWidth\n })\n }\n\n /**\n * Update scrollbar thumb width based on visible content\n */\n const updateScrollbar = (): void => {\n if (!settings.scrollbarThumb) return\n\n const feedRect = getFeedRect()\n const thumbWidth = (feedRect.width / settings.feed.scrollWidth) * 100\n settings.scrollbarThumb.style.width = `${thumbWidth}%`\n }\n\n /**\n * Update scrollbar thumb position based on scroll\n */\n const updateScrollbarPosition = (): void => {\n if (!settings.scrollbarThumb || !settings.scrollbarTrack) return\n\n const trackWidth = settings.scrollbarTrack.getBoundingClientRect().width\n const thumbWidth = settings.scrollbarThumb.getBoundingClientRect().width\n const totalTransform = trackWidth - thumbWidth\n\n const maxScroll = settings.feed.scrollWidth - settings.feed.clientWidth\n const scrollProgress = maxScroll > 0 ? settings.feed.scrollLeft / maxScroll : 0\n\n settings.scrollbarThumb.style.transform = `translateX(${totalTransform * scrollProgress}px)`\n }\n\n /**\n * Update visibility of navigation controls based on scroll position\n */\n const updateControlsVisibility = (): void => {\n const feedRect = getFeedRect()\n const isAtStart = settings.feed.scrollLeft <= 1 // Allow 1px tolerance\n const isAtEnd =\n settings.feed.scrollLeft + feedRect.width >= settings.feed.scrollWidth - 1\n const shouldHideScrollbar = settings.feed.scrollWidth <= feedRect.width\n\n // Hide/show scrollbar track\n if (settings.scrollbarTrack) {\n settings.scrollbarTrack.style.display = shouldHideScrollbar ? 'none' : 'block'\n }\n\n // When loop is enabled, always show both buttons (unless scrollbar should be hidden)\n if (settings.loop) {\n toggleControlVisibility(\n settings.prevSlideButton,\n !shouldHideScrollbar,\n settings.feed\n )\n toggleControlVisibility(\n settings.nextSlideButton,\n !shouldHideScrollbar,\n settings.feed\n )\n return\n }\n\n // Hide/show navigation buttons based on position\n toggleControlVisibility(\n settings.prevSlideButton,\n !isAtStart && !shouldHideScrollbar,\n settings.feed\n )\n toggleControlVisibility(\n settings.nextSlideButton,\n !isAtEnd && !shouldHideScrollbar,\n settings.feed\n )\n }\n\n /**\n * Calculate current slide index based on viewport position\n */\n const updateCurrentSlideIndex = (): void => {\n const feedRect = getFeedRect()\n\n // Use real slides for index calculation when loop is enabled\n const slidesToCheck = loopState.initialized\n ? loopState.realSlides\n : getVisibleSlides()\n\n // Find slides that are visible in the viewport\n const viewportVisibleSlides = slidesToCheck.filter((slide) => {\n const slideRect = slide.getBoundingClientRect()\n const tolerance = 20 // px tolerance for edge detection\n return (\n slideRect.right > feedRect.left + tolerance &&\n slideRect.left < feedRect.right - tolerance\n )\n })\n\n if (viewportVisibleSlides.length && viewportVisibleSlides[0]) {\n const newIndex = slidesToCheck.indexOf(viewportVisibleSlides[0])\n if (newIndex !== -1) {\n state.currentSlideIndex = newIndex\n settings.onScroll?.({\n currentScroll: settings.feed.scrollLeft,\n currentSlideIndex: state.currentSlideIndex\n })\n }\n }\n }\n\n /**\n * Smooth scroll to a target position using requestAnimationFrame\n */\n const smoothScrollTo = (\n target: number,\n customEasing: EasingFunction = easing,\n onComplete?: () => void\n ): void => {\n const start = settings.feed.scrollLeft\n const distance = Math.abs(target - start)\n\n // Dynamic duration based on distance\n const duration = Math.min(\n ANIMATION.MAX_DURATION,\n Math.max(ANIMATION.MIN_DURATION, distance / ANIMATION.SPEED_FACTOR)\n )\n\n const startTime = performance.now()\n\n const animateScroll = (currentTime: number): void => {\n const elapsed = (currentTime - startTime) / duration\n const progress = Math.min(elapsed, 1)\n const ease = customEasing(progress)\n\n settings.feed.scrollLeft = start + (target - start) * ease\n\n if (progress < 1) {\n requestAnimationFrame(animateScroll)\n } else {\n settings.feed.scrollLeft = target\n onComplete?.()\n }\n }\n\n requestAnimationFrame(animateScroll)\n }\n\n /**\n * Handle thumbnail click navigation\n */\n const handleThumbClick = (thumb: HTMLElement): void => {\n if (!settings.thumbs) return\n\n const index = settings.thumbs.indexOf(thumb)\n if (index === -1 || !settings.slides[index]) return\n\n state.currentSlideIndex = index\n updateActiveThumb(settings.thumbs, index)\n state.isScrolling = true\n\n // Clear existing timeout\n if (state.updateThumbTimeout) {\n clearTimeout(state.updateThumbTimeout)\n }\n\n // Reset scrolling flag after animation\n state.updateThumbTimeout = setTimeout(() => {\n state.isScrolling = false\n }, ANIMATION.THUMB_UPDATE_DELAY)\n\n smoothScrollTo(settings.slides[index].offsetLeft)\n }\n\n /**\n * Handle navigation button click (prev/next)\n */\n const handleNavButtonClick = (direction: SliderDirection): void => {\n const realSlides = loopState.initialized ? loopState.realSlides : getVisibleSlides()\n const slidesToScroll = isDesktop()\n ? settings.desktopSlidesPerScroll ?? 1\n : settings.mobileSlidesPerScroll ?? 1\n const totalRealSlides = realSlides.length\n\n // Update current index first\n updateCurrentSlideIndex()\n\n let targetSlide: HTMLElement | undefined\n let needsReposition = false\n\n if (direction === 'prev') {\n if (settings.loop && loopState.initialized && state.currentSlideIndex === 0) {\n\n const prependedClones = loopState.clonedSlides.filter(\n (clone) => clone.getAttribute('data-lazer-clone') === 'prepend'\n )\n targetSlide = prependedClones[prependedClones.length - 1]\n needsReposition = true\n } else {\n state.currentSlideIndex = Math.max(0, state.currentSlideIndex - slidesToScroll)\n targetSlide = realSlides[state.currentSlideIndex]\n }\n } else {\n if (settings.loop && loopState.initialized && state.currentSlideIndex >= totalRealSlides - 1) {\n const appendedClones = loopState.clonedSlides.filter(\n (clone) => clone.getAttribute('data-lazer-clone') === 'append'\n )\n targetSlide = appendedClones[0]\n needsReposition = true\n } else {\n state.currentSlideIndex = Math.min(\n totalRealSlides - 1,\n state.currentSlideIndex + slidesToScroll\n )\n targetSlide = realSlides[state.currentSlideIndex]\n }\n }\n\n if (!targetSlide) return\n\n // Fire scroll start callback\n settings.onScrollStart?.({\n currentScroll: settings.feed.scrollLeft,\n target: targetSlide,\n direction\n })\n\n if (needsReposition) {\n // Scroll to clone, then silently reposition to the real slide\n smoothScrollTo(targetSlide.offsetLeft, easing, () => {\n handleLoopReposition(direction)\n })\n } else {\n smoothScrollTo(targetSlide.offsetLeft)\n }\n }\n\n /**\n * Update all scroll-related state\n */\n const updateScrollPosition = (): void => {\n updateScrollbarPosition()\n updateControlsVisibility()\n updateCurrentSlideIndex()\n\n // Update thumbs only when not programmatically scrolling\n if (!state.isScrolling) {\n updateActiveThumb(settings.thumbs, state.currentSlideIndex)\n }\n\n // Clear existing timeout\n if (state.scrollEndTimeout) {\n clearTimeout(state.scrollEndTimeout)\n }\n\n // Fire scroll end callback after scroll settles\n state.scrollEndTimeout = setTimeout(() => {\n state.isScrolling = false\n settings.onScrollEnd?.({\n currentScroll: settings.feed.scrollLeft,\n currentSlideIndex: state.currentSlideIndex\n })\n }, ANIMATION.SCROLL_END_DELAY)\n }\n\n /**\n * Throttled scroll event handler\n */\n const handleFeedScroll = (): void => {\n if (!state.ticking) {\n requestAnimationFrame(() => {\n updateScrollPosition()\n state.ticking = false\n })\n state.ticking = true\n }\n }\n\n /**\n * Handle window resize\n */\n const handleWindowResize = (): void => {\n state.cachedFeedRect = null\n refresh()\n }\n\n /**\n * Attach all event listeners\n */\n const attachEventListeners = (): void => {\n const { signal } = state.abortController\n\n // Resize handler (not abortable, cleaned up manually)\n window.addEventListener('resize', handleWindowResize)\n\n // Scroll handler\n settings.feed.addEventListener('scroll', handleFeedScroll, {\n passive: true,\n signal\n })\n\n // Navigation button handlers\n if (settings.prevSlideButton) {\n settings.prevSlideButton.addEventListener(\n 'click',\n () => handleNavButtonClick('prev'),\n { signal }\n )\n }\n\n if (settings.nextSlideButton) {\n settings.nextSlideButton.addEventListener(\n 'click',\n () => handleNavButtonClick('next'),\n { signal }\n )\n }\n\n // Thumbnail handlers\n if (settings.thumbs?.length) {\n settings.thumbs[0]?.classList.add('active')\n settings.thumbs.forEach((thumb) => {\n thumb.addEventListener('click', () => handleThumbClick(thumb), { signal })\n })\n }\n\n // Keyboard navigation\n setupKeyboardNavigation(\n settings.feed,\n () => handleNavButtonClick('prev'),\n () => handleNavButtonClick('next'),\n signal\n )\n\n // Drag-to-scroll\n if (settings.enableDragToScroll) {\n dragState = setupDragToScroll({\n feed: settings.feed,\n slides: settings.slides,\n abortSignal: signal,\n smoothScrollTo,\n onDragEnd: () => {\n updateCurrentSlideIndex()\n updateActiveThumb(settings.thumbs, state.currentSlideIndex)\n }\n })\n }\n\n // Autoplay pause on hover/touch\n if (settings.autoplay && settings.pauseOnHover !== false) {\n settings.feed.addEventListener(\n 'mouseenter',\n pauseAutoplay,\n { signal }\n )\n settings.feed.addEventListener(\n 'mouseleave',\n resumeAutoplay,\n { signal }\n )\n settings.feed.addEventListener(\n 'touchstart',\n pauseAutoplay,\n { passive: true, signal }\n )\n settings.feed.addEventListener(\n 'touchend',\n resumeAutoplay,\n { signal }\n )\n }\n\n // Marquee pause on hover/touch\n attachMarqueeEventListeners(settings, state, signal)\n }\n\n /**\n * Start autoplay\n */\n const startAutoplay = (): void => {\n if (state.autoplayIntervalId) return;\n\n const interval = settings.autoplayInterval ?? 3000\n state.autoplayIntervalId = setInterval(() => {\n if (!state.autoplayPaused) {\n handleNavButtonClick('next')\n }\n }, interval)\n }\n\n /**\n * Stop autoplay\n */\n const stopAutoplay = (): void => {\n if (state.autoplayIntervalId) {\n clearInterval(state.autoplayIntervalId)\n state.autoplayIntervalId = null\n }\n }\n\n /**\n * Pause autoplay (temporary, resumes on mouse leave)\n */\n const pauseAutoplay = (): void => {\n state.autoplayPaused = true\n }\n\n /**\n * Resume autoplay after pause\n */\n const resumeAutoplay = (): void => {\n state.autoplayPaused = false\n }\n\n /**\n * Navigate to a specific slide by index\n */\n const goToIndex = (index: number): void => {\n // Use real slides when loop is enabled, otherwise use visible slides\n const slides = loopState.initialized ? loopState.realSlides : getVisibleSlides()\n const safeIndex = Math.max(0, Math.min(index, slides.length - 1))\n const targetSlide = slides[safeIndex]\n\n if (!targetSlide) return\n\n state.currentSlideIndex = safeIndex\n updateActiveThumb(settings.thumbs, safeIndex)\n smoothScrollTo(targetSlide.offsetLeft)\n }\n\n /**\n * Refresh slider state (recalculate dimensions, update controls)\n */\n const refresh = (): void => {\n state.cachedFeedRect = null\n applySlideWidths()\n updateScrollbar()\n updateControlsVisibility()\n\n // Recalculate marquee animation if in marquee mode\n if (settings.marquee && !state.marqueePaused) {\n startMarquee(settings, state)\n }\n }\n\n /**\n * Clean up all event listeners and timers\n */\n const unload = (): void => {\n // Stop autoplay and marquee\n stopAutoplay()\n stopMarquee(state, settings)\n\n // Abort all event listeners\n state.abortController.abort()\n\n // Remove resize listener\n window.removeEventListener('resize', handleWindowResize)\n\n // Clear timeouts\n if (state.updateThumbTimeout) {\n clearTimeout(state.updateThumbTimeout)\n }\n if (state.scrollEndTimeout) {\n clearTimeout(state.scrollEndTimeout)\n }\n\n // Clean up drag state\n if (dragState) {\n cleanupDrag(dragState)\n }\n\n // Clean up loop clones and marquee clones\n cleanupLoopClones()\n cleanupMarqueeClones(marqueeState)\n\n // Reset cached rect\n state.cachedFeedRect = null\n }\n\n // Initialize slider\n initAria(settings)\n applySlideWidths()\n\n // Marquee mode takes precedence over loop/autoplay\n if (settings.marquee) {\n setupMarquee(settings, state, marqueeState)\n } else {\n setupLoopClones()\n // Start autoplay if enabled (only in non-marquee mode)\n if (settings.autoplay) {\n startAutoplay()\n }\n }\n\n updateControlsVisibility()\n attachEventListeners()\n updateScrollbar()\n\n // Unified play/pause that works for both autoplay and marquee\n const play = (): void => {\n if (settings.marquee) {\n if (state.marqueePaused) {\n resumeMarquee(state, settings)\n } else {\n startMarquee(settings, state)\n }\n } else if (settings.autoplay) {\n startAutoplay()\n }\n }\n\n const pause = (): void => {\n if (settings.marquee) {\n pauseMarquee(state, settings)\n } else {\n stopAutoplay()\n }\n }\n\n return {\n goToIndex,\n refresh,\n unload,\n play,\n pause,\n next: () => handleNavButtonClick('next'),\n prev: () => handleNavButtonClick('prev')\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/core/easing.ts","../src/core/accessibility.ts","../src/core/drag.ts","../src/core/marquee.ts","../src/core/slider.ts"],"sourcesContent":["import type { EasingFunction } from './types.js'\n\n/**\n * Exponential ease-out - starts fast, decelerates smoothly\n * Best for scroll animations as it feels natural and responsive\n */\nexport const easeOutExpo: EasingFunction = (t) =>\n t === 1 ? 1 : 1 - Math.pow(2, -10 * t)\n\n/**\n * Cubic ease-out - smoother deceleration than exponential\n */\nexport const easeOutCubic: EasingFunction = (t) => 1 - Math.pow(1 - t, 3)\n\n/**\n * Cubic ease-in-out - smooth acceleration and deceleration\n */\nexport const easeInOutCubic: EasingFunction = (t) =>\n t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2\n\n/**\n * Quadratic ease-out - gentle deceleration\n */\nexport const easeOutQuad: EasingFunction = (t) => 1 - (1 - t) * (1 - t)\n\n/**\n * Linear - no easing, constant speed\n */\nexport const linear: EasingFunction = (t) => t\n","import type { SliderSettings } from './types.js'\n\n/**\n * Generate a unique ID for the slider\n */\nexport const generateSliderId = (): string =>\n `slider-${Math.random().toString(36).substring(2, 9)}`\n\n/**\n * Initialize ARIA attributes for accessibility\n */\nexport const initAria = (settings: SliderSettings): void => {\n const { feed, prevSlideButton, nextSlideButton, thumbs, slides } = settings\n\n // Ensure feed has an ID for aria-controls references\n if (!feed.id) {\n feed.id = generateSliderId()\n }\n\n // Set up feed as a scrollable region\n feed.setAttribute('role', 'region')\n feed.setAttribute('aria-label', 'Carousel')\n feed.setAttribute('aria-roledescription', 'carousel')\n\n // Remove tabindex to allow natural tab order\n feed.removeAttribute('tabindex')\n\n // Set up slides with proper roles\n slides.forEach((slide, index) => {\n slide.setAttribute('role', 'group')\n slide.setAttribute('aria-roledescription', 'slide')\n slide.setAttribute('aria-label', `Slide ${index + 1} of ${slides.length}`)\n })\n\n // Set up previous button\n if (prevSlideButton) {\n prevSlideButton.setAttribute('aria-label', 'Previous slide')\n prevSlideButton.setAttribute('aria-controls', feed.id)\n prevSlideButton.setAttribute('tabindex', '0')\n if (prevSlideButton.tagName !== 'BUTTON') {\n prevSlideButton.setAttribute('role', 'button')\n }\n }\n\n // Set up next button\n if (nextSlideButton) {\n nextSlideButton.setAttribute('aria-label', 'Next slide')\n nextSlideButton.setAttribute('aria-controls', feed.id)\n nextSlideButton.setAttribute('tabindex', '0')\n if (nextSlideButton.tagName !== 'BUTTON') {\n nextSlideButton.setAttribute('role', 'button')\n }\n }\n\n // Set up thumbnail/dot navigation\n if (thumbs?.length) {\n thumbs.forEach((thumb, index) => {\n if (thumb.tagName !== 'BUTTON') {\n thumb.setAttribute('role', 'button')\n }\n thumb.setAttribute('aria-label', `Go to slide ${index + 1}`)\n thumb.setAttribute('tabindex', '0')\n thumb.setAttribute('aria-controls', feed.id)\n })\n }\n}\n\n/**\n * Toggle visibility of navigation controls with proper ARIA handling\n */\nexport const toggleControlVisibility = (\n button: HTMLElement | null | undefined,\n shouldShow: boolean,\n feedElement?: HTMLElement\n): void => {\n if (!button) return\n\n // If hiding the button while it has focus, move focus to feed\n if (!shouldShow && button === document.activeElement && feedElement) {\n feedElement.focus()\n }\n\n // Update visual state with transition\n const transition = 'opacity 0.3s ease'\n const opacity = shouldShow ? '1' : '0'\n\n Object.assign(button.style, {\n opacity,\n transition,\n pointerEvents: shouldShow ? 'auto' : 'none'\n })\n\n // Update ARIA state\n if (!shouldShow) {\n button.setAttribute('aria-hidden', 'true')\n button.setAttribute('tabindex', '-1')\n } else {\n button.removeAttribute('aria-hidden')\n button.setAttribute('tabindex', '0')\n }\n\n // Hide from layout after transition if not visible\n if (!shouldShow) {\n setTimeout(() => {\n if (button.style.opacity === '0') {\n button.style.visibility = 'hidden'\n }\n }, 300)\n } else {\n button.style.visibility = 'visible'\n }\n}\n\n/**\n * Update active state on thumbnail elements\n */\nexport const updateActiveThumb = (\n thumbs: HTMLElement[] | undefined,\n currentIndex: number,\n activeClass: string = 'active'\n): void => {\n if (!thumbs?.length) return\n\n thumbs.forEach((thumb, index) => {\n const isActive = index === currentIndex\n thumb.classList.toggle(activeClass, isActive)\n thumb.setAttribute('aria-selected', isActive.toString())\n })\n}\n\n/**\n * Set up keyboard navigation for the slider\n */\nexport const setupKeyboardNavigation = (\n feed: HTMLElement,\n onPrev: () => void,\n onNext: () => void,\n abortSignal: AbortSignal\n): void => {\n feed.addEventListener(\n 'keydown',\n (event: KeyboardEvent) => {\n switch (event.key) {\n case 'ArrowLeft':\n event.preventDefault()\n onPrev()\n break\n case 'ArrowRight':\n event.preventDefault()\n onNext()\n break\n }\n },\n { signal: abortSignal }\n )\n\n // Make feed focusable for keyboard navigation\n if (!feed.hasAttribute('tabindex')) {\n feed.setAttribute('tabindex', '0')\n }\n}\n","import type { DragState, EasingFunction } from './types.js'\nimport { easeOutCubic } from './easing.js'\n\n/**\n * Configuration for drag behavior\n */\ninterface DragConfig {\n /** Feed element to enable dragging on */\n feed: HTMLElement\n /** Slide elements for snap-to-slide calculation */\n slides: HTMLElement[]\n /** AbortSignal for cleanup */\n abortSignal: AbortSignal\n /** Callback to smooth scroll to a position */\n smoothScrollTo: (target: number, easing?: EasingFunction) => void\n /** Callback when drag ends (for updating state) */\n onDragEnd?: () => void\n}\n\n/**\n * Create initial drag state\n */\nexport const createDragState = (): DragState => ({\n isDragging: false,\n startX: 0,\n startScrollLeft: 0,\n velocity: 0,\n lastX: 0,\n lastTime: 0,\n momentumId: null\n})\n\n/**\n * Find the nearest slide to snap to after drag\n */\nconst findNearestSlide = (\n feed: HTMLElement,\n slides: HTMLElement[]\n): HTMLElement | null => {\n const feedRect = feed.getBoundingClientRect()\n const feedCenter = feedRect.left + feedRect.width / 2\n\n let nearestSlide: HTMLElement | null = null\n let minDistance = Infinity\n\n for (const slide of slides) {\n if (slide.offsetParent === null) continue // Skip hidden slides\n\n const slideRect = slide.getBoundingClientRect()\n const slideCenter = slideRect.left + slideRect.width / 2\n const distance = Math.abs(feedCenter - slideCenter)\n\n if (distance < minDistance) {\n minDistance = distance\n nearestSlide = slide\n }\n }\n\n return nearestSlide\n}\n\n/**\n * Apply momentum scrolling after drag release\n */\nconst applyMomentum = (\n state: DragState,\n feed: HTMLElement,\n slides: HTMLElement[],\n smoothScrollTo: (target: number, easing?: EasingFunction) => void,\n onDragEnd?: () => void\n): void => {\n const friction = 0.95\n const minVelocity = 0.5\n\n const animate = () => {\n if (Math.abs(state.velocity) < minVelocity) {\n state.momentumId = null\n\n // Snap to nearest slide\n const nearestSlide = findNearestSlide(feed, slides)\n if (nearestSlide) {\n smoothScrollTo(nearestSlide.offsetLeft, easeOutCubic)\n }\n\n onDragEnd?.()\n return\n }\n\n feed.scrollLeft += state.velocity\n state.velocity *= friction\n\n state.momentumId = requestAnimationFrame(animate)\n }\n\n state.momentumId = requestAnimationFrame(animate)\n}\n\n/**\n * Get X position from mouse or touch event\n */\nconst getEventX = (event: MouseEvent | TouchEvent): number => {\n if ('touches' in event) {\n return event.touches[0]?.clientX ?? 0\n }\n return event.clientX\n}\n\n/**\n * Set up drag-to-scroll functionality\n */\nexport const setupDragToScroll = (config: DragConfig): DragState => {\n const { feed, slides, abortSignal, smoothScrollTo, onDragEnd } = config\n const state = createDragState()\n\n const handleDragStart = (event: MouseEvent | TouchEvent) => {\n // Cancel any ongoing momentum animation\n if (state.momentumId !== null) {\n cancelAnimationFrame(state.momentumId)\n state.momentumId = null\n }\n\n state.isDragging = true\n state.startX = getEventX(event)\n state.startScrollLeft = feed.scrollLeft\n state.velocity = 0\n state.lastX = state.startX\n state.lastTime = performance.now()\n\n // Visual feedback\n feed.style.cursor = 'grabbing'\n feed.style.userSelect = 'none'\n\n // Prevent default for mouse to avoid text selection\n if (event.type === 'mousedown') {\n event.preventDefault()\n }\n }\n\n const handleDragMove = (event: MouseEvent | TouchEvent) => {\n if (!state.isDragging) return\n\n const currentX = getEventX(event)\n const currentTime = performance.now()\n const deltaX = state.startX - currentX\n const deltaTime = currentTime - state.lastTime\n\n // Update scroll position\n feed.scrollLeft = state.startScrollLeft + deltaX\n\n // Calculate velocity for momentum\n if (deltaTime > 0) {\n state.velocity = (state.lastX - currentX) / deltaTime * 16 // Normalize to ~60fps\n }\n\n state.lastX = currentX\n state.lastTime = currentTime\n\n // Prevent default to stop page scrolling on touch\n if (event.type === 'touchmove') {\n event.preventDefault()\n }\n }\n\n const handleDragEnd = () => {\n if (!state.isDragging) return\n\n state.isDragging = false\n\n // Reset visual feedback\n feed.style.cursor = 'grab'\n feed.style.userSelect = ''\n\n // Apply momentum if velocity is significant\n if (Math.abs(state.velocity) > 1) {\n applyMomentum(state, feed, slides, smoothScrollTo, onDragEnd)\n } else {\n // Snap to nearest slide immediately\n const nearestSlide = findNearestSlide(feed, slides)\n if (nearestSlide) {\n smoothScrollTo(nearestSlide.offsetLeft, easeOutCubic)\n }\n onDragEnd?.()\n }\n }\n\n // Set initial cursor\n feed.style.cursor = 'grab'\n\n // Mouse events\n feed.addEventListener('mousedown', handleDragStart, { signal: abortSignal })\n document.addEventListener('mousemove', handleDragMove, { signal: abortSignal })\n document.addEventListener('mouseup', handleDragEnd, { signal: abortSignal })\n\n // Touch events\n feed.addEventListener('touchstart', handleDragStart, {\n passive: true,\n signal: abortSignal\n })\n feed.addEventListener('touchmove', handleDragMove, {\n passive: false, // Need to prevent default\n signal: abortSignal\n })\n feed.addEventListener('touchend', handleDragEnd, { signal: abortSignal })\n\n // Handle mouse leaving the window\n document.addEventListener('mouseleave', handleDragEnd, { signal: abortSignal })\n\n return state\n}\n\n/**\n * Clean up drag state (cancel any pending animations)\n */\nexport const cleanupDrag = (state: DragState): void => {\n if (state.momentumId !== null) {\n cancelAnimationFrame(state.momentumId)\n state.momentumId = null\n }\n}\n","import type { SliderSettings, SliderState } from './types.js'\n\n/**\n * Marquee state - tracks cloned slides for marquee mode\n */\nexport interface MarqueeState {\n initialized: boolean\n clonedSlides: HTMLElement[]\n styleElement: HTMLStyleElement | null\n}\n\n/**\n * Create initial marquee state\n */\nexport const createMarqueeState = (): MarqueeState => ({\n initialized: false,\n clonedSlides: [],\n styleElement: null\n})\n\n/**\n * Inject CSS keyframes for smooth marquee animation\n */\nconst injectMarqueeKeyframes = (): HTMLStyleElement => {\n // Check if keyframes already exist\n const existingStyle = document.getElementById('lazer-marquee-keyframes')\n if (existingStyle) {\n return existingStyle as HTMLStyleElement\n }\n\n const style = document.createElement('style')\n style.id = 'lazer-marquee-keyframes'\n style.textContent = `\n @keyframes lazer-marquee-scroll {\n 0% {\n transform: translateX(0);\n }\n 100% {\n transform: translateX(-50%);\n }\n }\n `\n document.head.appendChild(style)\n return style\n}\n\n/**\n * Clone all slides and append for seamless marquee looping\n */\nexport const setupMarqueeClones = (\n settings: SliderSettings,\n marqueeState: MarqueeState\n): void => {\n if (marqueeState.initialized) return\n\n // Inject CSS keyframes\n marqueeState.styleElement = injectMarqueeKeyframes()\n\n // Ensure feed has proper CSS for transform animation\n // Note: Parent container should have overflow: hidden for marquee effect\n settings.feed.style.display = 'flex'\n settings.feed.style.willChange = 'transform'\n\n // Clone all original slides and append to create seamless loop\n settings.slides.forEach((slide) => {\n const clone = slide.cloneNode(true) as HTMLElement\n clone.setAttribute('data-lazer-marquee-clone', 'true')\n clone.setAttribute('aria-hidden', 'true')\n settings.feed.appendChild(clone)\n marqueeState.clonedSlides.push(clone)\n })\n\n marqueeState.initialized = true\n}\n\n/**\n * Remove marquee cloned slides from the DOM\n */\nexport const cleanupMarqueeClones = (marqueeState: MarqueeState): void => {\n if (!marqueeState.initialized) return\n\n marqueeState.clonedSlides.forEach((clone) => {\n clone.remove()\n })\n\n marqueeState.clonedSlides = []\n marqueeState.initialized = false\n}\n\n/**\n * Setup marquee CSS animation for smooth hardware-accelerated scrolling\n */\nconst setupMarqueeCss = (\n settings: SliderSettings,\n state: SliderState\n): void => {\n // Ensure keyframes exist first\n injectMarqueeKeyframes()\n\n // Wait a frame to ensure keyframes are parsed and layout is complete\n requestAnimationFrame(() => {\n const scrollWidth = settings.feed.scrollWidth\n const speed = settings.marqueeSpeed ?? 50 // pixels per second\n const direction = settings.marqueeDirection ?? 'left'\n\n // Distance is half the scroll width (the original content width)\n const distance = scrollWidth / 2\n\n // Validate values\n if (distance <= 0 || speed <= 0) {\n console.warn('[lazer-slider] Invalid marquee values:', { distance, speed, scrollWidth })\n return\n }\n\n // Calculate duration: distance / speed (how long to scroll through original content)\n const duration = distance / speed\n const animationDirection = direction === 'right' ? 'reverse' : 'normal'\n\n // Apply animation - use percentage-based keyframes for seamless loop\n const animationValue = `lazer-marquee-scroll ${duration}s linear infinite ${animationDirection}`\n\n // Remove any existing animation first\n settings.feed.style.animation = 'none'\n\n // Force reflow to reset animation\n void settings.feed.offsetWidth\n\n // Apply new animation\n settings.feed.style.animation = animationValue\n settings.feed.style.animationPlayState = state.marqueePaused ? 'paused' : 'running'\n })\n}\n\n/**\n * Start marquee animation using smooth CSS animations\n */\nexport const startMarquee = (\n settings: SliderSettings,\n state: SliderState\n): void => {\n // Setup CSS animation for smooth hardware-accelerated scrolling\n setupMarqueeCss(settings, state)\n}\n\n/**\n * Stop marquee animation\n */\nexport const stopMarquee = (state: SliderState, settings: SliderSettings): void => {\n // Remove animation\n settings.feed.style.animation = ''\n settings.feed.style.animationPlayState = ''\n\n // Reset transform\n settings.feed.style.transform = ''\n settings.feed.style.willChange = ''\n}\n\n/**\n * Pause marquee animation (temporary, resumes on mouse leave)\n */\nexport const pauseMarquee = (state: SliderState, settings: SliderSettings): void => {\n state.marqueePaused = true\n settings.feed.style.animationPlayState = 'paused'\n}\n\n/**\n * Resume marquee animation after pause\n */\nexport const resumeMarquee = (state: SliderState, settings: SliderSettings): void => {\n state.marqueePaused = false\n settings.feed.style.animationPlayState = 'running'\n}\n\n/**\n * Setup marquee mode (clones + animation)\n */\nexport const setupMarquee = (\n settings: SliderSettings,\n state: SliderState,\n marqueeState: MarqueeState\n): void => {\n if (!settings.marquee) return\n\n setupMarqueeClones(settings, marqueeState)\n startMarquee(settings, state)\n}\n\n/**\n * Attach marquee pause-on-hover event listeners\n */\nexport const attachMarqueeEventListeners = (\n settings: SliderSettings,\n state: SliderState,\n signal: AbortSignal\n): void => {\n if (!settings.marquee || settings.pauseOnHover === false) return\n\n settings.feed.addEventListener(\n 'mouseenter',\n () => pauseMarquee(state, settings),\n { signal }\n )\n settings.feed.addEventListener(\n 'mouseleave',\n () => resumeMarquee(state, settings),\n { signal }\n )\n settings.feed.addEventListener(\n 'touchstart',\n () => pauseMarquee(state, settings),\n { passive: true, signal }\n )\n settings.feed.addEventListener(\n 'touchend',\n () => resumeMarquee(state, settings),\n { signal }\n )\n}\n","import type {\n SliderSettings,\n SliderState,\n SliderDirection,\n EasingFunction,\n Slider,\n DragState\n} from './types.js'\nimport { easeOutExpo } from './easing.js'\nimport {\n initAria,\n toggleControlVisibility,\n updateActiveThumb,\n setupKeyboardNavigation\n} from './accessibility.js'\nimport { setupDragToScroll, cleanupDrag } from './drag.js'\nimport {\n createMarqueeState,\n setupMarquee,\n startMarquee,\n stopMarquee,\n pauseMarquee,\n resumeMarquee,\n cleanupMarqueeClones,\n attachMarqueeEventListeners\n} from './marquee.js'\n\n/**\n * Animation duration configuration\n */\nconst ANIMATION = {\n MIN_DURATION: 400,\n MAX_DURATION: 1000,\n SPEED_FACTOR: 1.5,\n SCROLL_END_DELAY: 50,\n THUMB_UPDATE_DELAY: 500\n} as const\n\n/**\n * Desktop breakpoint for responsive slidesPerScroll\n */\nconst DESKTOP_BREAKPOINT = '(min-width: 64rem)'\n\n/**\n * Create a slider instance\n */\nexport const createSlider = (settings: SliderSettings): Slider => {\n // Validate required settings\n if (!settings.feed) {\n throw new Error('lazer-slider: feed element is required')\n }\n\n if (!settings.slides?.length) {\n throw new Error('lazer-slider: slides array is required and must not be empty')\n }\n\n // Initialize state\n const state: SliderState = {\n currentSlideIndex: 0,\n isScrolling: false,\n ticking: false,\n cachedFeedRect: null,\n lastWidth: 0,\n updateThumbTimeout: null,\n scrollEndTimeout: null,\n abortController: new AbortController(),\n autoplayIntervalId: null,\n autoplayPaused: false,\n marqueeAnimationId: null,\n marqueePaused: false,\n marqueeLastTimestamp: 0\n }\n\n // Drag state (initialized if drag is enabled)\n let dragState: DragState | null = null\n\n // Loop state - tracks cloned slides for infinite loop\n const loopState = {\n initialized: false,\n clonedSlides: [] as HTMLElement[],\n realSlides: [...settings.slides] as HTMLElement[],\n clonesPerSide: 0\n }\n\n // Marquee state - tracks cloned slides for marquee mode\n const marqueeState = createMarqueeState()\n\n // Default easing function\n const easing = settings.easing ?? easeOutExpo\n\n /**\n * Get cached feed bounding rect (invalidated on resize)\n */\n const getFeedRect = (): DOMRect => {\n const currentWidth = settings.feed.clientWidth\n if (!state.cachedFeedRect || state.lastWidth !== currentWidth) {\n state.cachedFeedRect = settings.feed.getBoundingClientRect()\n state.lastWidth = currentWidth\n }\n return state.cachedFeedRect\n }\n\n /**\n * Get only visible slides (not hidden via CSS)\n */\n const getVisibleSlides = (): HTMLElement[] => {\n return settings.slides.filter((slide) => slide.offsetParent !== null)\n }\n\n /**\n * Check if we're on desktop based on breakpoint\n */\n const isDesktop = (): boolean => {\n return window.matchMedia(DESKTOP_BREAKPOINT).matches\n }\n\n /**\n * Get the number of slides to clone per side for infinite loop\n */\n const getLoopClonesCount = (): number => {\n const perView = isDesktop()\n ? settings.desktopSlidesPerView\n : settings.mobileSlidesPerView\n\n // Clone at least 1 slide, or the number of visible slides\n if (!perView || perView === 'auto') {\n return 1\n }\n return Math.ceil(perView)\n }\n\n /**\n * Setup cloned slides for infinite loop functionality\n * Clones first N slides to the end and last N slides to the beginning\n */\n const setupLoopClones = (): void => {\n if (!settings.loop || loopState.initialized) return\n\n const realSlides = loopState.realSlides\n const clonesCount = getLoopClonesCount()\n loopState.clonesPerSide = clonesCount\n\n // Clone last N slides and prepend to beginning\n for (let i = realSlides.length - clonesCount; i < realSlides.length; i++) {\n const slide = realSlides[i]\n if (!slide) continue\n\n const clone = slide.cloneNode(true) as HTMLElement\n clone.setAttribute('data-lazer-clone', 'prepend')\n clone.setAttribute('aria-hidden', 'true')\n settings.feed.insertBefore(clone, settings.feed.firstChild)\n loopState.clonedSlides.push(clone)\n }\n\n // Clone first N slides and append to end\n for (let i = 0; i < clonesCount; i++) {\n const slide = realSlides[i]\n if (!slide) continue\n\n const clone = slide.cloneNode(true) as HTMLElement\n clone.setAttribute('data-lazer-clone', 'append')\n clone.setAttribute('aria-hidden', 'true')\n settings.feed.appendChild(clone)\n loopState.clonedSlides.push(clone)\n }\n\n // Set initial scroll position to first real slide (after prepended clones)\n requestAnimationFrame(() => {\n const firstRealSlide = realSlides[0]\n if (firstRealSlide) {\n settings.feed.scrollLeft = firstRealSlide.offsetLeft\n }\n })\n\n loopState.initialized = true\n }\n\n /**\n * Handle repositioning when reaching clone boundaries\n * Silently jumps to the corresponding real slide without animation\n */\n const handleLoopReposition = (direction: SliderDirection): void => {\n if (!settings.loop || !loopState.initialized) return\n\n const realSlides = loopState.realSlides\n const totalRealSlides = realSlides.length\n\n if (direction === 'next') {\n const firstRealSlide = realSlides[0]\n if (firstRealSlide) {\n settings.feed.scrollLeft = firstRealSlide.offsetLeft\n }\n state.currentSlideIndex = 0\n } else {\n const lastRealSlide = realSlides[totalRealSlides - 1]\n if (lastRealSlide) {\n settings.feed.scrollLeft = lastRealSlide.offsetLeft\n }\n state.currentSlideIndex = totalRealSlides - 1\n }\n }\n\n /**\n * Remove cloned slides from the DOM\n */\n const cleanupLoopClones = (): void => {\n if (!loopState.initialized) return\n\n loopState.clonedSlides.forEach((clone) => {\n clone.remove()\n })\n\n loopState.clonedSlides = []\n loopState.initialized = false\n loopState.clonesPerSide = 0\n }\n\n /**\n * Apply slide widths based on slidesPerView settings\n */\n const applySlideWidths = (): void => {\n const perView = isDesktop()\n ? settings.desktopSlidesPerView\n : settings.mobileSlidesPerView\n\n const gap = settings.slideGap ?? 0\n\n // Set gap on the feed container\n if (gap > 0) {\n settings.feed.style.gap = `${gap}px`\n }\n\n // If 'auto' or not set, let CSS handle widths (natural sizing)\n if (!perView || perView === 'auto') {\n // Reset any previously set widths\n settings.slides.forEach((slide) => {\n slide.style.flex = ''\n slide.style.minWidth = ''\n })\n return\n }\n\n const totalGapWidth = gap * (perView - 1)\n const slideWidth = `calc((100% - ${totalGapWidth}px) / ${perView})`\n\n settings.slides.forEach((slide) => {\n slide.style.flex = `0 0 ${slideWidth}`\n slide.style.minWidth = slideWidth\n })\n }\n\n /**\n * Update scrollbar thumb width based on visible content\n */\n const updateScrollbar = (): void => {\n if (!settings.scrollbarThumb) return\n\n const feedRect = getFeedRect()\n const thumbWidth = (feedRect.width / settings.feed.scrollWidth) * 100\n settings.scrollbarThumb.style.width = `${thumbWidth}%`\n }\n\n /**\n * Update scrollbar thumb position based on scroll\n */\n const updateScrollbarPosition = (): void => {\n if (!settings.scrollbarThumb || !settings.scrollbarTrack) return\n\n const trackWidth = settings.scrollbarTrack.getBoundingClientRect().width\n const thumbWidth = settings.scrollbarThumb.getBoundingClientRect().width\n const totalTransform = trackWidth - thumbWidth\n\n const maxScroll = settings.feed.scrollWidth - settings.feed.clientWidth\n const scrollProgress = maxScroll > 0 ? settings.feed.scrollLeft / maxScroll : 0\n\n settings.scrollbarThumb.style.transform = `translateX(${totalTransform * scrollProgress}px)`\n }\n\n /**\n * Update visibility of navigation controls based on scroll position\n */\n const updateControlsVisibility = (): void => {\n const feedRect = getFeedRect()\n const isAtStart = settings.feed.scrollLeft <= 1 // Allow 1px tolerance\n const isAtEnd =\n settings.feed.scrollLeft + feedRect.width >= settings.feed.scrollWidth - 1\n const shouldHideScrollbar = settings.feed.scrollWidth <= feedRect.width\n\n // Hide/show scrollbar track\n if (settings.scrollbarTrack) {\n settings.scrollbarTrack.style.display = shouldHideScrollbar ? 'none' : 'block'\n }\n\n // When loop is enabled, always show both buttons (unless scrollbar should be hidden)\n if (settings.loop) {\n toggleControlVisibility(\n settings.prevSlideButton,\n !shouldHideScrollbar,\n settings.feed\n )\n toggleControlVisibility(\n settings.nextSlideButton,\n !shouldHideScrollbar,\n settings.feed\n )\n return\n }\n\n // Hide/show navigation buttons based on position\n toggleControlVisibility(\n settings.prevSlideButton,\n !isAtStart && !shouldHideScrollbar,\n settings.feed\n )\n toggleControlVisibility(\n settings.nextSlideButton,\n !isAtEnd && !shouldHideScrollbar,\n settings.feed\n )\n }\n\n /**\n * Calculate current slide index based on viewport position\n */\n const updateCurrentSlideIndex = (): void => {\n const feedRect = getFeedRect()\n\n // Use real slides for index calculation when loop is enabled\n const slidesToCheck = loopState.initialized\n ? loopState.realSlides\n : getVisibleSlides()\n\n // Find slides that are visible in the viewport\n const viewportVisibleSlides = slidesToCheck.filter((slide) => {\n const slideRect = slide.getBoundingClientRect()\n const tolerance = 20 // px tolerance for edge detection\n return (\n slideRect.right > feedRect.left + tolerance &&\n slideRect.left < feedRect.right - tolerance\n )\n })\n\n if (viewportVisibleSlides.length && viewportVisibleSlides[0]) {\n const newIndex = slidesToCheck.indexOf(viewportVisibleSlides[0])\n if (newIndex !== -1) {\n state.currentSlideIndex = newIndex\n settings.onScroll?.({\n currentScroll: settings.feed.scrollLeft,\n currentSlideIndex: state.currentSlideIndex\n })\n }\n }\n }\n\n /**\n * Smooth scroll to a target position using requestAnimationFrame\n */\n const smoothScrollTo = (\n target: number,\n customEasing: EasingFunction = easing,\n onComplete?: () => void\n ): void => {\n const start = settings.feed.scrollLeft\n const distance = Math.abs(target - start)\n\n // Dynamic duration based on distance\n const duration = Math.min(\n ANIMATION.MAX_DURATION,\n Math.max(ANIMATION.MIN_DURATION, distance / ANIMATION.SPEED_FACTOR)\n )\n\n const startTime = performance.now()\n\n const animateScroll = (currentTime: number): void => {\n const elapsed = (currentTime - startTime) / duration\n const progress = Math.min(elapsed, 1)\n const ease = customEasing(progress)\n\n settings.feed.scrollLeft = start + (target - start) * ease\n\n if (progress < 1) {\n requestAnimationFrame(animateScroll)\n } else {\n settings.feed.scrollLeft = target\n onComplete?.()\n }\n }\n\n requestAnimationFrame(animateScroll)\n }\n\n /**\n * Handle thumbnail click navigation\n */\n const handleThumbClick = (thumb: HTMLElement): void => {\n if (!settings.thumbs) return\n\n const index = settings.thumbs.indexOf(thumb)\n if (index === -1 || !settings.slides[index]) return\n\n state.currentSlideIndex = index\n updateActiveThumb(settings.thumbs, index)\n state.isScrolling = true\n\n // Clear existing timeout\n if (state.updateThumbTimeout) {\n clearTimeout(state.updateThumbTimeout)\n }\n\n // Reset scrolling flag after animation\n state.updateThumbTimeout = setTimeout(() => {\n state.isScrolling = false\n }, ANIMATION.THUMB_UPDATE_DELAY)\n\n smoothScrollTo(settings.slides[index].offsetLeft)\n }\n\n /**\n * Handle navigation button click (prev/next)\n */\n const handleNavButtonClick = (direction: SliderDirection): void => {\n const realSlides = loopState.initialized ? loopState.realSlides : getVisibleSlides()\n const slidesToScroll = isDesktop()\n ? settings.desktopSlidesPerScroll ?? 1\n : settings.mobileSlidesPerScroll ?? 1\n const totalRealSlides = realSlides.length\n\n // Update current index first\n updateCurrentSlideIndex()\n\n let targetSlide: HTMLElement | undefined\n let needsReposition = false\n\n if (direction === 'prev') {\n if (settings.loop && loopState.initialized && state.currentSlideIndex === 0) {\n\n const prependedClones = loopState.clonedSlides.filter(\n (clone) => clone.getAttribute('data-lazer-clone') === 'prepend'\n )\n targetSlide = prependedClones[prependedClones.length - 1]\n needsReposition = true\n } else {\n state.currentSlideIndex = Math.max(0, state.currentSlideIndex - slidesToScroll)\n targetSlide = realSlides[state.currentSlideIndex]\n }\n } else {\n if (settings.loop && loopState.initialized && state.currentSlideIndex >= totalRealSlides - 1) {\n const appendedClones = loopState.clonedSlides.filter(\n (clone) => clone.getAttribute('data-lazer-clone') === 'append'\n )\n targetSlide = appendedClones[0]\n needsReposition = true\n } else {\n state.currentSlideIndex = Math.min(\n totalRealSlides - 1,\n state.currentSlideIndex + slidesToScroll\n )\n targetSlide = realSlides[state.currentSlideIndex]\n }\n }\n\n if (!targetSlide) return\n\n // Fire scroll start callback\n settings.onScrollStart?.({\n currentScroll: settings.feed.scrollLeft,\n target: targetSlide,\n direction\n })\n\n if (needsReposition) {\n // Scroll to clone, then silently reposition to the real slide\n smoothScrollTo(targetSlide.offsetLeft, easing, () => {\n handleLoopReposition(direction)\n })\n } else {\n smoothScrollTo(targetSlide.offsetLeft)\n }\n }\n\n /**\n * Update all scroll-related state\n */\n const updateScrollPosition = (): void => {\n updateScrollbarPosition()\n updateControlsVisibility()\n updateCurrentSlideIndex()\n\n // Update thumbs only when not programmatically scrolling\n if (!state.isScrolling) {\n updateActiveThumb(settings.thumbs, state.currentSlideIndex)\n }\n\n // Clear existing timeout\n if (state.scrollEndTimeout) {\n clearTimeout(state.scrollEndTimeout)\n }\n\n // Fire scroll end callback after scroll settles\n state.scrollEndTimeout = setTimeout(() => {\n state.isScrolling = false\n settings.onScrollEnd?.({\n currentScroll: settings.feed.scrollLeft,\n currentSlideIndex: state.currentSlideIndex\n })\n }, ANIMATION.SCROLL_END_DELAY)\n }\n\n /**\n * Throttled scroll event handler\n */\n const handleFeedScroll = (): void => {\n if (!state.ticking) {\n requestAnimationFrame(() => {\n updateScrollPosition()\n state.ticking = false\n })\n state.ticking = true\n }\n }\n\n /**\n * Handle window resize\n */\n const handleWindowResize = (): void => {\n state.cachedFeedRect = null\n refresh()\n }\n\n /**\n * Attach all event listeners\n */\n const attachEventListeners = (): void => {\n const { signal } = state.abortController\n\n // Resize handler (not abortable, cleaned up manually)\n window.addEventListener('resize', handleWindowResize)\n\n // Scroll handler\n settings.feed.addEventListener('scroll', handleFeedScroll, {\n passive: true,\n signal\n })\n\n // Navigation button handlers\n if (settings.prevSlideButton) {\n settings.prevSlideButton.addEventListener(\n 'click',\n () => handleNavButtonClick('prev'),\n { signal }\n )\n }\n\n if (settings.nextSlideButton) {\n settings.nextSlideButton.addEventListener(\n 'click',\n () => handleNavButtonClick('next'),\n { signal }\n )\n }\n\n // Thumbnail handlers\n if (settings.thumbs?.length) {\n settings.thumbs[0]?.classList.add('active')\n settings.thumbs.forEach((thumb) => {\n thumb.addEventListener('click', () => handleThumbClick(thumb), { signal })\n })\n }\n\n // Keyboard navigation\n setupKeyboardNavigation(\n settings.feed,\n () => handleNavButtonClick('prev'),\n () => handleNavButtonClick('next'),\n signal\n )\n\n // Drag-to-scroll\n if (settings.enableDragToScroll) {\n dragState = setupDragToScroll({\n feed: settings.feed,\n slides: settings.slides,\n abortSignal: signal,\n smoothScrollTo,\n onDragEnd: () => {\n updateCurrentSlideIndex()\n updateActiveThumb(settings.thumbs, state.currentSlideIndex)\n }\n })\n }\n\n // Autoplay pause on hover/touch\n if (settings.autoplay && settings.pauseOnHover !== false) {\n settings.feed.addEventListener(\n 'mouseenter',\n pauseAutoplay,\n { signal }\n )\n settings.feed.addEventListener(\n 'mouseleave',\n resumeAutoplay,\n { signal }\n )\n settings.feed.addEventListener(\n 'touchstart',\n pauseAutoplay,\n { passive: true, signal }\n )\n settings.feed.addEventListener(\n 'touchend',\n resumeAutoplay,\n { signal }\n )\n }\n\n // Marquee pause on hover/touch\n attachMarqueeEventListeners(settings, state, signal)\n }\n\n /**\n * Start autoplay\n */\n const startAutoplay = (): void => {\n if (state.autoplayIntervalId) return;\n\n const interval = settings.autoplayInterval ?? 3000\n state.autoplayIntervalId = setInterval(() => {\n if (!state.autoplayPaused) {\n handleNavButtonClick('next')\n }\n }, interval)\n }\n\n /**\n * Stop autoplay\n */\n const stopAutoplay = (): void => {\n if (state.autoplayIntervalId) {\n clearInterval(state.autoplayIntervalId)\n state.autoplayIntervalId = null\n }\n }\n\n /**\n * Pause autoplay (temporary, resumes on mouse leave)\n */\n const pauseAutoplay = (): void => {\n state.autoplayPaused = true\n }\n\n /**\n * Resume autoplay after pause\n */\n const resumeAutoplay = (): void => {\n state.autoplayPaused = false\n }\n\n /**\n * Navigate to a specific slide by index\n */\n const goToIndex = (index: number): void => {\n // Use real slides when loop is enabled, otherwise use visible slides\n const slides = loopState.initialized ? loopState.realSlides : getVisibleSlides()\n const safeIndex = Math.max(0, Math.min(index, slides.length - 1))\n const targetSlide = slides[safeIndex]\n\n if (!targetSlide) return\n\n state.currentSlideIndex = safeIndex\n updateActiveThumb(settings.thumbs, safeIndex)\n smoothScrollTo(targetSlide.offsetLeft)\n }\n\n /**\n * Refresh slider state (recalculate dimensions, update controls)\n */\n const refresh = (): void => {\n state.cachedFeedRect = null\n applySlideWidths()\n updateScrollbar()\n updateControlsVisibility()\n\n // Recalculate marquee animation if in marquee mode\n if (settings.marquee && !state.marqueePaused) {\n startMarquee(settings, state)\n }\n }\n\n /**\n * Clean up all event listeners and timers\n */\n const unload = (): void => {\n // Stop autoplay and marquee\n stopAutoplay()\n stopMarquee(state, settings)\n\n // Abort all event listeners\n state.abortController.abort()\n\n // Remove resize listener\n window.removeEventListener('resize', handleWindowResize)\n\n // Clear timeouts\n if (state.updateThumbTimeout) {\n clearTimeout(state.updateThumbTimeout)\n }\n if (state.scrollEndTimeout) {\n clearTimeout(state.scrollEndTimeout)\n }\n\n // Clean up drag state\n if (dragState) {\n cleanupDrag(dragState)\n }\n\n // Clean up loop clones and marquee clones\n cleanupLoopClones()\n cleanupMarqueeClones(marqueeState)\n\n // Reset cached rect\n state.cachedFeedRect = null\n }\n\n // Initialize slider\n initAria(settings)\n applySlideWidths()\n\n // Marquee mode takes precedence over loop/autoplay\n if (settings.marquee) {\n setupMarquee(settings, state, marqueeState)\n } else {\n setupLoopClones()\n // Start autoplay if enabled (only in non-marquee mode)\n if (settings.autoplay) {\n startAutoplay()\n }\n }\n\n updateControlsVisibility()\n attachEventListeners()\n updateScrollbar()\n\n // Unified play/pause that works for both autoplay and marquee\n const play = (): void => {\n if (settings.marquee) {\n if (state.marqueePaused) {\n resumeMarquee(state, settings)\n } else {\n startMarquee(settings, state)\n }\n } else if (settings.autoplay) {\n startAutoplay()\n }\n }\n\n const pause = (): void => {\n if (settings.marquee) {\n pauseMarquee(state, settings)\n } else {\n stopAutoplay()\n }\n }\n\n return {\n goToIndex,\n refresh,\n unload,\n play,\n pause,\n next: () => handleNavButtonClick('next'),\n prev: () => handleNavButtonClick('prev')\n }\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lazer-slider",
3
- "version": "1.0.8",
3
+ "version": "1.0.9",
4
4
  "description": "A lightweight, accessible slider with smooth scroll-to-snap animations and drag-to-scroll support",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",