@jorgemadrid/open-carousel 0.1.0
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/LICENSE +21 -0
- package/README.md +148 -0
- package/dist/index.cjs +2529 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +618 -0
- package/dist/index.d.ts +618 -0
- package/dist/index.js +2471 -0
- package/dist/index.js.map +1 -0
- package/dist/styles.css +102 -0
- package/package.json +71 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/Carousel.tsx","../src/hooks/useDraggableScroll.ts","../src/hooks/useLoadingState.ts","../src/hooks/useCarouselTeleport.ts","../src/hooks/useCarouselVisuals.ts","../src/config.ts","../src/hooks/useCarouselPersistence.ts","../src/hooks/useCarouselLayout.ts","../src/hooks/useCarouselNavigation.ts","../src/hooks/useScrollCompletion.ts","../src/hooks/useCarouselCoordinator.ts","../src/CarouselArrow.tsx","../src/logger.ts"],"sourcesContent":["export { Carousel } from './Carousel'\r\nexport { CarouselArrow } from './CarouselArrow'\r\nexport * from './hooks/useCarouselCoordinator'\r\nexport * from './hooks/useCarouselLayout'\r\nexport * from './hooks/useCarouselNavigation'\r\nexport * from './hooks/useCarouselPersistence'\r\nexport * from './hooks/useCarouselTeleport'\r\nexport * from './hooks/useCarouselVisuals'\r\nexport * from './hooks/useDraggableScroll'\r\nexport * from './hooks/useLoadingState'\r\nexport * from './hooks/useScrollCompletion'\r\nexport * from './config'\r\nexport * from './logger'\r\n","import { useRef, useEffect, useCallback, useLayoutEffect, useMemo, memo, type ReactNode } from 'react'\r\nimport { useDraggableScroll } from './hooks/useDraggableScroll'\r\nimport { useLoadingState } from './hooks/useLoadingState'\r\nimport { useCarouselTeleport } from './hooks/useCarouselTeleport'\r\nimport { useCarouselVisuals } from './hooks/useCarouselVisuals'\r\nimport { useCarouselPersistence } from './hooks/useCarouselPersistence'\r\nimport { useCarouselLayout, measureLayoutFromElement } from './hooks/useCarouselLayout'\r\nimport { useCarouselNavigation } from './hooks/useCarouselNavigation'\r\nimport { useScrollCompletion } from './hooks/useScrollCompletion'\r\nimport { useCarouselCoordinator } from './hooks/useCarouselCoordinator'\r\nimport { CarouselArrow } from './CarouselArrow'\r\nimport {\r\n VISUAL_CONFIG,\r\n TIMING_CONFIG,\r\n LAYOUT_CONFIG,\r\n} from './config'\r\nimport { createLogger, type DebugChannel, type ChannelConfig } from './logger'\r\n\r\n\r\n/** \r\n/** Available CSS variable names for carousel item widths */\r\nexport type CarouselWidthVar = 'default' | 'review' | 'compact' | 'collection' | 'wide'\r\n\r\n/** SSR fallback width - matches CSS :root default for default variant */\r\nconst SSR_FALLBACK_WIDTH = 170\r\n\r\n/** \r\n * Map CSS variable name to inline style value with fallback.\r\n * Fallback ensures carousel works without consumer defining CSS variables.\r\n * Consumers can override by defining their own CSS variables.\r\n */\r\nconst CSS_VAR_WITH_FALLBACK: Record<CarouselWidthVar, string> = {\r\n default: 'var(--carousel-item-width-default, 200px)',\r\n review: 'var(--carousel-item-width-review, 280px)',\r\n compact: 'var(--carousel-item-width-compact, 150px)',\r\n collection: 'var(--carousel-item-width-collection, 100px)',\r\n wide: 'var(--carousel-item-width-wide, 350px)',\r\n}\r\n\r\n/** Map CSS variable name to the actual CSS property name (for getComputedStyle) */\r\nconst CSS_VAR_MAP: Record<CarouselWidthVar, string> = {\r\n default: '--carousel-item-width-default',\r\n review: '--carousel-item-width-review',\r\n compact: '--carousel-item-width-compact',\r\n collection: '--carousel-item-width-collection',\r\n wide: '--carousel-item-width-wide',\r\n}\r\n\r\n/**\r\n * Get the computed item width from CSS variable.\r\n * This reads the actual computed value from the browser, which respects media queries.\r\n * @param cssVarOrName - Either a raw CSS variable string (e.g., '--my-width') or a CarouselWidthVar name\r\n * @param fallback - Fallback width if CSS variable is undefined (default: SSR_FALLBACK_WIDTH)\r\n */\r\nexport const getComputedItemWidth = (\r\n cssVarOrName: string | CarouselWidthVar = 'default',\r\n fallback: number = SSR_FALLBACK_WIDTH\r\n): number => {\r\n if (typeof window === 'undefined') return fallback\r\n // If it starts with '--', it's already a CSS variable name\r\n const cssVar = cssVarOrName.startsWith('--')\r\n ? cssVarOrName\r\n : CSS_VAR_MAP[cssVarOrName as CarouselWidthVar]\r\n const value = getComputedStyle(document.documentElement).getPropertyValue(cssVar)\r\n return parseInt(value, 10) || fallback\r\n}\r\n\r\nexport interface BaseCarouselProps<T> {\r\n items: T[]\r\n getItemKey: (item: T, index: number) => string\r\n renderItem: (item: T, index: number, helpers: { scrollToItem: () => void }) => ReactNode\r\n infinite?: boolean\r\n onEndReached?: () => void\r\n hasNextPage?: boolean\r\n /** \r\n * CSS variable name for item width. Uses native CSS media queries for responsive widths.\r\n * Options: 'default' | 'review' | 'compact' | 'collection' | 'wide'\r\n * @deprecated Use itemWidthCssVar for full flexibility\r\n */\r\n itemWidthVar?: CarouselWidthVar\r\n /**\r\n * Custom CSS variable name for item width (e.g., '--my-carousel-item-width').\r\n * Use this for full flexibility - define your own CSS variable with responsive breakpoints.\r\n * Takes precedence over itemWidthVar when provided.\r\n */\r\n itemWidthCssVar?: string\r\n /**\r\n * Fallback width in pixels when using itemWidthCssVar and the CSS variable is undefined.\r\n * Required when using itemWidthCssVar. Defaults to 200 if not provided.\r\n */\r\n fallbackWidth?: number\r\n itemClassName?: string\r\n snapType?: 'mandatory' | 'proximity'\r\n\r\n disableOpacityEffect?: boolean\r\n disableScaleEffect?: boolean\r\n /** Custom vertical padding for the carousel container. Defaults to '20px'. */\r\n verticalPadding?: string\r\n snap?: boolean\r\n /** Optional custom skeleton renderer. Receives index. */\r\n renderSkeleton?: (index: number) => ReactNode\r\n /** \r\n * Optional key for persisting scroll position in sessionStorage.\r\n * When provided, the carousel will restore its scroll position after navigation.\r\n * Use a unique key per carousel instance, e.g., 'homepage-recommended'.\r\n */\r\n persistKey?: string\r\n onActiveItemChange?: (item: T) => void\r\n /** Optional explicit gap value in pixels. If not provided, uses LAYOUT_CONFIG based on viewport. */\r\n gap?: number\r\n /** Optional id for debug logging - helps identify which carousel in console */\r\n debugId?: string\r\n /** \r\n * Optional per-instance debug config. \r\n * Set `channels` to override global config for this carousel only.\r\n * Use 'ALL' to enable all channels.\r\n */\r\n debug?: {\r\n channels?: ChannelConfig\r\n bufferSize?: number\r\n }\r\n /**\r\n * Optional initial index to scroll to on mount.\r\n * Takes precedence over buffer positioning but yields to persisted position if available.\r\n */\r\n initialIndex?: number\r\n /**\r\n * If true, changes the selection threshold on mobile (viewport < 640px) from 0.5 (50%) to 0.3 (30%)\r\n * This makes the carousel select the next/prev item with less swipe distance.\r\n */\r\n eagerSelectionOnMobile?: boolean\r\n}\r\n\r\nfunction BaseCarouselInner<T>({\r\n items,\r\n getItemKey,\r\n renderItem,\r\n infinite = false,\r\n onEndReached,\r\n hasNextPage = false,\r\n itemWidthVar = 'default',\r\n itemWidthCssVar,\r\n fallbackWidth = 200,\r\n itemClassName = '',\r\n snapType = 'mandatory',\r\n disableOpacityEffect = false,\r\n disableScaleEffect = false,\r\n verticalPadding = '20px',\r\n snap = true,\r\n renderSkeleton,\r\n persistKey,\r\n onActiveItemChange,\r\n gap: gapProp,\r\n debugId = 'carousel',\r\n debug,\r\n eagerSelectionOnMobile = false,\r\n initialIndex,\r\n}: BaseCarouselProps<T>) {\r\n // Resolve gap: use prop if provided, otherwise determine from viewport\r\n const resolvedGap = gapProp ?? (\r\n typeof window !== 'undefined' && window.innerWidth < LAYOUT_CONFIG.GAP_BREAKPOINT\r\n ? LAYOUT_CONFIG.GAP_MOBILE\r\n : LAYOUT_CONFIG.GAP_DESKTOP\r\n )\r\n // ═══════════════════════════════════════════════════════════════════════════\r\n // LOGGER 2.0: Factory-created instance for this carousel\r\n // ═══════════════════════════════════════════════════════════════════════════\r\n const logger = useMemo(() => createLogger(debugId, debug), [debugId, debug])\r\n\r\n // Resolve width CSS - custom variable takes precedence over named variant\r\n const widthCssValue = itemWidthCssVar\r\n ? `var(${itemWidthCssVar}, ${fallbackWidth}px)`\r\n : CSS_VAR_WITH_FALLBACK[itemWidthVar]\r\n // Raw CSS variable name for getComputedStyle\r\n const widthCssVar = itemWidthCssVar || CSS_VAR_MAP[itemWidthVar]\r\n\r\n // Performance tracking - uses logger's timer for consistent metrics\r\n const initTimerRef = useRef(logger.createTimer())\r\n const getElapsedMs = () => initTimerRef.current.elapsed()\r\n // Use unified loading state hook for skeleton management\r\n // isInstant = true when carousel was previously seen (cache hit) - skips fade animation\r\n // This prevents the visible \"reset\" flash on Safari iOS when scrolling carousel back into view\r\n const { isReady, showSkeleton, markReady, isInstant } = useLoadingState({\r\n cacheKey: persistKey ? `carousel-${persistKey}` : undefined,\r\n skeletonDelay: 50,\r\n fallbackTimeout: 3000,\r\n })\r\n\r\n // Use persistence hook for scroll position save/restore across navigation\r\n const { getSavedPosition, savePosition } = useCarouselPersistence({\r\n persistKey,\r\n debounceMs: 150,\r\n })\r\n\r\n // TELEPORTING BUFFER STRATEGY\r\n // We render a fixed set of items: [BufferBefore] [OriginalItems] [BufferAfter]\r\n // And seamlessly teleport the scroll position when the user reaches the boundaries.\r\n\r\n const handleEndReached = () => {\r\n if (hasNextPage && onEndReached) {\r\n onEndReached()\r\n }\r\n }\r\n\r\n // Get draggable scroll first (needed for layout hook ref)\r\n const { ref: draggableRef, isDragging, events, cancelMomentum, adjustScroll } = useDraggableScroll({\r\n infinite,\r\n onEndReached: handleEndReached,\r\n hasNextPage,\r\n cardWidth: LAYOUT_CONFIG.INITIAL_CARD_WIDTH,\r\n gap: resolvedGap\r\n })\r\n\r\n // Use layout hook for stride measurement, viewport detection, and resize handling\r\n // Must be after draggableRef is declared\r\n // NOTE: We don't use onLayoutChange here because useCarouselVisuals hasn't been called yet.\r\n // Instead, we use a useEffect below to react to layout.cardWidth/gap changes.\r\n // resizeCount triggers re-renders when ResizeObserver fires, even if values are unchanged\r\n const { layout, measureLayout: triggerLayoutMeasure, resizeCount, isMobile } = useCarouselLayout({\r\n containerRef: draggableRef,\r\n logger,\r\n })\r\n\r\n // Setup Fixed Buffers\r\n // We need enough items to cover the screen width + buffer.\r\n // For safety, we aim for ~3 screens worth of items on each side if possible, or at least ~20 items.\r\n const { clonesBefore, clonesAfter } = useMemo(() => {\r\n let before: T[] = []\r\n let after: T[] = []\r\n\r\n if (infinite && items.length > 0) {\r\n const itemsNeeded = Math.ceil(LAYOUT_CONFIG.MIN_BUFFER_COUNT / items.length)\r\n const count = Math.max(1, itemsNeeded)\r\n\r\n before = Array(count).fill(items).flat()\r\n after = Array(count).fill(items).flat()\r\n }\r\n return { clonesBefore: before, clonesAfter: after }\r\n }, [infinite, items])\r\n\r\n const allItems = useMemo(() => [...clonesBefore, ...items, ...clonesAfter], [clonesBefore, items, clonesAfter])\r\n const bufferBeforeCount = clonesBefore.length\r\n\r\n // ═══════════════════════════════════════════════════════════════════════════\r\n // COORDINATOR: Single source of truth for carousel state\r\n // Replaces: isTeleporting, pendingScrollTarget, isPreTeleportingRef, isBouncing\r\n // ═══════════════════════════════════════════════════════════════════════════\r\n const { transition, getPhase, getContext, contextRef } = useCarouselCoordinator({ logger })\r\n\r\n // SSR-safe useLayoutEffect - falls back to useEffect on the server\r\n const useIsomorphicLayoutEffect = typeof window !== 'undefined' ? useLayoutEffect : useEffect\r\n\r\n const hasInitialized = useRef(false)\r\n // Timer/listener refs are still external (coordinator tracks IDs, we manage lifecycle)\r\n const scrollEndListenerRef = useRef<(() => void) | null>(null)\r\n const snapTimeoutRef = useRef<NodeJS.Timeout | null>(null)\r\n const scrollIdleTimeoutRef = useRef<NodeJS.Timeout | null>(null)\r\n\r\n // Note: Timer refs (snapTimeoutRef, scrollIdleTimeoutRef) still needed to manage actual timer handles\r\n // The coordinator only tracks timeout IDs for coordination, not the actual setTimeout return values\r\n // lastActiveItemRef: Used for dedup in onActiveItemChange callback - not coordinator state\r\n const lastActiveItemRef = useRef<T | null>(null)\r\n const lastInitRef = useRef<{ cardWidth: number, gap: number } | null>(null)\r\n\r\n // Use the extracted visuals hook for position cache and visual effects\r\n const {\r\n childrenPositions,\r\n isCacheDirty,\r\n containerWidthRef,\r\n isContainerWidthDirty,\r\n updateCache,\r\n applyVisuals,\r\n } = useCarouselVisuals({\r\n layout,\r\n itemsCount: items.length,\r\n bufferBeforeCount,\r\n disableOpacityEffect,\r\n disableScaleEffect,\r\n logger,\r\n })\r\n\r\n // Use the extracted teleport hook for infinite carousels\r\n // This handles scroll/scrollend/pointerdown events for the hybrid teleport strategy\r\n const { preTeleport } = useCarouselTeleport({\r\n containerRef: draggableRef,\r\n infinite,\r\n itemsCount: items.length,\r\n cardWidth: layout.cardWidth,\r\n gap: layout.gap,\r\n bufferBeforeCount,\r\n applyVisuals,\r\n adjustScroll,\r\n preTeleportClearDelayMs: TIMING_CONFIG.PRE_TELEPORT_CLEAR_DELAY_MS,\r\n coordinator: {\r\n transition,\r\n getContext,\r\n getPhase,\r\n contextRef,\r\n isBusy: () => getPhase() !== 'IDLE',\r\n isBlocking: () => getPhase() === 'BOUNCING' || getPhase() === 'TELEPORTING',\r\n },\r\n logger,\r\n })\r\n\r\n // ┌─────────────────────────────────────────────────────────────────────┐\r\n // │ IDEMPOTENT INITIALIZATION (Jan 2025) │\r\n // │ Extracted into a function so it can be called from both: │\r\n // │ 1. Ref callback (fast path, might fire before children render) │\r\n // │ 2. useLayoutEffect (guaranteed correct, after children in DOM) │\r\n // │ The idempotent check ensures only one of them actually does work. │\r\n // └─────────────────────────────────────────────────────────────────────┘\r\n const initializeCarousel = useCallback((node: HTMLDivElement) => {\r\n if (items.length === 0) {\r\n if (!isReady) markReady()\r\n return\r\n }\r\n\r\n // Use cached layout from ResizeObserver when available (avoids redundant getBoundingClientRect)\r\n // On first initialization, measure fresh to ensure accuracy before ResizeObserver has fired\r\n let cardWidth: number\r\n let gap: number\r\n\r\n if (hasInitialized.current) {\r\n // Subsequent calls (resize): use cached layout to avoid double measurement\r\n cardWidth = layout.cardWidth\r\n gap = layout.gap\r\n } else {\r\n // First call: measure fresh to ensure accurate initial positioning\r\n const measured = triggerLayoutMeasure()\r\n cardWidth = measured.cardWidth\r\n gap = measured.gap\r\n }\r\n\r\n // Get expected width from CSS variable.\r\n // CSS variables handle responsive widths natively via media queries,\r\n // avoiding the SSR timing issues of the old JS-based approach.\r\n const expectedWidth = getComputedItemWidth(widthCssVar, fallbackWidth)\r\n const widthDiff = Math.abs(cardWidth - expectedWidth)\r\n\r\n // Consider \"measured\" if width is within 10px of expected value\r\n // This handles sub-pixel differences and slight rendering variations\r\n const TOLERANCE = 10\r\n const hasNoChildren = node.children.length === 0\r\n const isNotExpectedWidth = widthDiff > TOLERANCE\r\n\r\n // Also check that scroll dimensions are ready (scrollWidth > clientWidth for scrollable content)\r\n // Without this, we might initialize with maxScroll=0 and clamp positions incorrectly\r\n const hasScrollableContent = node.scrollWidth > node.clientWidth\r\n const isUnmeasured = hasNoChildren || isNotExpectedWidth || !hasScrollableContent\r\n\r\n if (isUnmeasured && !hasInitialized.current) {\r\n logger.log('INIT', 'Skipping - not ready for initialization', {\r\n elapsedMs: getElapsedMs(),\r\n childrenCount: node.children.length,\r\n cardWidth,\r\n expectedWidth,\r\n widthDiff,\r\n itemWidthVar,\r\n hasScrollableContent,\r\n scrollWidth: node.scrollWidth,\r\n clientWidth: node.clientWidth,\r\n gap\r\n })\r\n return\r\n }\r\n\r\n // Idempotency Check: If already initialized and layout is identical, SKIP.\r\n // This prevents \"drift\" bugs where ResizeObserver fires (e.g. on mount/font-load)\r\n // but dimensions are unchanged, causing us to read a slightly snapped scroll position\r\n // and re-calculate targetPos with rounding errors.\r\n if (hasInitialized.current &&\r\n lastInitRef.current &&\r\n lastInitRef.current.cardWidth === cardWidth &&\r\n lastInitRef.current.gap === gap) {\r\n logger.log('INIT', 'Skipping - layout unchanged', { cardWidth, gap })\r\n return\r\n }\r\n\r\n // Update last init ref\r\n lastInitRef.current = { cardWidth, gap }\r\n\r\n const stride = cardWidth + gap\r\n\r\n // Determine target position\r\n let targetPos: number\r\n\r\n // If it's a subsequent run (e.g. resize), preserve the current active item\r\n if (hasInitialized.current) {\r\n const currentIndex = Math.round(node.scrollLeft / stride)\r\n targetPos = currentIndex * stride\r\n // Log the re-init for debugging to understand why it ran\r\n logger.log('INIT', `Re-initializing (Resize/Update)`, {\r\n currentIndex,\r\n targetPos,\r\n prevScroll: node.scrollLeft,\r\n stride\r\n })\r\n } else {\r\n const savedPosition = getSavedPosition()\r\n\r\n if (savedPosition !== null) {\r\n // Clamp to valid scroll range to handle viewport size changes\r\n const maxScroll = Math.max(0, node.scrollWidth - node.clientWidth)\r\n targetPos = Math.min(savedPosition, maxScroll)\r\n logger.log('CACHE', `Restoring scroll position`, { saved: savedPosition, clamped: targetPos, maxScroll })\r\n } else {\r\n // DOM-BASED INITIALIZATION (Deterministically matches CSS Snap)\r\n // Instead of calculating theoretical position (which drifts due to padding/snap logic),\r\n // we measure exactly where the target item is and center it manually.\r\n const startIdx = typeof initialIndex === 'number' ? initialIndex : 0\r\n const targetIndex = infinite ? bufferBeforeCount + startIdx : startIdx\r\n const targetNode = node.children[targetIndex] as HTMLElement\r\n\r\n if (targetNode) {\r\n // Center the item: ItemCenter - ContainerCenter\r\n // This naturally accounts for all padding, margins, and gaps.\r\n const itemCenter = targetNode.offsetLeft + (targetNode.offsetWidth / 2)\r\n const containerCenter = node.clientWidth / 2\r\n targetPos = Math.max(0, itemCenter - containerCenter)\r\n\r\n logger.log('INIT', 'DOM-based positioning used', {\r\n targetIndex,\r\n itemCenter,\r\n containerCenter,\r\n targetPos,\r\n offsetLeft: targetNode.offsetLeft,\r\n initialIndex\r\n })\r\n } else {\r\n // Fallback to theoretical math if DOM node missing (unlikely in useLayoutEffect)\r\n const startIdx = typeof initialIndex === 'number' ? initialIndex : 0\r\n const targetIdx = infinite ? bufferBeforeCount + startIdx : startIdx\r\n targetPos = targetIdx * stride\r\n logger.log('INIT', 'Fallback to theoretical positioning', { targetPos, initialIndex })\r\n }\r\n }\r\n\r\n // DEBUG: Trace initial target mapping\r\n const initialTargetIndex = targetPos / stride\r\n logger.log('INIT', 'Target Calc Trace', {\r\n targetPos,\r\n stride,\r\n initialTargetIndex,\r\n bufferBeforeCount,\r\n infinite\r\n })\r\n }\r\n\r\n // Check if correction needed (idempotent - safe to call multiple times)\r\n const positionDrift = Math.abs(node.scrollLeft - targetPos)\r\n const needsCorrection = !hasInitialized.current || positionDrift > stride / 2\r\n\r\n logger.log('INIT', 'Target Calc Result', {\r\n targetPos,\r\n currentScroll: node.scrollLeft,\r\n needsCorrection,\r\n hasInitialized: hasInitialized.current,\r\n infinite,\r\n bufferBeforeCount\r\n })\r\n\r\n if (needsCorrection) {\r\n logger.log('INIT', `Applying position correction`, {\r\n elapsedMs: getElapsedMs(),\r\n current: node.scrollLeft,\r\n target: targetPos,\r\n drift: positionDrift,\r\n firstInit: !hasInitialized.current\r\n })\r\n\r\n // Disable snap during position set\r\n node.style.scrollSnapType = 'none'\r\n\r\n // Apply padding synchronously (iOS race condition fix)\r\n if (infinite) {\r\n const centerPadding = `calc(50% - ${cardWidth / 2}px)`\r\n node.style.paddingLeft = centerPadding\r\n node.style.paddingRight = centerPadding\r\n node.style.scrollPaddingLeft = centerPadding\r\n node.style.scrollPaddingRight = centerPadding\r\n }\r\n\r\n logger.log('INIT', 'DRIFT DEBUG: Before scrollLeft set', {\r\n currentScroll: node.scrollLeft,\r\n targetPos,\r\n paddingLeft: node.style.paddingLeft,\r\n clientWidth: node.clientWidth\r\n })\r\n\r\n node.scrollLeft = targetPos\r\n\r\n logger.log('INIT', 'DRIFT DEBUG: After scrollLeft set (before flush)', {\r\n scrollLeftNow: node.scrollLeft\r\n })\r\n\r\n // Force synchronous layout flush to ensure scrollLeft is applied\r\n // before re-enabling scroll-snap (prevents snap from animating to wrong position)\r\n void node.offsetHeight\r\n\r\n logger.log('INIT', 'DRIFT DEBUG: After layout flush (before snap re-enable)', {\r\n scrollLeftNow: node.scrollLeft\r\n })\r\n\r\n // Re-enable snap\r\n node.style.scrollSnapType = ''\r\n\r\n logger.log('INIT', 'DRIFT DEBUG: After snap re-enabled', {\r\n scrollLeftNow: node.scrollLeft\r\n })\r\n }\r\n\r\n applyVisuals(node)\r\n node.style.opacity = '1'\r\n\r\n if (!hasInitialized.current) {\r\n hasInitialized.current = true\r\n transition({ type: 'INITIALIZE' })\r\n }\r\n if (!isReady) markReady()\r\n }, [items.length, bufferBeforeCount, applyVisuals, isReady, infinite, markReady, layout.cardWidth, layout.gap, triggerLayoutMeasure, transition, getSavedPosition, resizeCount, itemWidthVar, initialIndex])\r\n\r\n // Ref callback: fast path (might work if timing is good)\r\n const setCarouselRef = useCallback((node: HTMLDivElement | null) => {\r\n if (node) {\r\n (draggableRef as React.MutableRefObject<HTMLDivElement | null>).current = node\r\n initializeCarousel(node)\r\n }\r\n }, [draggableRef, initializeCarousel])\r\n\r\n // useLayoutEffect: guaranteed second attempt after children are in DOM\r\n // Using isomorphic version to avoid SSR warnings\r\n useIsomorphicLayoutEffect(() => {\r\n const node = draggableRef.current\r\n if (node && items.length > 0) {\r\n initializeCarousel(node)\r\n }\r\n }, [items.length, initializeCarousel, draggableRef, resizeCount])\r\n\r\n // NOTE: ResizeObserver is now handled by useCarouselLayout hook internally.\r\n // This useEffect reacts to layout changes and invalidates caches.\r\n useEffect(() => {\r\n if (draggableRef.current) {\r\n isCacheDirty.current = true\r\n isContainerWidthDirty.current = true\r\n updateCache(draggableRef.current)\r\n requestAnimationFrame(() => applyVisuals(draggableRef.current!))\r\n }\r\n }, [layout.cardWidth, layout.gap, updateCache, applyVisuals, draggableRef, isCacheDirty, isContainerWidthDirty])\r\n\r\n // Save scroll position on scrollend for persistence across navigation\r\n useEffect(() => {\r\n const el = draggableRef.current\r\n if (!el || !persistKey) return\r\n\r\n const handleScrollEnd = () => {\r\n // Save position after scroll animation completes\r\n savePosition(el.scrollLeft)\r\n }\r\n\r\n el.addEventListener('scrollend', handleScrollEnd)\r\n return () => el.removeEventListener('scrollend', handleScrollEnd)\r\n }, [draggableRef, persistKey, savePosition])\r\n\r\n // NOTE: Teleport logic is now handled by useCarouselTeleport hook\r\n\r\n const getActiveItemAtScroll = useCallback((scrollLeft: number, direction: number = 0, overrides?: { cardWidth: number, gap: number }) => {\r\n const activeCardWidth = overrides?.cardWidth ?? layout.cardWidth\r\n const activeGap = overrides?.gap ?? layout.gap\r\n const stride = activeCardWidth + activeGap\r\n\r\n if (stride <= 0 || items.length === 0) return null\r\n\r\n // Use cached domStride from layout hook (measured on resize, not per-scroll)\r\n // This avoids triggering layout reflow on every scroll event\r\n const activeStride = layout.domStride > 0 ? layout.domStride : stride\r\n const effectiveScroll = scrollLeft\r\n\r\n const rawIndex = effectiveScroll / activeStride\r\n let totalIndex: number\r\n\r\n // DEBUG: Selection Input\r\n logger.log('NAV', 'getActiveItemAtScroll START', {\r\n scrollLeft,\r\n effectiveScroll,\r\n calculatedStride: stride,\r\n domStride: activeStride,\r\n rawIndex,\r\n direction,\r\n overrides\r\n })\r\n\r\n // EAGER SELECTION: Symmetrical directional bias\r\n // Use 30% threshold on mobile for snappy feedback, 50% (standard round) on desktop\r\n const EAGER_THRESHOLD = (isMobile && eagerSelectionOnMobile) ? 0.3 : 0.5\r\n\r\n if (direction > 0) {\r\n // Swiping forward (left swipe): eagerly select next item at threshold\r\n totalIndex = Math.floor(rawIndex + (1 - EAGER_THRESHOLD))\r\n } else if (direction < 0) {\r\n // Swiping backward (right swipe): eagerly select prev item at threshold\r\n totalIndex = Math.ceil(rawIndex - (1 - EAGER_THRESHOLD))\r\n } else {\r\n // Idle: Standard round (closest item)\r\n totalIndex = Math.round(rawIndex)\r\n }\r\n\r\n let activeIndex: number\r\n\r\n if (infinite) {\r\n activeIndex = ((totalIndex - bufferBeforeCount) % items.length + items.length) % items.length\r\n } else {\r\n activeIndex = Math.max(0, Math.min(totalIndex, items.length - 1))\r\n }\r\n\r\n // DEEP LOG: Selection params\r\n logger.log('NAV', 'getActiveItemAtScroll RESULT', {\r\n scrollLeft,\r\n stride,\r\n rawIndex,\r\n totalIndex,\r\n activeIndex,\r\n bufferBeforeCount,\r\n itemId: (items[activeIndex] as any)?.id,\r\n title: (items[activeIndex] as any)?.title\r\n })\r\n\r\n return items[activeIndex] || null\r\n }, [layout.cardWidth, layout.gap, layout.domStride, items, infinite, bufferBeforeCount, isMobile, eagerSelectionOnMobile])\r\n\r\n const onScrollToItemComplete = useCallback((source: string) => {\r\n const el = draggableRef.current\r\n if (!el) return\r\n\r\n const ctx = getContext()\r\n // INTERRUPTION GUARD: If target is already null, it was interrupted by \r\n // PointerDown/Wheel/TouchStart. We MUST NOT restore snap because \r\n // the user is now manually controlling the carousel.\r\n if (ctx.pendingTarget === null && source !== 'safety-timeout') {\r\n // We can't easily get the click ID here without passing it through, \r\n // but strictly speaking we just need to know if we should cleanup.\r\n // If debugging is needed we can add context.\r\n logger.log('NAV', `ScrollToItem found pending=null (interrupted) via ${source}`)\r\n return\r\n }\r\n\r\n if (ctx.isPreTeleporting) {\r\n logger.log('NAV', `ScrollToItem finished but isPreTeleporting=true, skipping cleanup`)\r\n return\r\n }\r\n\r\n logger.log('NAV', `ScrollToItem complete via ${source}!`)\r\n transition({ type: 'SCROLL_COMPLETE' })\r\n if (infinite && el) {\r\n el.style.scrollSnapType = ''\r\n logger.log('NAV', `Snap restored after ScrollToItem`)\r\n }\r\n }, [infinite, draggableRef, getContext, transition])\r\n\r\n const { waitForScrollCompletion: waitForScrollCompletionForClick } = useScrollCompletion({\r\n ref: draggableRef,\r\n onComplete: onScrollToItemComplete,\r\n // We can reuse the snapTimeoutRef for safety, or let the hook manage its own.\r\n // Since scrollToThisItem used snapTimeoutRef as \"safety net\" before, we can pass it.\r\n // BUT wait, snapTimeoutRef is usually for restoring snap.\r\n // Check legacy code: \"snapTimeoutRef.current = setTimeout(...)\". Yes, it was reusing that ref.\r\n timeoutRef: snapTimeoutRef,\r\n })\r\n\r\n const scrollToThisItem = useCallback((index: number) => {\r\n const el = draggableRef.current\r\n if (!el) return\r\n\r\n const stride = layout.cardWidth + layout.gap\r\n if (stride <= 0) return\r\n\r\n let targetScroll = index * stride\r\n\r\n // CORRECTION: Use DOM positioning for clicks to match initialization logic\r\n // If we use index * stride, we'll scroll to the wrong place due to the accumulated stride error.\r\n if (infinite && el.children[index]) {\r\n const targetNode = el.children[index] as HTMLElement\r\n // Scroll so item center aligns with container center\r\n const itemCenter = targetNode.offsetLeft + (targetNode.offsetWidth / 2)\r\n const containerCenter = el.clientWidth / 2\r\n targetScroll = Math.max(0, itemCenter - containerCenter)\r\n logger.log('INTERACT', `Calculated DOM target for click`, { index, itemCenter, containerCenter, targetScroll })\r\n }\r\n\r\n // For infinite carousels, disable snap to prevent interference\r\n if (infinite) {\r\n el.style.scrollSnapType = 'none'\r\n }\r\n\r\n const thisClickId = Math.floor(Math.random() * 1000)\r\n logger.log('INTERACT', `━━━ Item Click #${thisClickId} START ━━━`, { index, targetScroll })\r\n\r\n transition({ type: 'ITEM_CLICK', targetScroll })\r\n el.scrollTo({\r\n left: targetScroll,\r\n behavior: 'smooth',\r\n })\r\n\r\n // SNAPPY: Trigger selection change immediately when the user clicks\r\n const targetItem = getActiveItemAtScroll(targetScroll)\r\n if (targetItem && onActiveItemChange) {\r\n lastActiveItemRef.current = targetItem\r\n onActiveItemChange(targetItem)\r\n }\r\n\r\n // CLEARANCE LOGIC: Use the shared hook to detect scroll completion\r\n waitForScrollCompletionForClick()\r\n }, [layout, draggableRef, infinite, getActiveItemAtScroll, onActiveItemChange, waitForScrollCompletionForClick, transition])\r\n\r\n // Navigation Hook - Phase 2: uses coordinator as single source of truth\r\n const { handleScrollNav, scrollLeft, scrollRight } = useCarouselNavigation({\r\n containerRef: draggableRef,\r\n infinite: !!infinite,\r\n layout: { cardWidth: layout.cardWidth, gap: layout.gap },\r\n cancelMomentum,\r\n preTeleport,\r\n coordinator: { transition, getPhase, getContext, contextRef, isBusy: () => getPhase() !== 'IDLE', isBlocking: () => getPhase() === 'BOUNCING' || getPhase() === 'TELEPORTING' },\r\n onNavigate: (targetScroll) => {\r\n const targetItem = getActiveItemAtScroll(targetScroll)\r\n if (targetItem && onActiveItemChange) {\r\n lastActiveItemRef.current = targetItem\r\n onActiveItemChange(targetItem)\r\n }\r\n },\r\n logger,\r\n })\r\n\r\n const activeItemCallbackRef = useRef(onActiveItemChange)\r\n activeItemCallbackRef.current = onActiveItemChange\r\n const getterRef = useRef(getActiveItemAtScroll)\r\n getterRef.current = getActiveItemAtScroll\r\n\r\n // Track last scroll position to determine direction for eager updates\r\n const lastScrollLeftRef = useRef(0)\r\n // Track last meaningful direction to prevent jitter/flicker in eager zones\r\n const lastMeaningfulDirectionRef = useRef(0)\r\n\r\n useEffect(() => {\r\n const currentCallback = activeItemCallbackRef.current\r\n if (!currentCallback || items.length === 0) return\r\n const el = draggableRef.current\r\n if (!el) return\r\n\r\n let timeoutId: NodeJS.Timeout\r\n\r\n const emitActiveItem = (scrollLeft: number) => {\r\n const ctx = contextRef.current\r\n // If we are currently teleporting, the scroll position is in flux\r\n if (ctx.isTeleporting) {\r\n lastScrollLeftRef.current = scrollLeft\r\n return\r\n }\r\n\r\n // If we are animating to a specific target (arrow click or direct click),\r\n // ignore intermediate scroll events.\r\n if (ctx.pendingTarget !== null) {\r\n lastScrollLeftRef.current = scrollLeft\r\n return\r\n }\r\n\r\n // Determine direction: 1 = Right, -1 = Left, 0 = Idle\r\n // STABILIZATION: Use a hysteresis threshold (e.g., 5px) to avoid flipping direction\r\n // on micro-movements or touch noise, which causes eager selection flickering.\r\n const delta = scrollLeft - lastScrollLeftRef.current\r\n let direction = lastMeaningfulDirectionRef.current\r\n\r\n if (Math.abs(delta) > 5) {\r\n direction = delta > 0 ? 1 : -1\r\n lastMeaningfulDirectionRef.current = direction\r\n }\r\n\r\n // Pass stabilized direction to getter for eager selection\r\n const activeItem = getterRef.current(scrollLeft, direction)\r\n\r\n // Use a stable reference check to avoid redundant calls\r\n if (activeItem && activeItem !== lastActiveItemRef.current) {\r\n lastActiveItemRef.current = activeItem\r\n // Always call the latest version of the callback\r\n if (activeItemCallbackRef.current) {\r\n activeItemCallbackRef.current(activeItem)\r\n }\r\n }\r\n\r\n lastScrollLeftRef.current = scrollLeft\r\n }\r\n\r\n const handleScrollEnd = () => {\r\n emitActiveItem(el.scrollLeft)\r\n }\r\n\r\n const handleScrollImmediate = () => {\r\n emitActiveItem(el.scrollLeft)\r\n }\r\n\r\n const supportsScrollEnd = typeof window !== 'undefined' && 'onscrollend' in window\r\n\r\n const scrollFallbackListener = () => {\r\n clearTimeout(timeoutId)\r\n timeoutId = setTimeout(handleScrollEnd, 150)\r\n }\r\n\r\n if (supportsScrollEnd) {\r\n el.addEventListener('scrollend', handleScrollEnd)\r\n } else {\r\n el.addEventListener('scroll', scrollFallbackListener, { passive: true })\r\n }\r\n\r\n el.addEventListener('scroll', handleScrollImmediate, { passive: true })\r\n\r\n return () => {\r\n el.removeEventListener('scrollend', handleScrollEnd)\r\n el.removeEventListener('scroll', scrollFallbackListener)\r\n el.removeEventListener('scroll', handleScrollImmediate)\r\n clearTimeout(timeoutId)\r\n }\r\n }, [items.length]) // Only items.length matters now, callback changes are handled via ref\r\n\r\n // Re-apply visuals after render\r\n useIsomorphicLayoutEffect(() => {\r\n const el = draggableRef.current\r\n if (!el) return\r\n\r\n // Phase 2: Safe Initial Measurement\r\n // INITIAL RENDER ONLY: Measure synchronously before paint to prevent flicker\r\n if (isCacheDirty.current) {\r\n updateCache(el)\r\n isCacheDirty.current = false\r\n }\r\n applyVisuals(el)\r\n })\r\n\r\n\r\n // Interaction tracking to prevent \"Ghost Interruption\"\r\n // If user clicks an arrow, we don't want a subsequent \"ghost\" pointerdown (or fat finger overlap)\r\n // to immediately interrupt the scroll we just started.\r\n const lastInteractionRef = useRef(0)\r\n\r\n const handleArrowClick = (direction: 'left' | 'right') => {\r\n lastInteractionRef.current = Date.now()\r\n if (direction === 'left') scrollLeft()\r\n else scrollRight()\r\n }\r\n\r\n return (\r\n <div\r\n className=\"base-carousel-container relative carousel-hover-group overflow-hidden\"\r\n style={{\r\n paddingTop: verticalPadding,\r\n paddingBottom: verticalPadding\r\n }}\r\n >\r\n <CarouselArrow direction=\"left\" onClick={() => handleArrowClick('left')} className=\"prev\" />\r\n\r\n {/* SKELETON LOADER OVERLAY - shows while infinite carousel initializes */}\r\n {infinite && !isReady && (\r\n <div\r\n className=\"absolute inset-0 z-10 flex gap-6 overflow-hidden pointer-events-none px-4\"\r\n aria-hidden=\"true\"\r\n style={{\r\n paddingTop: 0,\r\n paddingBottom: 0,\r\n }}\r\n >\r\n {Array.from({ length: 8 }).map((_, i) => (\r\n <div\r\n key={i}\r\n className={`flex-shrink-0 ${itemClassName}`}\r\n style={{ width: widthCssValue }}\r\n >\r\n {renderSkeleton ? renderSkeleton(i) : (\r\n <div className=\"w-full h-full bg-gradient-to-br from-gray-100 via-gray-200 to-gray-100 animate-pulse rounded-md\" style={{ minHeight: '200px' }} />\r\n )}\r\n </div>\r\n ))}\r\n </div>\r\n )}\r\n\r\n <div\r\n onPointerDown={(e: React.PointerEvent<HTMLDivElement>) => {\r\n // GRACE PERIOD CHECK:\r\n // If an arrow was clicked < 400ms ago, ignore this pointer down.\r\n // This handles \"Ghost Clicks\" (simulated mouse events after touch) \r\n // and \"Fat Finger\" issues (touching container while tapping arrow).\r\n const timeSinceInteraction = Date.now() - lastInteractionRef.current\r\n if (timeSinceInteraction < 400) {\r\n logger.log('INTERACT', `Ignoring PointerDown during grace period (${timeSinceInteraction}ms)`)\r\n // Prevent this event from triggering browser defaults that might mess us up\r\n e.preventDefault()\r\n return\r\n }\r\n\r\n const hadPendingScroll = contextRef.current.pendingTarget !== null\r\n\r\n // 1. Clear pending arrow scroll target when user starts dragging\r\n if (hadPendingScroll) {\r\n logger.log('INTERACT', 'PointerDown: Interrupting programmatic scroll')\r\n transition({ type: 'USER_INTERRUPT' }) // Coordinator: SCROLLING -> IDLE (clears pendingTarget)\r\n // Force an immediate visual refresh since the smooth scroll stopped\r\n if (draggableRef.current) applyVisuals(draggableRef.current)\r\n }\r\n if (snapTimeoutRef.current) {\r\n logger.log('INTERACT', 'PointerDown: Clearing snap timeout')\r\n clearTimeout(snapTimeoutRef.current)\r\n snapTimeoutRef.current = null\r\n }\r\n\r\n // For touch - always restore snap after interrupting programmatic scroll\r\n // (Mouse/pen drags will disable snap again below)\r\n if (draggableRef.current && e.pointerType === 'touch') {\r\n draggableRef.current.style.scrollSnapType = ''\r\n }\r\n\r\n // 2. Only disable snap for mouse/pen drags\r\n if (e.pointerType !== 'touch' && draggableRef.current) {\r\n draggableRef.current.style.scrollSnapType = 'none'\r\n }\r\n\r\n // 3. Reset direction tracking for new swipe (prevents stale direction from previous swipe)\r\n lastMeaningfulDirectionRef.current = 0\r\n\r\n // 4. Call the draggable hook's handler\r\n events.onPointerDown(e)\r\n }}\r\n onWheel={() => {\r\n // Clear pending scroll target if user uses mouse wheel\r\n if (contextRef.current.pendingTarget !== null) {\r\n logger.log('INTERACT', 'Wheel: Interrupting programmatic scroll')\r\n transition({ type: 'USER_INTERRUPT' }) // Coordinator: SCROLLING -> IDLE (clears pendingTarget)\r\n if (draggableRef.current) draggableRef.current.style.scrollSnapType = ''\r\n }\r\n if (snapTimeoutRef.current) {\r\n logger.log('INTERACT', 'Wheel: Clearing snap timeout')\r\n clearTimeout(snapTimeoutRef.current)\r\n snapTimeoutRef.current = null\r\n }\r\n }}\r\n onTouchStart={() => {\r\n // Mobile Optimization: Minimal logic here.\r\n // 1. Clear timeout if exists (sync but cheap)\r\n if (snapTimeoutRef.current) {\r\n clearTimeout(snapTimeoutRef.current)\r\n snapTimeoutRef.current = null\r\n }\r\n // 2. Do NOT transition coordinator state here (too expensive).\r\n // Let the native scroll happen or useDraggableScroll handle the gesture.\r\n }}\r\n className={`base-carousel flex items-stretch overflow-x-auto overscroll-x-none scrollbar-hide select-none ${snap ? `snap-x snap-${snapType}` : ''}`}\r\n onPointerUp={events.onPointerUp}\r\n onPointerMove={events.onPointerMove}\r\n onLostPointerCapture={events.onLostPointerCapture}\r\n onClickCapture={events.onClickCapture}\r\n onDragStart={events.onDragStart}\r\n ref={setCarouselRef}\r\n style={{\r\n gap: `${resolvedGap}px`,\r\n cursor: isDragging ? 'grabbing' : 'grab',\r\n scrollBehavior: 'auto',\r\n // Optimization: tell browser this element is independent for rendering\r\n contain: 'paint layout',\r\n // Only apply center-padding for infinite carousels\r\n // Finite carousels should start/end at the edges\r\n ...(infinite ? {\r\n paddingLeft: `calc(50% - ${layout.cardWidth / 2}px)`,\r\n paddingRight: `calc(50% - ${layout.cardWidth / 2}px)`,\r\n scrollPaddingLeft: `calc(50% - ${layout.cardWidth / 2}px)`,\r\n scrollPaddingRight: `calc(50% - ${layout.cardWidth / 2}px)`,\r\n } : {\r\n paddingLeft: '16px',\r\n paddingRight: '16px',\r\n scrollPaddingLeft: '16px',\r\n scrollPaddingRight: '16px',\r\n }),\r\n minHeight: 0,\r\n opacity: (isReady || isInstant) ? 1 : 0,\r\n }}\r\n >\r\n {useMemo(() => allItems.map((item, index) => {\r\n // Unique keys for clones\r\n let type = 'item'\r\n // Check logic for types if needed or just use index prefixes\r\n // Buffer Before: 0 to bufferBeforeCount - 1\r\n // Original: bufferBeforeCount to bufferBeforeCount + items.length - 1\r\n // Buffer After: Rest\r\n let realIndex = 0\r\n if (infinite) {\r\n if (index < bufferBeforeCount) {\r\n type = 'clone-before'\r\n realIndex = index % items.length\r\n } else if (index >= bufferBeforeCount + items.length) {\r\n type = 'clone-after'\r\n realIndex = (index - bufferBeforeCount - items.length) % items.length\r\n } else {\r\n type = 'original'\r\n realIndex = index - bufferBeforeCount\r\n }\r\n } else {\r\n realIndex = index\r\n }\r\n\r\n const key = `${type}-${getItemKey(item, realIndex)}-${index}`\r\n // Use snap-start for finite carousels (edge alignment), snap-center for infinite\r\n const snapAlignment = infinite ? 'snap-center' : 'snap-start'\r\n\r\n return (\r\n <div\r\n key={key}\r\n className={`carousel-item flex-shrink-0 ${itemClassName} cursor-pointer ${snapAlignment} snap-stop-always`}\r\n style={{\r\n width: widthCssValue,\r\n WebkitFontSmoothing: 'subpixel-antialiased',\r\n WebkitTapHighlightColor: 'transparent',\r\n scrollSnapStop: 'always',\r\n contain: 'layout paint',\r\n }}\r\n >\r\n {renderItem(item, realIndex, { scrollToItem: () => scrollToThisItem(index) })}\r\n </div>\r\n )\r\n }), [allItems, infinite, bufferBeforeCount, items.length, getItemKey, renderItem, widthCssValue, itemClassName, scrollToThisItem])}\r\n </div>\r\n <CarouselArrow direction=\"right\" onClick={() => handleArrowClick('right')} className=\"next\" />\r\n </div >\r\n )\r\n}\r\n\r\n// Cast to any to allow generic props to pass through React.memo\r\n// This is a common pattern for generic memoized components\r\nexport const Carousel = memo(BaseCarouselInner) as typeof BaseCarouselInner\r\n","import { useRef, useState, useCallback, useEffect, type PointerEvent as ReactPointerEvent, type MouseEvent } from 'react'\r\n\r\n// Configuration constants\r\nconst MAX_VELOCITY = 500 // Maximum momentum speed\r\nconst MIN_VELOCITY_THRESHOLD = 10 // Minimum velocity to trigger momentum\r\nconst FRICTION = 0.96 // Momentum decay per frame\r\nconst VELOCITY_SMOOTHING = 0.15 // How quickly velocity responds to input\r\n\r\n// Bounce effect constants\r\nconst BOUNCE_DISTANCE = 35 // How far to bounce on momentum hit (pixels)\r\nconst BOUNCE_DURATION = 400 // Bounce animation duration (ms)\r\n\r\n// Rubber band pull constants\r\nconst PULL_RESISTANCE = 0.35 // How hard it is to pull at edges (lower = more resistance)\r\nconst MAX_PULL_DISTANCE = 80 // Maximum pull distance (pixels)\r\nconst SNAP_BACK_DURATION = 250 // How fast it snaps back when released (ms)\r\n\r\n// Snap-to-item constants\r\nconst SNAP_DURATION = 200 // Duration of snap animation (ms)\r\nconst SNAP_THRESHOLD = 30 // Minimum velocity to consider for snap direction\r\n\r\n\r\n\r\ninterface UseDraggableScrollOptions {\r\n infinite?: boolean\r\n hasNextPage?: boolean\r\n onEndReached?: () => void\r\n cardWidth?: number\r\n gap?: number\r\n cloneCount?: number\r\n friction?: number\r\n maxVelocity?: number\r\n}\r\n\r\nexport function useDraggableScroll({\r\n infinite = false,\r\n hasNextPage = false,\r\n onEndReached,\r\n cardWidth = 320,\r\n gap = 24,\r\n cloneCount = 3,\r\n friction,\r\n maxVelocity,\r\n}: UseDraggableScrollOptions = {}) {\r\n const ref = useRef<HTMLDivElement>(null)\r\n const [isDragging, setIsDragging] = useState(false)\r\n\r\n // Drag state\r\n const isDown = useRef(false)\r\n const startX = useRef(0)\r\n const scrollLeftStart = useRef(0)\r\n const currentPointerId = useRef<number | null>(null)\r\n\r\n // Velocity tracking\r\n const velocity = useRef(0)\r\n const lastTimestamp = useRef(0)\r\n const lastPageX = useRef(0)\r\n const animationFrameId = useRef<number | null>(null)\r\n\r\n // Edge pull state\r\n const currentPullOffset = useRef(0)\r\n const isPullingEdge = useRef(false)\r\n const startedAtLeftEdge = useRef(false)\r\n const startedAtRightEdge = useRef(false)\r\n const isBouncing = useRef(false)\r\n const lastEndReachedTime = useRef(0)\r\n\r\n // Infinite scroll state\r\n const stride = cardWidth + gap\r\n\r\n const cancelAnimation = useCallback((force = false) => {\r\n if (isBouncing.current && !force) return\r\n\r\n if (animationFrameId.current) {\r\n cancelAnimationFrame(animationFrameId.current)\r\n animationFrameId.current = null\r\n }\r\n if (ref.current) {\r\n ref.current.style.transform = ''\r\n }\r\n currentPullOffset.current = 0\r\n isPullingEdge.current = false\r\n isBouncing.current = false\r\n }, [])\r\n\r\n // Find nearest snap point (center of child to center of container)\r\n const findNearestSnapPoint = useCallback((currentScroll: number, direction: number) => {\r\n if (!ref.current) return currentScroll\r\n\r\n const container = ref.current\r\n const children = Array.from(container.children) as HTMLElement[]\r\n\r\n if (children.length === 0) return currentScroll\r\n\r\n let nearestPoint = currentScroll\r\n let minDistance = Infinity\r\n const containerCenter = currentScroll + container.clientWidth / 2\r\n\r\n children.forEach((child) => {\r\n // Snap to center of each child\r\n const childCenter = child.offsetLeft + child.offsetWidth / 2\r\n const distance = Math.abs(childCenter - containerCenter)\r\n\r\n // Calculate the exact scroll position to center this child\r\n const targetScroll = childCenter - container.clientWidth / 2\r\n\r\n // If we have a direction preference, favor that direction\r\n if (direction !== 0) {\r\n const isInDirection = direction > 0\r\n ? childCenter < containerCenter\r\n : childCenter > containerCenter\r\n\r\n if (isInDirection && distance < minDistance) {\r\n minDistance = distance\r\n nearestPoint = targetScroll\r\n }\r\n } else if (distance < minDistance) {\r\n minDistance = distance\r\n nearestPoint = targetScroll\r\n }\r\n })\r\n\r\n // Clamp to valid scroll range if not infinite (or handle clamping differently)\r\n const maxScroll = container.scrollWidth - container.clientWidth\r\n return Math.max(0, Math.min(maxScroll, nearestPoint))\r\n }, [])\r\n\r\n // Smooth snap animation to target position (for mouse drag)\r\n const snapToPosition = useCallback((targetScroll: number) => {\r\n if (!ref.current) return\r\n\r\n const el = ref.current\r\n const startScroll = el.scrollLeft\r\n const distance = targetScroll - startScroll\r\n\r\n if (Math.abs(distance) < 1) {\r\n // Even if no movement, re-enable snap\r\n el.style.scrollSnapType = ''\r\n return\r\n }\r\n\r\n const startTime = performance.now()\r\n\r\n const snapLoop = () => {\r\n const elapsed = performance.now() - startTime\r\n const progress = Math.min(elapsed / SNAP_DURATION, 1)\r\n\r\n // Smooth ease-out curve\r\n const easeProgress = 1 - Math.pow(1 - progress, 3)\r\n\r\n el.scrollLeft = startScroll + (distance * easeProgress)\r\n\r\n if (progress < 1) {\r\n animationFrameId.current = requestAnimationFrame(snapLoop)\r\n } else {\r\n el.scrollLeft = targetScroll\r\n animationFrameId.current = null\r\n // Re-enable CSS snap after custom positioning completes\r\n el.style.scrollSnapType = ''\r\n }\r\n }\r\n\r\n animationFrameId.current = requestAnimationFrame(snapLoop)\r\n }, [])\r\n\r\n const snapBack = useCallback(() => {\r\n if (!ref.current) return\r\n if (isBouncing.current) return\r\n\r\n const el = ref.current\r\n const startOffset = currentPullOffset.current\r\n const startTime = performance.now()\r\n\r\n currentPullOffset.current = 0\r\n isPullingEdge.current = false\r\n\r\n if (Math.abs(startOffset) < 1) {\r\n el.style.transform = ''\r\n return\r\n }\r\n\r\n const snapLoop = () => {\r\n const elapsed = performance.now() - startTime\r\n const progress = Math.min(elapsed / SNAP_BACK_DURATION, 1)\r\n const easeProgress = 1 - Math.pow(1 - progress, 3)\r\n const offset = startOffset * (1 - easeProgress)\r\n\r\n el.style.transform = Math.abs(offset) > 0.5 ? `translateX(${offset}px)` : ''\r\n\r\n if (progress < 1) {\r\n animationFrameId.current = requestAnimationFrame(snapLoop)\r\n } else {\r\n el.style.transform = ''\r\n animationFrameId.current = null\r\n }\r\n }\r\n\r\n animationFrameId.current = requestAnimationFrame(snapLoop)\r\n }, [])\r\n\r\n const triggerBounce = useCallback((direction: 'left' | 'right') => {\r\n if (!ref.current) return\r\n // Disable bounce if infinite (unless specific edge cases, but generally teleport handles it)\r\n // Or if we are loading next page\r\n if (infinite) return\r\n if (direction === 'right' && hasNextPage) return\r\n\r\n cancelAnimation(true)\r\n isBouncing.current = true\r\n\r\n const el = ref.current\r\n const startTime = performance.now()\r\n const bounceDirection = direction === 'left' ? 1 : -1\r\n\r\n const bounceLoop = () => {\r\n const elapsed = performance.now() - startTime\r\n const progress = Math.min(elapsed / BOUNCE_DURATION, 1)\r\n const easeProgress = Math.sin(progress * Math.PI)\r\n const offset = BOUNCE_DISTANCE * bounceDirection * easeProgress\r\n\r\n el.style.transform = `translateX(${offset}px)`\r\n\r\n if (progress < 1) {\r\n animationFrameId.current = requestAnimationFrame(bounceLoop)\r\n } else {\r\n el.style.transform = ''\r\n animationFrameId.current = null\r\n isBouncing.current = false\r\n }\r\n }\r\n\r\n animationFrameId.current = requestAnimationFrame(bounceLoop)\r\n }, [cancelAnimation, infinite, hasNextPage])\r\n\r\n const startMomentumScroll = useCallback(() => {\r\n if (!ref.current) return\r\n\r\n const el = ref.current\r\n const activeFriction = friction ?? FRICTION\r\n const activeMaxVelocity = maxVelocity ?? MAX_VELOCITY\r\n\r\n let currentVel = Math.max(-activeMaxVelocity, Math.min(activeMaxVelocity, velocity.current * 16))\r\n let lastLoopTime = performance.now()\r\n const initialDirection = currentVel > 0 ? 1 : currentVel < 0 ? -1 : 0\r\n\r\n if (Math.abs(currentVel) <= MIN_VELOCITY_THRESHOLD) {\r\n // No momentum, just snap to nearest\r\n const snapTarget = findNearestSnapPoint(el.scrollLeft, 0)\r\n snapToPosition(snapTarget)\r\n return\r\n }\r\n\r\n const maxScroll = el.scrollWidth - el.clientWidth\r\n\r\n if (el.scrollLeft <= 0 && currentVel > 0) {\r\n triggerBounce('left')\r\n return\r\n }\r\n if (el.scrollLeft >= maxScroll && currentVel < 0) {\r\n triggerBounce('right')\r\n return\r\n }\r\n\r\n const momentumLoop = () => {\r\n if (!ref.current) return\r\n\r\n const now = performance.now()\r\n const dt = now - lastLoopTime\r\n lastLoopTime = now\r\n\r\n const frameRatio = Math.min(dt / 16, 3)\r\n currentVel *= Math.pow(activeFriction, frameRatio)\r\n\r\n const prevScroll = el.scrollLeft\r\n const max = el.scrollWidth - el.clientWidth\r\n\r\n el.scrollLeft = prevScroll - (currentVel * frameRatio)\r\n const newScroll = el.scrollLeft\r\n\r\n // Check if scroll hit an edge\r\n // Logic updated for infinite:\r\n // infinite + hasNextPage -> no bounce right\r\n const hitLeft = currentVel > 0 && newScroll <= 0 && prevScroll > 0\r\n const hitRight = currentVel < 0 && newScroll >= max && prevScroll < max\r\n\r\n if (hitLeft) {\r\n if (!infinite) triggerBounce('left')\r\n return\r\n }\r\n if (hitRight) {\r\n if (!infinite && !(hasNextPage)) triggerBounce('right')\r\n return\r\n }\r\n\r\n // When velocity is low enough, snap to nearest item\r\n if (Math.abs(currentVel) < SNAP_THRESHOLD) {\r\n const snapTarget = findNearestSnapPoint(el.scrollLeft, initialDirection)\r\n snapToPosition(snapTarget)\r\n return\r\n }\r\n\r\n if (Math.abs(currentVel) > 0.5) {\r\n animationFrameId.current = requestAnimationFrame(momentumLoop)\r\n } else {\r\n // Snap when momentum ends\r\n const snapTarget = findNearestSnapPoint(el.scrollLeft, 0)\r\n snapToPosition(snapTarget)\r\n }\r\n }\r\n\r\n animationFrameId.current = requestAnimationFrame(momentumLoop)\r\n }, [triggerBounce, findNearestSnapPoint, snapToPosition, infinite, hasNextPage])\r\n\r\n const endDrag = useCallback(() => {\r\n if (!isDown.current) return\r\n\r\n isDown.current = false\r\n\r\n if (ref.current && currentPointerId.current !== null) {\r\n try {\r\n ref.current.releasePointerCapture(currentPointerId.current)\r\n } catch {\r\n // Already released\r\n }\r\n }\r\n currentPointerId.current = null\r\n\r\n if (isPullingEdge.current) {\r\n snapBack()\r\n setTimeout(() => setIsDragging(false), 0)\r\n return\r\n }\r\n\r\n if (isDragging) {\r\n startMomentumScroll()\r\n }\r\n setTimeout(() => setIsDragging(false), 0)\r\n }, [isDragging, startMomentumScroll, snapBack])\r\n\r\n const onPointerDown = useCallback((e: ReactPointerEvent<HTMLDivElement>) => {\r\n // For touch devices, let native scroll + CSS snap handle it\r\n if (e.pointerType === 'touch') {\r\n return\r\n }\r\n\r\n // For mouse, disable CSS snap so our custom drag works freely\r\n if (ref.current) {\r\n ref.current.style.scrollSnapType = 'none'\r\n }\r\n\r\n isDown.current = true\r\n cancelAnimation()\r\n window.getSelection()?.removeAllRanges()\r\n\r\n if (ref.current) {\r\n const el = ref.current\r\n const maxScroll = el.scrollWidth - el.clientWidth\r\n\r\n\r\n // We don't set capture here yet to allow simple clicks to pass through\r\n // Capture will be set in onPointerMove if movement threshold is exceeded\r\n startX.current = e.pageX\r\n scrollLeftStart.current = el.scrollLeft\r\n lastPageX.current = e.pageX\r\n lastTimestamp.current = performance.now()\r\n velocity.current = 0\r\n\r\n // Use 20px tolerance for left edge to account for padding in finite carousels\r\n startedAtLeftEdge.current = el.scrollLeft <= 20\r\n startedAtRightEdge.current = el.scrollLeft >= maxScroll - 5\r\n }\r\n }, [cancelAnimation])\r\n\r\n const onPointerUp = useCallback((e: ReactPointerEvent<HTMLDivElement>) => {\r\n if (!isDown.current) return\r\n\r\n if (performance.now() - lastTimestamp.current > 80) {\r\n velocity.current = 0\r\n }\r\n\r\n endDrag()\r\n }, [endDrag])\r\n\r\n const onPointerMove = useCallback((e: ReactPointerEvent<HTMLDivElement>) => {\r\n if (!isDown.current || !ref.current) return\r\n e.preventDefault()\r\n\r\n const now = performance.now()\r\n const pageX = e.pageX\r\n const el = ref.current\r\n\r\n // Optimization: Removed getBoundingClientRect() to avoid reflows\r\n\r\n const timeDelta = now - lastTimestamp.current\r\n if (timeDelta > 0) {\r\n const instantVel = (pageX - lastPageX.current) / timeDelta\r\n velocity.current = velocity.current * (1 - VELOCITY_SMOOTHING) + instantVel * VELOCITY_SMOOTHING\r\n lastTimestamp.current = now\r\n lastPageX.current = pageX\r\n }\r\n\r\n const x = pageX\r\n const walk = x - startX.current\r\n const intendedScroll = scrollLeftStart.current - walk\r\n const maxScroll = el.scrollWidth - el.clientWidth\r\n\r\n const isAtLeftEdge = el.scrollLeft <= 20 // 20px tolerance for padding\r\n const isAtRightEdge = el.scrollLeft >= maxScroll - 5\r\n\r\n // Pull resistance logic disabled if infinite\r\n const canPullLeft = !infinite && !isBouncing.current && startedAtLeftEdge.current && isAtLeftEdge && intendedScroll < 0\r\n const canPullRight = !infinite && !hasNextPage && !isBouncing.current && startedAtRightEdge.current && isAtRightEdge && intendedScroll > maxScroll\r\n\r\n if (canPullLeft || canPullRight) {\r\n isPullingEdge.current = true\r\n\r\n let pullAmount: number\r\n if (canPullLeft) {\r\n pullAmount = -intendedScroll * PULL_RESISTANCE\r\n } else {\r\n pullAmount = -(intendedScroll - maxScroll) * PULL_RESISTANCE\r\n }\r\n\r\n const sign = pullAmount > 0 ? 1 : -1\r\n const absPull = Math.min(Math.abs(pullAmount), MAX_PULL_DISTANCE)\r\n const dampedPull = sign * Math.sqrt(absPull / MAX_PULL_DISTANCE) * MAX_PULL_DISTANCE\r\n\r\n currentPullOffset.current = dampedPull\r\n el.style.transform = `translateX(${dampedPull}px)`\r\n\r\n if (!isDragging) setIsDragging(true)\r\n } else {\r\n // Normal drag\r\n if (isPullingEdge.current) {\r\n el.style.transform = ''\r\n currentPullOffset.current = 0\r\n isPullingEdge.current = false\r\n }\r\n\r\n if (Math.abs(walk) > 10) {\r\n if (!isDragging) {\r\n setIsDragging(true)\r\n // Set pointer capture when we are sure it's a drag\r\n try {\r\n el.setPointerCapture(e.pointerId)\r\n currentPointerId.current = e.pointerId\r\n } catch (err) {\r\n // Ignore\r\n }\r\n }\r\n el.scrollLeft = intendedScroll\r\n }\r\n }\r\n }, [isDragging, endDrag, infinite, hasNextPage])\r\n\r\n const onClickCapture = useCallback((e: MouseEvent) => {\r\n if (isDragging) {\r\n e.preventDefault()\r\n e.stopPropagation()\r\n }\r\n }, [isDragging])\r\n\r\n // Safety net: Listen to window for pointerup to ensure we always clean up\r\n // even if pointer capture fails or is interrupted.\r\n useEffect(() => {\r\n const handleWindowPointerUp = (e: any) => {\r\n if (isDown.current) {\r\n // If the event target is the element itself, the normal onPointerUp \r\n // will handle it. But if it's something else (released outside), \r\n // this is our fallback.\r\n if (e.target !== ref.current && !ref.current?.contains(e.target as Node)) {\r\n endDrag()\r\n }\r\n }\r\n }\r\n\r\n window.addEventListener('pointerup', handleWindowPointerUp)\r\n window.addEventListener('blur', endDrag) // Close on window blur too\r\n\r\n return () => {\r\n window.removeEventListener('pointerup', handleWindowPointerUp)\r\n window.removeEventListener('blur', endDrag)\r\n }\r\n }, [endDrag])\r\n\r\n useEffect(() => cancelAnimation, [cancelAnimation])\r\n\r\n // Expose method to adjust internal state when parent teleports scroll position\r\n const adjustScroll = useCallback((delta: number) => {\r\n scrollLeftStart.current += delta\r\n // Also adjust current velocity tracking to prevent jumps? Not needed for velocity, just position.\r\n }, [])\r\n\r\n useEffect(() => {\r\n const el = ref.current\r\n if (!el) return\r\n\r\n const handleScroll = () => {\r\n // Check for OnEndReached (Append Logic - Legacy support if needed)\r\n if (onEndReached) {\r\n const scrollLeft = el.scrollLeft\r\n const scrollWidth = el.scrollWidth\r\n const clientWidth = el.clientWidth\r\n\r\n const distToEnd = scrollWidth - (scrollLeft + clientWidth)\r\n const threshold = 2 * clientWidth\r\n\r\n if (distToEnd < threshold) {\r\n const now = performance.now()\r\n if (now - lastEndReachedTime.current > 1000) {\r\n onEndReached()\r\n lastEndReachedTime.current = now\r\n }\r\n }\r\n }\r\n }\r\n\r\n el.addEventListener('scroll', handleScroll, { passive: true })\r\n // Initial check\r\n handleScroll()\r\n\r\n return () => {\r\n el.removeEventListener('scroll', handleScroll)\r\n }\r\n }, [onEndReached])\r\n\r\n // Touch-based edge effects for infinite carousels\r\n // Currently disabled for performance optimization (Phase 1)\r\n // The previous implementation used heavy paint operations (gradients) on the main thread\r\n useEffect(() => {\r\n const el = ref.current\r\n if (!el || infinite) return\r\n\r\n // Lightweight scroll end detection (optional future use)\r\n }, [infinite])\r\n\r\n return {\r\n ref,\r\n isDragging,\r\n cancelMomentum: () => cancelAnimation(true),\r\n adjustScroll,\r\n events: {\r\n onPointerDown,\r\n onPointerUp,\r\n onPointerMove,\r\n onLostPointerCapture: onPointerUp,\r\n onClickCapture,\r\n onDragStart: (e: MouseEvent) => e.preventDefault(),\r\n },\r\n }\r\n}\r\n","import { useState, useEffect, useRef, useCallback } from 'react'\r\n\r\n// ┌─────────────────────────────────────────────────────────────────────────────┐\r\n// │ SESSION CACHE: Tracks which resources have been loaded this session │\r\n// │ │\r\n// │ KNOWN LIMITATION (Safari iOS): │\r\n// │ This in-memory Set may be cleared when Safari aggressively reclaims memory: │\r\n// │ - Tab backgrounding under memory pressure │\r\n// │ - Long scroll away from component (DOM virtualization) │\r\n// │ - Extended inactivity (30+ minutes) │\r\n// │ │\r\n// │ FUTURE ENHANCEMENT (if Safari issues persist): │\r\n// │ Add sessionStorage fallback - replace seenResources.has(key) with: │\r\n// │ function hasBeenSeen(key: string): boolean { │\r\n// │ if (seenResources.has(key)) return true │\r\n// │ if (typeof sessionStorage !== 'undefined') { │\r\n// │ return sessionStorage.getItem(`seen-${key}`) === '1' │\r\n// │ } │\r\n// │ return false │\r\n// │ } │\r\n// │ And update markAsSeen to write to both memory and sessionStorage. │\r\n// └─────────────────────────────────────────────────────────────────────────────┘\r\nconst seenResources = new Set<string>()\r\n\r\nexport interface UseLoadingStateOptions {\r\n /** Unique key for session cache. If provided, enables caching behavior. */\r\n cacheKey?: string\r\n /** Delay in ms before showing skeleton. Default: 500 */\r\n skeletonDelay?: number\r\n /** Hard fallback timeout in ms. Guarantees isReady after this. Default: 3000 */\r\n fallbackTimeout?: number\r\n /** \r\n * If true, start in ready state immediately (e.g., for already-mounted carousels).\r\n * Useful when the component should skip the loading phase entirely.\r\n */\r\n startReady?: boolean\r\n}\r\n\r\nexport interface UseLoadingStateReturn {\r\n /** True when resource is loaded OR fallback has fired */\r\n isReady: boolean\r\n /** True if loading takes longer than skeletonDelay AND resource is not cached */\r\n showSkeleton: boolean\r\n /** True if resource was cached (instant load, no fade needed) */\r\n isInstant: boolean\r\n /** Call this when your resource finishes loading (e.g., image onLoad) */\r\n markReady: () => void\r\n}\r\n\r\n/**\r\n * Unified hook for managing loading states with:\r\n * - First load: fade in, optional skeleton after delay\r\n * - Cached load: instant (no fade, no skeleton)\r\n * - Strict fallback: guaranteed ready after timeout\r\n */\r\nexport function useLoadingState({\r\n cacheKey,\r\n skeletonDelay = 500,\r\n fallbackTimeout = 3000,\r\n startReady = false,\r\n}: UseLoadingStateOptions = {}): UseLoadingStateReturn {\r\n // Check if this resource was seen before in this session\r\n const wasCached = cacheKey ? seenResources.has(cacheKey) : false\r\n\r\n const [isReady, setIsReady] = useState(startReady || wasCached)\r\n const [showSkeleton, setShowSkeleton] = useState(false)\r\n const [isInstant] = useState(wasCached)\r\n\r\n // Refs to prevent stale closures and duplicate executions\r\n const mountedRef = useRef(true)\r\n const readyFiredRef = useRef(startReady || wasCached)\r\n const skeletonTimerRef = useRef<NodeJS.Timeout | null>(null)\r\n const fallbackTimerRef = useRef<NodeJS.Timeout | null>(null)\r\n\r\n // Mark resource as ready (called by consumer, e.g., onLoad)\r\n const markReady = useCallback(() => {\r\n if (readyFiredRef.current) return\r\n readyFiredRef.current = true\r\n\r\n // Clear timers\r\n if (skeletonTimerRef.current) clearTimeout(skeletonTimerRef.current)\r\n if (fallbackTimerRef.current) clearTimeout(fallbackTimerRef.current)\r\n\r\n // Add to cache\r\n if (cacheKey) seenResources.add(cacheKey)\r\n\r\n // Update state\r\n if (mountedRef.current) {\r\n setIsReady(true)\r\n setShowSkeleton(false)\r\n }\r\n }, [cacheKey])\r\n\r\n useEffect(() => {\r\n mountedRef.current = true\r\n\r\n // If already ready (cached or startReady), skip all timers\r\n if (readyFiredRef.current) return\r\n\r\n // Start skeleton delay timer\r\n skeletonTimerRef.current = setTimeout(() => {\r\n if (mountedRef.current && !readyFiredRef.current) {\r\n setShowSkeleton(true)\r\n }\r\n }, skeletonDelay)\r\n\r\n // Start hard fallback timer\r\n fallbackTimerRef.current = setTimeout(() => {\r\n if (mountedRef.current && !readyFiredRef.current) {\r\n readyFiredRef.current = true\r\n if (cacheKey) seenResources.add(cacheKey)\r\n setIsReady(true)\r\n setShowSkeleton(false)\r\n }\r\n }, fallbackTimeout)\r\n\r\n return () => {\r\n mountedRef.current = false\r\n if (skeletonTimerRef.current) clearTimeout(skeletonTimerRef.current)\r\n if (fallbackTimerRef.current) clearTimeout(fallbackTimerRef.current)\r\n }\r\n }, [cacheKey, skeletonDelay, fallbackTimeout])\r\n\r\n return { isReady, showSkeleton, isInstant, markReady }\r\n}\r\n\r\n/**\r\n * Utility to clear the session cache (for testing)\r\n */\r\nexport function clearLoadingStateCache() {\r\n seenResources.clear()\r\n}\r\n","import { useEffect, useRef } from 'react'\r\nimport type { UseCarouselCoordinatorReturn } from './useCarouselCoordinator'\r\nimport type { CarouselLoggerInstance } from '../logger'\r\n\r\nexport interface UseCarouselTeleportOptions {\r\n /** Ref to the scrollable carousel container */\r\n containerRef: React.RefObject<HTMLDivElement | null>\r\n /** Whether this is an infinite carousel */\r\n infinite: boolean\r\n /** Number of items in the original set */\r\n itemsCount: number\r\n /** Width of each card in pixels */\r\n cardWidth: number\r\n /** Gap between cards in pixels */\r\n gap: number\r\n /** Number of buffer items before the original set */\r\n bufferBeforeCount: number\r\n /** Function to apply visual effects after teleport */\r\n applyVisuals: (el: HTMLElement, scrollLeft?: number) => void\r\n /** Function to adjust scroll tracking in useDraggableScroll */\r\n adjustScroll: (delta: number) => void\r\n /** Delay in ms before clearing pre-teleport flag */\r\n preTeleportClearDelayMs: number\r\n /** Coordinator for state management (required - Phase 3) */\r\n coordinator: UseCarouselCoordinatorReturn\r\n /** Optional logger for debugging */\r\n logger?: CarouselLoggerInstance\r\n}\r\n\r\n/**\r\n * Hook that handles the teleport logic for infinite carousels.\r\n * \r\n * THE TELEPORT LOOP - HYBRID STRATEGY\r\n * Desktop (mouse): Teleport during scroll - works perfectly, no compositor conflict\r\n * Mobile (touch): \r\n * - Teleport on 'scrollend' - natural stop after momentum\r\n * - Teleport on 'pointerdown' - \"catch & reset\" when user touches during momentum\r\n */\r\nexport function useCarouselTeleport({\r\n containerRef,\r\n infinite,\r\n itemsCount,\r\n cardWidth,\r\n gap,\r\n bufferBeforeCount,\r\n applyVisuals,\r\n adjustScroll,\r\n preTeleportClearDelayMs,\r\n coordinator,\r\n logger,\r\n}: UseCarouselTeleportOptions) {\r\n // HYBRID STRATEGY: Track if last interaction was touch (mobile) to disable during-scroll teleport\r\n const isTouchInteraction = useRef(false)\r\n // Internal ref for scrollend listener (holds function reference, managed by this hook)\r\n const scrollEndListenerRef = useRef<(() => void) | null>(null)\r\n\r\n // ═══════════════════════════════════════════════════════════════════════════\r\n // APPENDIX A FIX: Store unstable dependencies in refs to prevent effect re-run\r\n // These values change frequently but don't require handler re-attachment\r\n // ═══════════════════════════════════════════════════════════════════════════\r\n const coordinatorRef = useRef(coordinator)\r\n const applyVisualsRef = useRef(applyVisuals)\r\n const adjustScrollRef = useRef(adjustScroll)\r\n const loggerRef = useRef(logger)\r\n\r\n // Sync refs on every render (no effect trigger)\r\n coordinatorRef.current = coordinator\r\n applyVisualsRef.current = applyVisuals\r\n adjustScrollRef.current = adjustScroll\r\n loggerRef.current = logger\r\n\r\n useEffect(() => {\r\n const el = containerRef.current\r\n if (!el || !infinite || itemsCount === 0) return\r\n\r\n let stride = cardWidth + gap\r\n\r\n // CORRECTION: Measure real DOM stride to ensure teleport is visually perfect.\r\n // If we use calculated stride (162) vs real (166), teleporting 12 items (buffer)\r\n // results in a ~48px jump (misalignment) because 12 * 4px error = 48px.\r\n if (infinite && el.children.length > 1) {\r\n const firstChild = el.children[0] as HTMLElement\r\n const secondChild = el.children[1] as HTMLElement\r\n const domStride = secondChild.offsetLeft - firstChild.offsetLeft\r\n\r\n if (domStride > 0 && Math.abs(domStride - stride) > 1) {\r\n loggerRef.current?.log('TELEPORT', `Using DOM stride for teleport accuracy`, { calculated: stride, measured: domStride })\r\n stride = domStride\r\n }\r\n }\r\n\r\n const originalSetWidth = itemsCount * stride\r\n const bufferBeforeWidth = bufferBeforeCount * stride\r\n\r\n let rafId: number | null = null\r\n\r\n // Shared teleport logic - called from scroll, scrollend, or pointerdown\r\n const performTeleport = (source: string): boolean => {\r\n const ctx = coordinatorRef.current.getContext()\r\n if (ctx.isTeleporting) {\r\n loggerRef.current?.log('TELEPORT', `Teleport in progress (${source})`)\r\n return false\r\n }\r\n if (ctx.pendingTarget !== null && source === 'scroll') {\r\n loggerRef.current?.log('TELEPORT', `Pending target exists (${source})`, { target: ctx.pendingTarget })\r\n return false\r\n }\r\n\r\n const currentScroll = el.scrollLeft\r\n\r\n if (currentScroll >= bufferBeforeWidth + originalSetWidth) {\r\n // Teleport Back\r\n const overshoot = currentScroll - bufferBeforeWidth\r\n const setsPassed = Math.floor(overshoot / originalSetWidth)\r\n if (setsPassed > 0) {\r\n const adjust = setsPassed * originalSetWidth\r\n loggerRef.current?.log('TELEPORT', `⚡ BACKWARD (${source})`, {\r\n from: currentScroll.toFixed(0),\r\n to: (currentScroll - adjust).toFixed(0)\r\n })\r\n coordinatorRef.current.transition({ type: 'SET_TELEPORTING', value: true })\r\n // Momentum cancel for desktop only\r\n if (!isTouchInteraction.current) {\r\n el.scrollTo({ left: el.scrollLeft, behavior: 'auto' })\r\n }\r\n const newPos = currentScroll - adjust\r\n el.scrollLeft = newPos\r\n // Platform-optimized visuals\r\n if (!isTouchInteraction.current) {\r\n applyVisualsRef.current(el, newPos)\r\n } else {\r\n requestAnimationFrame(() => applyVisualsRef.current(el, newPos))\r\n }\r\n adjustScrollRef.current(-adjust)\r\n coordinatorRef.current.transition({ type: 'SET_TELEPORTING', value: false })\r\n coordinatorRef.current.transition({ type: 'END_TELEPORT' })\r\n return true\r\n }\r\n } else if (currentScroll < bufferBeforeWidth) {\r\n // Teleport Forward\r\n loggerRef.current?.log('TELEPORT', `⚡ FORWARD (${source})`, {\r\n from: currentScroll.toFixed(0),\r\n to: (currentScroll + originalSetWidth).toFixed(0)\r\n })\r\n coordinatorRef.current.transition({ type: 'SET_TELEPORTING', value: true })\r\n // Momentum cancel for desktop only\r\n if (!isTouchInteraction.current) {\r\n el.scrollTo({ left: el.scrollLeft, behavior: 'auto' })\r\n }\r\n const newPos = currentScroll + originalSetWidth\r\n el.scrollLeft = newPos\r\n // Platform-optimized visuals\r\n if (!isTouchInteraction.current) {\r\n applyVisualsRef.current(el, newPos)\r\n } else {\r\n requestAnimationFrame(() => applyVisualsRef.current(el, newPos))\r\n }\r\n adjustScrollRef.current(originalSetWidth)\r\n coordinatorRef.current.transition({ type: 'SET_TELEPORTING', value: false })\r\n coordinatorRef.current.transition({ type: 'END_TELEPORT' })\r\n return true\r\n }\r\n return false\r\n }\r\n\r\n const handleScroll = () => {\r\n if (rafId) {\r\n return\r\n }\r\n rafId = requestAnimationFrame(() => {\r\n const rafStartTime = performance.now()\r\n applyVisualsRef.current(el)\r\n\r\n const ctx = coordinatorRef.current.getContext()\r\n if (ctx.isTeleporting || ctx.isPreTeleporting) {\r\n rafId = null\r\n return\r\n }\r\n\r\n // HYBRID: Only teleport during scroll for DESKTOP (non-touch)\r\n if (!isTouchInteraction.current) {\r\n performTeleport('scroll')\r\n } else {\r\n loggerRef.current?.log('TELEPORT', 'Touch drag active: skipping scroll-teleport')\r\n }\r\n\r\n rafId = null\r\n\r\n const rafDuration = performance.now() - rafStartTime\r\n if (rafDuration > 5) {\r\n loggerRef.current?.log('TELEPORT', `RAF took ${rafDuration.toFixed(1)}ms`)\r\n }\r\n })\r\n }\r\n\r\n // MOBILE: Teleport on scrollend - momentum has naturally stopped\r\n const handleScrollEnd = () => {\r\n if (!isTouchInteraction.current) return // Desktop already handled in scroll\r\n if (coordinatorRef.current.getContext().pendingTarget !== null) return // Arrow nav in progress\r\n\r\n // SNAP SAFETY CHECK:\r\n // Mobile Safari fires scrollend BEFORE the CSS Snap animation finishes.\r\n // If we teleport mid-snap (e.g. at index 72.5), we kill momentum and the browser\r\n // snaps back to 72 instead of continuing to 73.\r\n // We must only teleport if we are effectively \"settled\" on a slot.\r\n const currentScroll = el.scrollLeft\r\n\r\n // Calculate raw index to check alignment\r\n // Note: We need paddingOffset and stride. Stride is available in scope.\r\n // PaddingOffset we can infer from first child or assume standard centering.\r\n let paddingOffset = 0\r\n if (el.children.length > 0) {\r\n paddingOffset = (el.children[0] as HTMLElement).offsetLeft\r\n }\r\n\r\n const rawIndex = (currentScroll - paddingOffset) / stride\r\n const snapSkew = Math.abs(rawIndex - Math.round(rawIndex))\r\n\r\n // Tolerance: 5% of a card width (e.g. ~8px for a 160px card)\r\n if (snapSkew > 0.05) {\r\n loggerRef.current?.log('TELEPORT', 'Skipping teleport - mid-snap detected', {\r\n rawIndex: rawIndex.toFixed(3),\r\n snapSkew: snapSkew.toFixed(3)\r\n })\r\n return\r\n }\r\n\r\n loggerRef.current?.log('TELEPORT', 'Mobile scroll settled, checking teleport')\r\n performTeleport('scrollend')\r\n }\r\n\r\n // MOBILE: Teleport on pointerdown - \"Safety Valve\" Strategy\r\n // Only teleport if the user is dangerously close to the physical edge.\r\n // Otherwise, stay quiet to avoid fighting the browser's touch interaction (causes flicker).\r\n const handlePointerDown = (e: PointerEvent) => {\r\n if (e.pointerType === 'touch') {\r\n if (!isTouchInteraction.current) loggerRef.current?.log('TELEPORT', 'Switched to TOUCH')\r\n isTouchInteraction.current = true\r\n\r\n // SAFETY VALVE CHECK:\r\n // We have a huge buffer (~6000px). Only teleport if we are running out of runway.\r\n const SAFETY_THRESHOLD = 500 // pixels indicating \"dangerously close to edge\"\r\n const maxScroll = el.scrollWidth - el.clientWidth\r\n\r\n const isDangerouslyCloseToStart = el.scrollLeft < SAFETY_THRESHOLD\r\n const isDangerouslyCloseToEnd = el.scrollLeft > maxScroll - SAFETY_THRESHOLD\r\n\r\n if (isDangerouslyCloseToStart || isDangerouslyCloseToEnd) {\r\n loggerRef.current?.log('TELEPORT', '⚠️ SAFETY VALVE TRIGGERED: Teleporting during touch to prevent hitting wall')\r\n const didTeleport = performTeleport('pointerdown')\r\n if (didTeleport) {\r\n loggerRef.current?.log('TELEPORT', 'Catch & reset teleport performed (Safety Valve)')\r\n }\r\n } else {\r\n // Safe zone - Do nothing! Let the user drag native scroll without interference.\r\n // This is the key to \"Zero Flicker\".\r\n }\r\n } else {\r\n if (isTouchInteraction.current) loggerRef.current?.log('TELEPORT', 'Switched to MOUSE/PEN')\r\n isTouchInteraction.current = false\r\n }\r\n }\r\n\r\n el.addEventListener('scroll', handleScroll, { passive: true })\r\n el.addEventListener('scrollend', handleScrollEnd)\r\n el.addEventListener('pointerdown', handlePointerDown, { passive: true })\r\n\r\n loggerRef.current?.log('TELEPORT', 'Hybrid teleport handlers attached', {\r\n stride,\r\n originalSetWidth,\r\n bufferBeforeWidth,\r\n itemsCount\r\n })\r\n\r\n return () => {\r\n el.removeEventListener('scroll', handleScroll)\r\n el.removeEventListener('scrollend', handleScrollEnd)\r\n el.removeEventListener('pointerdown', handlePointerDown)\r\n if (rafId) cancelAnimationFrame(rafId)\r\n loggerRef.current?.log('TELEPORT', 'Scroll handlers removed')\r\n }\r\n }, [containerRef, infinite, itemsCount, cardWidth, gap, bufferBeforeCount])\r\n\r\n /**\r\n * Proactive pre-teleport for arrow navigation.\r\n */\r\n const preTeleport = (targetScroll: number): number => {\r\n const el = containerRef.current\r\n if (!el || !infinite || itemsCount === 0) return targetScroll\r\n\r\n // FIX: Measure real DOM stride for teleport accuracy (matches scroll handler)\r\n // Using calculated stride (cardWidth + gap) causes misalignment when DOM stride differs\r\n // e.g., calculated=232 vs DOM=236 → 40px drift per 10-item cycle\r\n let stride = cardWidth + gap\r\n if (el.children.length > 1) {\r\n const firstChild = el.children[0] as HTMLElement\r\n const secondChild = el.children[1] as HTMLElement\r\n const domStride = secondChild.offsetLeft - firstChild.offsetLeft\r\n if (domStride > 0 && Math.abs(domStride - stride) > 1) {\r\n logger?.log('TELEPORT', 'preTeleport using DOM stride', { calculated: stride, measured: domStride })\r\n stride = domStride\r\n }\r\n }\r\n\r\n const originalSetWidth = itemsCount * stride\r\n const bufferBeforeWidth = bufferBeforeCount * stride\r\n\r\n // Check if target is outside safe zone\r\n const needsLeftPreTeleport = targetScroll < bufferBeforeWidth\r\n const needsRightPreTeleport = targetScroll >= bufferBeforeWidth + originalSetWidth\r\n\r\n if (!needsLeftPreTeleport && !needsRightPreTeleport) {\r\n logger?.log('TELEPORT', 'No pre-teleport needed', { targetScroll })\r\n return targetScroll\r\n }\r\n\r\n const direction = needsLeftPreTeleport ? 'LEFT' : 'RIGHT'\r\n const offset = needsLeftPreTeleport ? originalSetWidth : -originalSetWidth\r\n const preTeleportStart = performance.now()\r\n\r\n logger?.log('TELEPORT', `⚡ ${direction} PRE-TELEPORT START`, {\r\n oldTarget: targetScroll.toFixed(1),\r\n newTarget: (targetScroll + offset).toFixed(1)\r\n })\r\n\r\n // Adjust target\r\n const adjustedTarget = targetScroll + offset\r\n\r\n // Block scroll handler from teleporting during this operation\r\n coordinator.transition({ type: 'SET_PRE_TELEPORTING', value: true })\r\n coordinator.transition({ type: 'START_PRE_TELEPORT' })\r\n logger?.log('TELEPORT', 'isPreTeleporting = true (via coordinator)')\r\n\r\n // CRITICAL: Remove old scrollend listener BEFORE stopping scroll\r\n if (scrollEndListenerRef.current) {\r\n logger?.log('TELEPORT', 'Removing old scrollend listener before stop')\r\n el.removeEventListener('scrollend', scrollEndListenerRef.current)\r\n scrollEndListenerRef.current = null\r\n }\r\n\r\n // STOP any ongoing smooth scroll animation first\r\n logger?.log('TELEPORT', 'Stopping ongoing smooth scroll...')\r\n el.scrollTo({ left: el.scrollLeft, behavior: 'auto' })\r\n\r\n // Perform the instant teleport\r\n coordinator.transition({ type: 'SET_TELEPORTING', value: true })\r\n const oldScrollLeft = el.scrollLeft\r\n const newScrollLeft = oldScrollLeft + offset\r\n el.scrollLeft = newScrollLeft\r\n logger?.log('TELEPORT', `Instant teleport: ${oldScrollLeft.toFixed(1)} → ${el.scrollLeft.toFixed(1)}`)\r\n\r\n // Apply visuals via RAF to avoid layout thrashing\r\n logger?.log('TELEPORT', 'Applying visuals after teleport...')\r\n requestAnimationFrame(() => applyVisuals(el, newScrollLeft))\r\n\r\n coordinator.transition({ type: 'SET_TELEPORTING', value: false })\r\n\r\n // CRITICAL: Update pendingTarget in coordinator with the ADJUSTED target\r\n coordinator.transition({ type: 'SET_PENDING_TARGET', target: adjustedTarget })\r\n\r\n // Clear the pre-teleport flag after a short delay\r\n setTimeout(() => {\r\n coordinator.transition({ type: 'SET_PRE_TELEPORTING', value: false })\r\n logger?.log('TELEPORT', `isPreTeleporting = false (after ${preTeleportClearDelayMs}ms)`)\r\n }, preTeleportClearDelayMs)\r\n\r\n logger?.log('TELEPORT', `⚡ ${direction} PRE-TELEPORT END`, {\r\n duration: `${(performance.now() - preTeleportStart).toFixed(1)}ms`\r\n })\r\n\r\n return adjustedTarget\r\n }\r\n\r\n return {\r\n /** Ref to track if current interaction is touch-based */\r\n isTouchInteraction,\r\n /** Proactive pre-teleport for arrow navigation */\r\n preTeleport,\r\n }\r\n}\r\n","import { useCallback, useRef, useEffect } from 'react'\r\nimport { VISUAL_CONFIG } from '../config'\r\nimport type { CarouselLoggerInstance } from '../logger'\r\n\r\nexport interface UseCarouselVisualsOptions {\r\n /** Layout measurements */\r\n layout: { cardWidth: number; gap: number }\r\n /** Number of items (used to detect cache invalidation) */\r\n itemsCount: number\r\n /** Buffer items before original set */\r\n bufferBeforeCount: number\r\n /** Disable opacity effect */\r\n disableOpacityEffect: boolean\r\n /** Disable scale effect */\r\n disableScaleEffect: boolean\r\n /** Optional logger for debugging */\r\n logger?: CarouselLoggerInstance\r\n}\r\n\r\nexport interface ChildPosition {\r\n left: number\r\n width: number\r\n}\r\n\r\n/**\r\n * Hook that manages visual effects for carousel items.\r\n * Handles:\r\n * - Position cache for children (offsetLeft, offsetWidth)\r\n * - Container width cache (to avoid layout thrashing)\r\n * - Apply visual effects (scale, opacity, shadow, z-index)\r\n * - Viewport culling (skip items outside visible area)\r\n */\r\nexport function useCarouselVisuals({\r\n layout,\r\n itemsCount,\r\n bufferBeforeCount,\r\n disableOpacityEffect,\r\n disableScaleEffect,\r\n logger,\r\n}: UseCarouselVisualsOptions) {\r\n // Position cache for all children\r\n const childrenPositions = useRef<ChildPosition[]>([])\r\n const isCacheDirty = useRef(true)\r\n\r\n // Container width cache (avoids reflow when reading clientWidth after scrollLeft write)\r\n const containerWidthRef = useRef(0)\r\n const isContainerWidthDirty = useRef(true)\r\n\r\n // Mark cache dirty when items change\r\n useEffect(() => {\r\n isCacheDirty.current = true\r\n isContainerWidthDirty.current = true\r\n logger?.log('VISUALS', 'Marked cache dirty', { itemsCount, bufferBeforeCount })\r\n }, [itemsCount, bufferBeforeCount, logger])\r\n\r\n /**\r\n * Update the position cache for all children\r\n */\r\n const updateCache = useCallback((el: HTMLElement) => {\r\n childrenPositions.current = Array.from(el.children).map((child) => {\r\n const node = child as HTMLElement\r\n return {\r\n left: node.offsetLeft,\r\n width: node.offsetWidth,\r\n }\r\n })\r\n logger?.log('VISUALS', 'Updated positions cache', { count: childrenPositions.current.length })\r\n }, [logger])\r\n\r\n /**\r\n * Apply visual effects (scale, opacity, shadow) to visible items\r\n * Uses position cache to avoid layout thrashing\r\n */\r\n const applyVisuals = useCallback((el: HTMLElement, overrideScrollLeft?: number) => {\r\n // OPTIMIZATION: Use override value if provided to avoid DOM Read after Write\r\n const currentScrollLeft = overrideScrollLeft ?? el.scrollLeft\r\n\r\n // Cache container width to avoid reflow\r\n if (isContainerWidthDirty.current || containerWidthRef.current === 0) {\r\n containerWidthRef.current = el.clientWidth\r\n isContainerWidthDirty.current = false\r\n }\r\n\r\n const containerCenter = currentScrollLeft + containerWidthRef.current / 2\r\n if (childrenPositions.current.length === 0) {\r\n logger?.log('VISUALS', 'Skipping: positions cache empty')\r\n return\r\n }\r\n const positions = childrenPositions.current\r\n\r\n // Skip if both effects are disabled\r\n if (disableOpacityEffect && disableScaleEffect) return\r\n\r\n // Responsive breakpoints\r\n const width = window.innerWidth\r\n const isMobile = width < 640\r\n const isTablet = width < 1024\r\n\r\n const maxDist = isMobile\r\n ? VISUAL_CONFIG.MAX_DIST_MOBILE\r\n : isTablet\r\n ? VISUAL_CONFIG.MAX_DIST_TABLET\r\n : VISUAL_CONFIG.MAX_DIST_DESKTOP\r\n const baseScale = isMobile\r\n ? VISUAL_CONFIG.BASE_SCALE_MOBILE\r\n : isTablet\r\n ? VISUAL_CONFIG.BASE_SCALE_TABLET\r\n : VISUAL_CONFIG.BASE_SCALE_DESKTOP\r\n const scaleRange = 1 - baseScale\r\n\r\n // Viewport culling bounds\r\n const viewStart = currentScrollLeft - VISUAL_CONFIG.VIEW_BUFFER\r\n const viewEnd = currentScrollLeft + containerWidthRef.current + VISUAL_CONFIG.VIEW_BUFFER\r\n\r\n // OPTIMIZATION: Iterate HTMLCollection directly to avoid Array allocation (GC pressure)\r\n const count = el.children.length\r\n\r\n // Calculate stride for index-based culling\r\n const stride = layout.cardWidth + layout.gap\r\n const firstItemLeft = positions[0]?.left ?? 0\r\n\r\n // Calculate visible index range\r\n // We add a safety buffer of +/- 4 items to ensure we don't accidentally cull partially visible items due to sub-pixel rounding\r\n // or dynamic scaling transforms that might push items slightly out of their calculated slot\r\n let startIndex = 0\r\n let endIndex = count\r\n\r\n if (stride > 0) {\r\n // Formula: itemLeft = firstItemLeft + index * stride\r\n // Want: itemLeft + itemWidth > viewStart -> index > (viewStart - itemWidth - firstItemLeft) / stride\r\n startIndex = Math.floor((viewStart - layout.cardWidth - firstItemLeft) / stride) - 4\r\n startIndex = Math.max(0, startIndex)\r\n\r\n // Want: itemLeft < viewEnd -> index < (viewEnd - firstItemLeft) / stride\r\n endIndex = Math.ceil((viewEnd - firstItemLeft) / stride) + 4\r\n endIndex = Math.min(count, endIndex)\r\n }\r\n\r\n const loopStart = performance.now()\r\n let processedCount = 0\r\n\r\n for (let i = startIndex; i < endIndex; i++) {\r\n const child = el.children[i] as HTMLElement\r\n const pos = positions[i]\r\n if (!pos) continue\r\n\r\n // Double check bounds (cheap) just in case calc was off\r\n if (pos.left + pos.width < viewStart || pos.left > viewEnd) {\r\n continue\r\n }\r\n\r\n processedCount++\r\n\r\n const childCenter = pos.left + pos.width / 2\r\n const dist = Math.abs(containerCenter - childCenter)\r\n\r\n // Cubic easing for smooth falloff\r\n const normDist = Math.min(dist / maxDist, 1)\r\n const factor = 1 - normDist\r\n const easeFactor = 1 - Math.pow(1 - factor, 3)\r\n\r\n const opacity = 0.5 + (0.5 * easeFactor)\r\n\r\n if (!disableOpacityEffect) {\r\n child.style.opacity = `${opacity}`\r\n if (dist < VISUAL_CONFIG.CENTER_THRESHOLD) child.style.opacity = '1'\r\n }\r\n\r\n const zIndex = Math.round(easeFactor * 100)\r\n child.style.zIndex = `${zIndex}`\r\n\r\n // PERF: Dynamic box-shadow animations trigger a \"Paint\" on every frame, which is very expensive (>1ms/frame).\r\n // We disable this by default to maintain 60fps, especially with many items on screen.\r\n if (!disableScaleEffect && !VISUAL_CONFIG.DISABLE_DYNAMIC_SHADOW) {\r\n const shadowOpacity = 0.12 * easeFactor\r\n child.style.boxShadow = `0 10px 20px -5px rgba(0, 0, 0, ${shadowOpacity})`\r\n }\r\n\r\n if (!disableScaleEffect) {\r\n const scale = baseScale + (scaleRange * easeFactor)\r\n child.style.transform = `scale(${scale})`\r\n }\r\n }\r\n\r\n const loopDuration = performance.now() - loopStart\r\n // Only log \"heavy\" frames to avoid console spam (default threshold 1ms)\r\n if (loopDuration > 1.0) {\r\n logger?.log('PERF', `Visuals Loop: ${loopDuration.toFixed(2)}ms`, {\r\n processed: processedCount,\r\n total: count,\r\n range: `${startIndex}-${endIndex}`\r\n })\r\n }\r\n }, [disableOpacityEffect, disableScaleEffect, logger, layout])\r\n\r\n return {\r\n /** Position cache for all children */\r\n childrenPositions,\r\n /** Whether the cache needs to be rebuilt */\r\n isCacheDirty,\r\n /** Container width cache ref */\r\n containerWidthRef,\r\n /** Whether container width needs to be remeasured */\r\n isContainerWidthDirty,\r\n /** Update the position cache */\r\n updateCache,\r\n /** Apply visual effects to visible items */\r\n applyVisuals,\r\n }\r\n}\r\n","// Visual effect configuration\r\nexport const VISUAL_CONFIG = {\r\n MAX_DIST_MOBILE: 320,\r\n MAX_DIST_TABLET: 400,\r\n MAX_DIST_DESKTOP: 500,\r\n BASE_SCALE_MOBILE: 0.80,\r\n BASE_SCALE_TABLET: 0.82,\r\n BASE_SCALE_DESKTOP: 0.85,\r\n VIEW_BUFFER: 200,\r\n CENTER_THRESHOLD: 10,\r\n DISABLE_DYNAMIC_SHADOW: true,\r\n} as const\r\n\r\n// Animation timing configuration\r\nexport const TIMING_CONFIG = {\r\n BOUNCE_DISTANCE_PX: 30,\r\n BOUNCE_PHASE1_MS: 150,\r\n BOUNCE_PHASE2_MS: 450,\r\n // How long to wait before clearing the pre-teleport flag\r\n PRE_TELEPORT_CLEAR_DELAY_MS: 100,\r\n SCROLL_IDLE_FALLBACK_MS: 300,\r\n SCROLL_DEBOUNCE_FALLBACK_MS: 50,\r\n SNAP_RESTORE_DELAY_MS: 100,\r\n RESIZE_DEBOUNCE_MS: 100,\r\n SCROLL_PERSIST_DEBOUNCE_MS: 150,\r\n // Milliseconds to wait for scroll to stop before considering it complete (fallback)\r\n SCROLL_COMPLETION_DEBOUNCE_MS: 150,\r\n // Tolerance for target arrival check (ratio of stride)\r\n SCROLL_TARGET_TOLERANCE_RATIO: 0.5,\r\n} as const\r\n\r\n// Layout configuration\r\nexport const LAYOUT_CONFIG = {\r\n GAP_MOBILE: 12,\r\n GAP_DESKTOP: 16,\r\n GAP_BREAKPOINT: 768,\r\n MIN_BUFFER_COUNT: 50,\r\n EDGE_TOLERANCE_START: 20,\r\n EDGE_TOLERANCE_END: 5,\r\n // Initial fallback values (should match --carousel-item-width-default in tailwind.css)\r\n INITIAL_CARD_WIDTH: 180,\r\n INITIAL_GAP: 16,\r\n} as const\r\n\r\nexport const DEBUG_CONFIG = {\r\n // 🛡️ SAFETY: This defaults to false in production. \r\n // Even if set to true manually, the logger has a hard guard against console output in production.\r\n ENABLED: false,\r\n HISTORY_SIZE: 100,\r\n // Enable specific channels to debug features\r\n // Options: 'ALL', 'TELEPORT', 'VISUALS', 'LAYOUT', 'INIT', 'CACHE', 'COORDINATOR', 'NAV', 'INTERACT'\r\n CHANNELS: {\r\n ALL: false,\r\n TELEPORT: false,\r\n VISUALS: false,\r\n LAYOUT: false,\r\n INIT: false,\r\n CACHE: false,\r\n COORDINATOR: false,\r\n NAV: false,\r\n INTERACT: false,\r\n PERF: false,\r\n },\r\n}\r\n\r\n// Feature flags\r\nexport const FEATURE_FLAGS = {\r\n USE_RAF_FRAME_SEPARATION: true,\r\n} as const\r\n","import { useRef, useCallback, useEffect } from 'react'\r\n\r\nexport interface UseCarouselPersistenceOptions {\r\n /** Unique key for this carousel's scroll position in sessionStorage */\r\n persistKey?: string\r\n /** Debounce delay in ms for saving scroll position. Default: 150 */\r\n debounceMs?: number\r\n}\r\n\r\nexport interface UseCarouselPersistenceReturn {\r\n /** Get saved scroll position from sessionStorage (null if not found) */\r\n getSavedPosition: () => number | null\r\n /** Save current scroll position to sessionStorage (debounced) */\r\n savePosition: (scrollLeft: number) => void\r\n /** Immediately save position (no debounce, for unmount) */\r\n savePositionImmediate: (scrollLeft: number) => void\r\n /** Clear saved position from sessionStorage */\r\n clearPosition: () => void\r\n}\r\n\r\n/**\r\n * Hook for persisting carousel scroll position across navigation.\r\n * \r\n * Uses sessionStorage so position survives:\r\n * - Browser back/forward navigation\r\n * - Same-tab navigation\r\n * \r\n * But resets on:\r\n * - New tab/window\r\n * - Browser close\r\n * \r\n * @example\r\n * ```tsx\r\n * const { getSavedPosition, savePosition } = useCarouselPersistence({ \r\n * persistKey: 'homepage-featured' \r\n * })\r\n * \r\n * // On mount\r\n * useEffect(() => {\r\n * const saved = getSavedPosition()\r\n * if (saved !== null) containerRef.current.scrollLeft = saved\r\n * }, [])\r\n * \r\n * // On scroll\r\n * const handleScroll = () => savePosition(containerRef.current.scrollLeft)\r\n * ```\r\n */\r\nexport function useCarouselPersistence({\r\n persistKey,\r\n debounceMs = 150,\r\n}: UseCarouselPersistenceOptions = {}): UseCarouselPersistenceReturn {\r\n const debounceTimerRef = useRef<NodeJS.Timeout | null>(null)\r\n const lastSavedRef = useRef<number | null>(null)\r\n\r\n // Build storage key\r\n const storageKey = persistKey ? `carousel-scroll-${persistKey}` : null\r\n\r\n const getSavedPosition = useCallback((): number | null => {\r\n if (!storageKey) return null\r\n\r\n try {\r\n const stored = sessionStorage.getItem(storageKey)\r\n if (stored === null) return null\r\n\r\n const parsed = parseInt(stored, 10)\r\n return Number.isFinite(parsed) ? parsed : null\r\n } catch {\r\n // sessionStorage may throw in private mode or when disabled\r\n return null\r\n }\r\n }, [storageKey])\r\n\r\n const savePositionImmediate = useCallback((scrollLeft: number) => {\r\n if (!storageKey) return\r\n if (!Number.isFinite(scrollLeft)) return\r\n\r\n // Skip if value hasn't changed (optimization)\r\n if (lastSavedRef.current === scrollLeft) return\r\n lastSavedRef.current = scrollLeft\r\n\r\n try {\r\n sessionStorage.setItem(storageKey, String(Math.round(scrollLeft)))\r\n } catch {\r\n // sessionStorage may throw when full or in private mode\r\n }\r\n }, [storageKey])\r\n\r\n const savePosition = useCallback((scrollLeft: number) => {\r\n if (!storageKey) return\r\n\r\n // Clear previous debounce timer\r\n if (debounceTimerRef.current) {\r\n clearTimeout(debounceTimerRef.current)\r\n }\r\n\r\n // Debounce the save\r\n debounceTimerRef.current = setTimeout(() => {\r\n savePositionImmediate(scrollLeft)\r\n }, debounceMs)\r\n }, [storageKey, debounceMs, savePositionImmediate])\r\n\r\n const clearPosition = useCallback(() => {\r\n if (!storageKey) return\r\n\r\n try {\r\n sessionStorage.removeItem(storageKey)\r\n lastSavedRef.current = null\r\n } catch {\r\n // Ignore errors\r\n }\r\n }, [storageKey])\r\n\r\n // Cleanup debounce timer on unmount\r\n useEffect(() => {\r\n return () => {\r\n if (debounceTimerRef.current) {\r\n clearTimeout(debounceTimerRef.current)\r\n }\r\n }\r\n }, [])\r\n\r\n return {\r\n getSavedPosition,\r\n savePosition,\r\n savePositionImmediate,\r\n clearPosition,\r\n }\r\n}\r\n","import { useRef, useCallback, useEffect, useState } from 'react'\r\nimport { LAYOUT_CONFIG, TIMING_CONFIG } from '../config'\r\nimport type { CarouselLoggerInstance } from '../logger'\r\n\r\nexport interface UseCarouselLayoutOptions {\r\n /** Ref to the scrollable carousel container */\r\n containerRef: React.RefObject<HTMLDivElement | null>\r\n /** Callback when layout is measured (for updating visual caches) */\r\n onLayoutChange?: (layout: { cardWidth: number; gap: number }) => void\r\n /** Debounce delay for resize handling in ms. Default: 100 */\r\n resizeDebounceMs?: number\r\n /** Optional logger for debugging */\r\n logger?: CarouselLoggerInstance\r\n}\r\n\r\nexport interface UseCarouselLayoutReturn {\r\n /** Current layout measurements */\r\n layout: { cardWidth: number; gap: number; domStride: number }\r\n /** Computed stride (cardWidth + gap) - fallback if domStride unavailable */\r\n stride: number\r\n /** Whether viewport is mobile (< 640px) */\r\n isMobile: boolean\r\n /** Whether viewport is tablet (< 1024px but >= 640px) */\r\n isTablet: boolean\r\n /** Manually trigger a layout measurement */\r\n measureLayout: () => { cardWidth: number; gap: number }\r\n /** Mark layout as dirty (triggers remeasure on next access) */\r\n invalidateLayout: () => void\r\n /** \r\n * Counter that increments each time ResizeObserver fires.\r\n * Used to trigger re-checks even when layout values are unchanged.\r\n */\r\n resizeCount: number\r\n}\r\n\r\n/**\r\n * Calculate layout measurements from a carousel container element.\r\n * Reads the first child's width and determines gap based on breakpoint.\r\n * \r\n * @param container - The scrollable carousel container element\r\n * @returns The measured cardWidth and gap values, or null if children aren't rendered yet\r\n */\r\nexport function measureLayoutFromElement(container: HTMLElement): { cardWidth: number; gap: number; domStride: number } | null {\r\n const firstCard = container.firstElementChild as HTMLElement\r\n if (!firstCard) {\r\n // Children not rendered yet - return null to signal \"can't measure\"\r\n return null\r\n }\r\n\r\n // Use getBoundingClientRect for sub-pixel precision (critical for large buffer accumulative drift)\r\n const cardWidth = firstCard.getBoundingClientRect().width\r\n // Use known Tailwind gap values - responsive breakpoint\r\n const gap = window.innerWidth < LAYOUT_CONFIG.GAP_BREAKPOINT\r\n ? LAYOUT_CONFIG.GAP_MOBILE\r\n : LAYOUT_CONFIG.GAP_DESKTOP\r\n\r\n // Measure actual stride from DOM (distance between item centers)\r\n // This accounts for sub-pixel rendering that may differ from cardWidth + gap\r\n let domStride = cardWidth + gap // fallback\r\n const secondCard = container.children[1] as HTMLElement | undefined\r\n if (secondCard) {\r\n domStride = secondCard.offsetLeft - firstCard.offsetLeft\r\n }\r\n\r\n return { cardWidth, gap, domStride }\r\n}\r\n\r\n/**\r\n * Hook that manages carousel layout measurements.\r\n * Handles:\r\n * - Initial layout measurement from first card element\r\n * - Viewport detection (mobile, tablet, desktop)\r\n * - Resize handling with debouncing\r\n * - Stride calculation\r\n */\r\nexport function useCarouselLayout({\r\n containerRef,\r\n onLayoutChange,\r\n resizeDebounceMs = TIMING_CONFIG.RESIZE_DEBOUNCE_MS,\r\n logger,\r\n}: UseCarouselLayoutOptions): UseCarouselLayoutReturn {\r\n // Layout state - triggers re-render on change\r\n const [layout, setLayout] = useState<{ cardWidth: number; gap: number; domStride: number }>({\r\n cardWidth: LAYOUT_CONFIG.INITIAL_CARD_WIDTH,\r\n gap: LAYOUT_CONFIG.INITIAL_GAP,\r\n domStride: LAYOUT_CONFIG.INITIAL_CARD_WIDTH + LAYOUT_CONFIG.INITIAL_GAP\r\n })\r\n\r\n // Counter that increments on each ResizeObserver callback - forces re-render even when values unchanged\r\n const [resizeCount, setResizeCount] = useState(0)\r\n\r\n // Dirty flag for lazy remeasurement\r\n const isLayoutDirty = useRef(true)\r\n\r\n // Callback ref to avoid stale closure issues\r\n const onLayoutChangeRef = useRef(onLayoutChange)\r\n onLayoutChangeRef.current = onLayoutChange\r\n\r\n // Logger ref to avoid effect re-running when logger changes\r\n const loggerRef = useRef(logger)\r\n loggerRef.current = logger\r\n\r\n // Compute stride from layout\r\n const stride = layout.cardWidth + layout.gap\r\n\r\n // Viewport detection\r\n const isMobile = typeof window !== 'undefined' && window.innerWidth < 640\r\n const isTablet = typeof window !== 'undefined' && window.innerWidth >= 640 && window.innerWidth < 1024\r\n\r\n /**\r\n * Measure layout from container and update state if changed\r\n */\r\n const measureLayout = useCallback(() => {\r\n const el = containerRef.current\r\n if (!el) {\r\n logger?.log('LAYOUT', 'No container element')\r\n return layout\r\n }\r\n\r\n const measured = measureLayoutFromElement(el)\r\n\r\n // If children aren't rendered yet, return current layout (don't update state)\r\n if (measured === null) {\r\n logger?.log('LAYOUT', 'Children not rendered, keeping current layout')\r\n return layout\r\n }\r\n\r\n logger?.log('LAYOUT', 'Layout measured', measured)\r\n\r\n // Only update state if values changed (prevents unnecessary re-renders)\r\n setLayout(prev => {\r\n if (prev.cardWidth === measured.cardWidth && prev.gap === measured.gap && prev.domStride === measured.domStride) {\r\n logger?.log('LAYOUT', 'Layout unchanged, skipping update')\r\n return prev\r\n }\r\n logger?.log('LAYOUT', 'Layout changed, updating state', {\r\n old: prev,\r\n new: measured\r\n })\r\n // Notify subscribers of layout change\r\n if (onLayoutChangeRef.current) {\r\n onLayoutChangeRef.current(measured)\r\n }\r\n return measured\r\n })\r\n\r\n isLayoutDirty.current = false\r\n return measured\r\n }, [containerRef, layout, logger])\r\n\r\n /**\r\n * Mark layout as dirty - will trigger remeasure on next access\r\n */\r\n const invalidateLayout = useCallback(() => {\r\n isLayoutDirty.current = true\r\n logger?.log('LAYOUT', 'Layout marked as dirty')\r\n }, [logger])\r\n\r\n /**\r\n * Resize handler - uses ResizeObserver with debouncing\r\n * Only fires when the container element actually changes size\r\n */\r\n // Use ref to avoid effect re-running when measureLayout changes\r\n const measureLayoutRef = useRef(measureLayout)\r\n measureLayoutRef.current = measureLayout\r\n\r\n useEffect(() => {\r\n const el = containerRef.current\r\n if (!el) return\r\n\r\n let timeoutId: NodeJS.Timeout\r\n let isFirstObservation = true\r\n\r\n const ro = new ResizeObserver(() => {\r\n // First observation: browser just computed layout, measure immediately\r\n if (isFirstObservation) {\r\n isFirstObservation = false\r\n loggerRef.current?.log('LAYOUT', 'First observation - measuring immediately')\r\n measureLayoutRef.current()\r\n // Increment resizeCount to trigger re-render even if values unchanged\r\n setResizeCount(c => c + 1)\r\n return\r\n }\r\n\r\n // Subsequent observations: debounce for stability\r\n clearTimeout(timeoutId)\r\n timeoutId = setTimeout(() => {\r\n loggerRef.current?.log('LAYOUT', 'Container resized, remeasuring layout')\r\n measureLayoutRef.current()\r\n setResizeCount(c => c + 1)\r\n }, resizeDebounceMs)\r\n })\r\n\r\n ro.observe(el)\r\n loggerRef.current?.log('LAYOUT', 'ResizeObserver attached')\r\n\r\n return () => {\r\n ro.disconnect()\r\n clearTimeout(timeoutId)\r\n loggerRef.current?.log('LAYOUT', 'ResizeObserver disconnected')\r\n }\r\n }, [containerRef, resizeDebounceMs])\r\n\r\n return {\r\n layout,\r\n stride,\r\n isMobile,\r\n isTablet,\r\n measureLayout,\r\n invalidateLayout,\r\n resizeCount,\r\n }\r\n}\r\n","import { useRef, useCallback } from 'react'\r\nimport { LAYOUT_CONFIG, TIMING_CONFIG, FEATURE_FLAGS } from '../config'\r\nimport { useScrollCompletion } from './useScrollCompletion'\r\nimport type { UseCarouselCoordinatorReturn } from './useCarouselCoordinator'\r\nimport type { CarouselLoggerInstance } from '../logger'\r\n\r\nexport interface UseCarouselNavigationOptions {\r\n /** Ref to the scrollable carousel container */\r\n containerRef: React.RefObject<HTMLDivElement | null>\r\n /** Whether this is an infinite carousel */\r\n infinite: boolean\r\n /** Layout measurements */\r\n layout: { cardWidth: number; gap: number }\r\n /** Function to cancel momentum scroll (from useDraggableScroll) */\r\n cancelMomentum: () => void\r\n /** Pre-teleport function for infinite carousels (from useCarouselTeleport) */\r\n preTeleport?: (targetScroll: number) => number\r\n /** Callback when navigating to a new item */\r\n onNavigate?: (targetScroll: number) => void\r\n /** Coordinator for state management (REQUIRED in Phase 2+) */\r\n coordinator: UseCarouselCoordinatorReturn\r\n /** Optional logger for debugging */\r\n logger?: CarouselLoggerInstance\r\n}\r\n\r\nexport interface UseCarouselNavigationReturn {\r\n /** Navigate left (previous item) */\r\n scrollLeft: () => void\r\n /** Navigate right (next item) */\r\n scrollRight: () => void\r\n /** Direct access to navigation handler */\r\n handleScrollNav: (direction: -1 | 1) => void\r\n}\r\n\r\n/**\r\n * Hook that handles arrow navigation for carousels.\r\n * \r\n * Phase 2: Uses coordinator as single source of truth for state.\r\n * No more external ref passing - all state is managed via coordinator.\r\n * \r\n * Features:\r\n * - Rapid-click \"Catch-Up & Advance\" strategy for smooth multi-click navigation\r\n * - Bounce animation at edges for finite carousels\r\n * - Pre-teleport integration for infinite carousels\r\n * - Scroll completion detection (scrollend or debounce fallback)\r\n */\r\nexport function useCarouselNavigation({\r\n containerRef,\r\n infinite,\r\n layout,\r\n cancelMomentum,\r\n preTeleport,\r\n onNavigate,\r\n coordinator,\r\n logger,\r\n}: UseCarouselNavigationOptions): UseCarouselNavigationReturn {\r\n // Internal refs that can't be stored in coordinator (functions/objects)\r\n const scrollEndListenerRef = useRef<(() => void) | null>(null)\r\n const scrollIdleTimeoutRef = useRef<NodeJS.Timeout | null>(null)\r\n const snapTimeoutRef = useRef<NodeJS.Timeout | null>(null)\r\n\r\n // Debug counters per instance using refs\r\n const debugClickCountRef = useRef(0)\r\n\r\n // Clear pending target when scroll completes\r\n const onAnimationComplete = useCallback((source: string) => {\r\n const el = containerRef.current\r\n if (!el) return\r\n\r\n const ctx = coordinator.getContext()\r\n if (ctx.pendingTarget === null && source !== 'safety-timeout') {\r\n logger?.log('NAV', `#${debugClickCountRef.current} Arrow nav ${source} - Skipping cleanup (Interrupted)`)\r\n return\r\n }\r\n if (ctx.isPreTeleporting) return\r\n\r\n // POSITION CHECK: Verify we actually reached the target before clearing\r\n const currentPos = el.scrollLeft\r\n const targetPos = ctx.pendingTarget\r\n const stride = layout.cardWidth + layout.gap\r\n\r\n if (targetPos !== null) {\r\n const distanceToTarget = Math.abs(currentPos - targetPos)\r\n // Allow some tolerance for rounding/animation imprecision\r\n if (distanceToTarget > stride * TIMING_CONFIG.SCROLL_TARGET_TOLERANCE_RATIO) {\r\n logger?.log('NAV', `#${debugClickCountRef.current} Ignoring premature ${source} - not at target yet`, {\r\n currentPos: currentPos.toFixed(1),\r\n targetPos: targetPos.toFixed(1),\r\n distanceToTarget: distanceToTarget.toFixed(1)\r\n })\r\n return\r\n }\r\n }\r\n\r\n logger?.log('NAV', `#${debugClickCountRef.current} Arrow nav complete via ${source}`)\r\n scrollEndListenerRef.current = null\r\n // Coordinator: Notify scroll completion (clears pendingTarget internally)\r\n coordinator.transition({ type: 'SCROLL_COMPLETE' })\r\n if (infinite && el) el.style.scrollSnapType = ''\r\n }, [containerRef, coordinator, infinite, layout.cardWidth, layout.gap, logger])\r\n\r\n // Check for scrollend support\r\n const { waitForScrollCompletion } = useScrollCompletion({\r\n ref: containerRef,\r\n listenerRef: scrollEndListenerRef,\r\n timeoutRef: scrollIdleTimeoutRef,\r\n onComplete: onAnimationComplete\r\n })\r\n\r\n const handleScrollNav = useCallback((direction: -1 | 1) => {\r\n const clickStartTime = performance.now()\r\n debugClickCountRef.current++\r\n const thisClickId = debugClickCountRef.current\r\n\r\n logger?.log('NAV', `━━━ Arrow Click #${thisClickId} START ━━━`, {\r\n direction: direction === 1 ? 'RIGHT →' : '← LEFT',\r\n timestamp: clickStartTime.toFixed(1)\r\n })\r\n\r\n const el = containerRef.current\r\n if (!el) {\r\n logger?.log('NAV', `#${thisClickId} ABORT: No element ref`)\r\n return\r\n }\r\n\r\n // Read state from coordinator\r\n const ctx = coordinator.getContext()\r\n\r\n // Ignore clicks while bouncing\r\n if (coordinator.getPhase() === 'BOUNCING') {\r\n logger?.log('NAV', `#${thisClickId} ABORT: Currently bouncing`)\r\n return\r\n }\r\n\r\n // OPTIMIZATION: Read dimensions BEFORE cancelling momentum to avoid layout thrashing\r\n // (cancelMomentum writes to the DOM, causing a forced reflow if we read after it)\r\n const perfStart = performance.now()\r\n const stride = layout.cardWidth + layout.gap\r\n const currentScroll = el.scrollLeft\r\n const maxScroll = el.scrollWidth - el.clientWidth\r\n\r\n // This measurement confirms we are NOT triggering a reflow (should be < 0.5ms)\r\n const readTime = performance.now() - perfStart\r\n\r\n logger?.log('PERF', `Layout Read: ${readTime.toFixed(2)}ms`, {\r\n safe: readTime < 1.0,\r\n threshold: '1.0ms'\r\n })\r\n\r\n logger?.log('NAV', `#${thisClickId} Cancelling momentum...`)\r\n cancelMomentum()\r\n\r\n logger?.log('NAV', `#${thisClickId} Current state`, {\r\n currentScroll: currentScroll.toFixed(1),\r\n stride,\r\n maxScroll: maxScroll.toFixed(1),\r\n infinite\r\n })\r\n\r\n logger?.log('NAV', `#${thisClickId} Current state`, {\r\n currentScroll: currentScroll.toFixed(1),\r\n stride,\r\n maxScroll: maxScroll.toFixed(1),\r\n infinite\r\n })\r\n\r\n // For finite carousels, check if we're at the edge\r\n if (!infinite) {\r\n const isAtStart = currentScroll <= LAYOUT_CONFIG.EDGE_TOLERANCE_START\r\n const isAtEnd = currentScroll >= maxScroll - LAYOUT_CONFIG.EDGE_TOLERANCE_END\r\n\r\n // If at edge and trying to go further, do bounce animation\r\n if ((direction === -1 && isAtStart) || (direction === 1 && isAtEnd)) {\r\n logger?.log('NAV', `#${thisClickId} Edge reached, bouncing`, { isAtStart, isAtEnd })\r\n const bounceAmount = direction * TIMING_CONFIG.BOUNCE_DISTANCE_PX\r\n\r\n // Coordinator: Start bounce phase\r\n const bounceTimeoutId = setTimeout(() => {\r\n el.style.transition = ''\r\n el.style.transform = ''\r\n coordinator.transition({ type: 'END_BOUNCE' })\r\n logger?.log('NAV', `#${thisClickId} Bounce complete`)\r\n }, TIMING_CONFIG.BOUNCE_PHASE2_MS)\r\n coordinator.transition({ type: 'START_BOUNCE', timeoutId: bounceTimeoutId })\r\n\r\n el.style.transition = 'transform 0.15s ease-out'\r\n el.style.transform = `translateX(${-bounceAmount}px)`\r\n\r\n setTimeout(() => {\r\n el.style.transition = 'transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1)'\r\n el.style.transform = 'translateX(0)'\r\n }, TIMING_CONFIG.BOUNCE_PHASE1_MS)\r\n\r\n return // Don't scroll further\r\n }\r\n }\r\n\r\n // For infinite carousels, disable snap to prevent interference with teleport\r\n if (infinite) {\r\n el.style.scrollSnapType = 'none'\r\n if (snapTimeoutRef.current) {\r\n logger?.log('NAV', `#${thisClickId} Clearing previous snap timeout`)\r\n clearTimeout(snapTimeoutRef.current)\r\n }\r\n }\r\n\r\n // ═══════════════════════════════════════════════════════════════════════════\r\n // RAPID-CLICK FIX: \"Catch-Up & Advance\" Strategy\r\n // ═══════════════════════════════════════════════════════════════════════════\r\n\r\n let targetScroll: number\r\n\r\n // COMMON: Measure real DOM stride and padding first\r\n // We must use the same \"ruler\" for both idle and rapid clicks to avoid drift.\r\n let activeStride = stride\r\n let paddingOffset = 0\r\n\r\n if (infinite && el.children.length > 0) {\r\n const firstChild = el.children[0] as HTMLElement\r\n paddingOffset = firstChild.offsetLeft\r\n\r\n if (el.children.length > 1) {\r\n const secondChild = el.children[1] as HTMLElement\r\n const domStride = secondChild.offsetLeft - firstChild.offsetLeft\r\n\r\n // If DOM stride differs significantly, trust the DOM\r\n if (domStride > 0 && Math.abs(domStride - stride) > 1) {\r\n activeStride = domStride\r\n }\r\n }\r\n }\r\n\r\n const pendingTarget = ctx.pendingTarget\r\n if (pendingTarget !== null) {\r\n // Mid-animation: CATCH UP first, then advance\r\n const previousTarget = pendingTarget\r\n\r\n // CRITICAL: Remove old scrollend listener BEFORE the instant snap\r\n if (scrollEndListenerRef.current) {\r\n el.removeEventListener('scrollend', scrollEndListenerRef.current)\r\n el.removeEventListener('scroll', scrollEndListenerRef.current)\r\n scrollEndListenerRef.current = null\r\n logger?.log('NAV', `#${thisClickId} Removed old scrollend listener before catch-up`)\r\n }\r\n\r\n // Instantly snap to where we were headed (no animation restart lag)\r\n el.scrollTo({ left: previousTarget, behavior: 'auto' })\r\n\r\n // Now calculate the next target from that position\r\n targetScroll = previousTarget + (direction * activeStride)\r\n\r\n logger?.log('NAV', `#${thisClickId} Mid-animation: Caught up to ${previousTarget}, now targeting`, {\r\n caughtUpTo: previousTarget,\r\n direction,\r\n activeStride,\r\n targetScroll: targetScroll.toFixed(1)\r\n })\r\n } else {\r\n // Idle: use current scroll position as base\r\n\r\n // Calculate index directly from scroll position\r\n // We assume Scroll 0 = Index 0, so simply dividing by stride works.\r\n const currentIndex = Math.round(currentScroll / activeStride)\r\n const nextIndex = currentIndex + direction\r\n\r\n // TARGET CALCULATION: TRUST THE DOM\r\n // Instead of multiplying Stride * Index (which accumulates errors),\r\n // we find the exact DOM element we want to land on and scroll there.\r\n let domTargetFound = false\r\n\r\n if (infinite && el.children[nextIndex]) {\r\n const targetNode = el.children[nextIndex] as HTMLElement\r\n // We know paddingOffset is essentially (ContainerWidth - CardWidth)/2\r\n // So Target = ItemLeft - PaddingOffset centers the item.\r\n targetScroll = targetNode.offsetLeft - paddingOffset\r\n domTargetFound = true\r\n } else {\r\n // Fallback to math if item not rendered yet\r\n targetScroll = paddingOffset + (nextIndex * activeStride)\r\n }\r\n\r\n logger?.log('NAV', `#${thisClickId} Idle target calculation`, {\r\n currentIndex,\r\n nextIndex,\r\n direction,\r\n paddingOffset,\r\n activeStride,\r\n method: domTargetFound ? 'DOM_EXACT' : 'MATH_APPROX',\r\n targetScroll: targetScroll.toFixed(1)\r\n })\r\n }\r\n\r\n // Pre-emptive teleport: If target would cross a threshold, teleport FIRST\r\n if (infinite && preTeleport) {\r\n targetScroll = preTeleport(targetScroll)\r\n }\r\n\r\n // Coordinator: Notify about arrow click scroll start (sets pendingTarget internally)\r\n coordinator.transition({ type: 'ARROW_CLICK', direction, targetScroll })\r\n logger?.log('NAV', `#${thisClickId} pendingScrollTarget = ${targetScroll.toFixed(1)}`)\r\n\r\n // Notify about navigation\r\n if (onNavigate) {\r\n onNavigate(targetScroll)\r\n }\r\n\r\n logger?.log('NAV', `#${thisClickId} 🚀 Starting smooth scroll to ${targetScroll.toFixed(1)}`)\r\n\r\n // Scroll to target - with or without RAF frame separation based on flag\r\n if (FEATURE_FLAGS.USE_RAF_FRAME_SEPARATION) {\r\n requestAnimationFrame(() => {\r\n el.scrollTo({\r\n left: targetScroll,\r\n behavior: 'smooth',\r\n })\r\n })\r\n } else {\r\n el.scrollTo({\r\n left: targetScroll,\r\n behavior: 'smooth',\r\n })\r\n }\r\n\r\n // Start listening for completion\r\n waitForScrollCompletion()\r\n\r\n // Restore scroll snap after animation (only for infinite carousels)\r\n // MOVED TO onAnimationComplete to avoid premature snapping\r\n // if (infinite) {\r\n // snapTimeoutRef.current = setTimeout(() => {\r\n // if (el) {\r\n // el.style.scrollSnapType = ''\r\n // logger?.log('NAV', `#${thisClickId} Scroll snap restored`)\r\n // }\r\n // }, TIMING_CONFIG.SNAP_RESTORE_DELAY_MS)\r\n // }\r\n\r\n const clickDuration = performance.now() - clickStartTime\r\n logger?.log('NAV', `━━━ Arrow Click #${thisClickId} END ━━━`, {\r\n totalDuration: `${clickDuration.toFixed(1)}ms`,\r\n finalTarget: targetScroll.toFixed(1)\r\n })\r\n }, [containerRef, infinite, layout, cancelMomentum, preTeleport, onNavigate, coordinator, waitForScrollCompletion, logger])\r\n\r\n const scrollLeft = useCallback(() => handleScrollNav(-1), [handleScrollNav])\r\n const scrollRight = useCallback(() => handleScrollNav(1), [handleScrollNav])\r\n\r\n return {\r\n scrollLeft,\r\n scrollRight,\r\n handleScrollNav,\r\n }\r\n}\r\n","import { useRef, useCallback, useEffect } from 'react'\r\nimport { TIMING_CONFIG } from '../config'\r\n\r\nexport interface UseScrollCompletionOptions {\r\n ref: React.RefObject<HTMLElement | null>\r\n onComplete: (source: string) => void\r\n /** Optional shared ref to track the listener for cleanup */\r\n listenerRef?: React.MutableRefObject<(() => void) | null>\r\n /** Optional shared ref for the safety timeout */\r\n timeoutRef?: React.MutableRefObject<NodeJS.Timeout | null>\r\n}\r\n\r\n/**\r\n * Hook to handle scroll completion detection across browsers.\r\n * Uses native `scrollend` event where available, falls back to debounce pattern.\r\n */\r\nexport function useScrollCompletion({\r\n ref,\r\n onComplete,\r\n listenerRef,\r\n timeoutRef\r\n}: UseScrollCompletionOptions) {\r\n // Internal refs if external ones aren't provided\r\n const internalListenerRef = useRef<(() => void) | null>(null)\r\n const activeListenerRef = listenerRef || internalListenerRef\r\n\r\n const internalTimeoutRef = useRef<NodeJS.Timeout | null>(null)\r\n const activeTimeoutRef = timeoutRef || internalTimeoutRef\r\n\r\n // Check support once\r\n const supportsScrollEnd = typeof window !== 'undefined' && 'onscrollend' in window\r\n\r\n /**\r\n * Starts listening for scroll completion.\r\n * @param timeoutDuration Override default timeout duration (for safety net)\r\n */\r\n const waitForScrollCompletion = useCallback((timeoutDuration = TIMING_CONFIG.SCROLL_IDLE_FALLBACK_MS) => {\r\n const el = ref.current\r\n if (!el) return\r\n\r\n // Cleanup previous listeners\r\n if (activeListenerRef.current) {\r\n el.removeEventListener('scrollend', activeListenerRef.current)\r\n el.removeEventListener('scroll', activeListenerRef.current) // In case it was the debounce one\r\n activeListenerRef.current = null\r\n }\r\n if (activeTimeoutRef.current) {\r\n clearTimeout(activeTimeoutRef.current)\r\n activeTimeoutRef.current = null\r\n }\r\n\r\n if (supportsScrollEnd) {\r\n // NATIVE: Use scrollend\r\n const listener = () => {\r\n activeListenerRef.current = null\r\n onComplete('scrollend')\r\n }\r\n activeListenerRef.current = listener\r\n el.addEventListener('scrollend', listener, { once: true })\r\n } else {\r\n // FALLBACK: Debounce pattern\r\n let debounceTimer: NodeJS.Timeout\r\n\r\n const debounceListener = () => {\r\n clearTimeout(debounceTimer)\r\n debounceTimer = setTimeout(() => {\r\n if (activeListenerRef.current) {\r\n el.removeEventListener('scroll', activeListenerRef.current)\r\n activeListenerRef.current = null\r\n }\r\n if (activeTimeoutRef.current) {\r\n clearTimeout(activeTimeoutRef.current)\r\n activeTimeoutRef.current = null\r\n }\r\n onComplete('debounce')\r\n }, TIMING_CONFIG.SCROLL_DEBOUNCE_FALLBACK_MS)\r\n }\r\n\r\n activeListenerRef.current = debounceListener\r\n el.addEventListener('scroll', debounceListener, { passive: true })\r\n\r\n // Safety net: in case scroll never starts\r\n activeTimeoutRef.current = setTimeout(() => {\r\n if (activeListenerRef.current) {\r\n el.removeEventListener('scroll', activeListenerRef.current)\r\n activeListenerRef.current = null\r\n }\r\n onComplete('safety-timeout')\r\n }, timeoutDuration)\r\n }\r\n }, [ref, onComplete, supportsScrollEnd])\r\n\r\n // Cleanup on unmount\r\n useEffect(() => {\r\n return () => {\r\n const el = ref.current\r\n if (el && activeListenerRef.current) {\r\n el.removeEventListener('scrollend', activeListenerRef.current)\r\n el.removeEventListener('scroll', activeListenerRef.current)\r\n }\r\n if (activeTimeoutRef.current) {\r\n clearTimeout(activeTimeoutRef.current)\r\n }\r\n }\r\n }, [ref])\r\n\r\n return {\r\n waitForScrollCompletion\r\n }\r\n}\r\n","import { useRef, useCallback } from 'react'\r\nimport type { CarouselLoggerInstance } from '../logger'\r\n\r\n// ═══════════════════════════════════════════════════════════════════════════\r\n// PHASE ENUM - Single source of truth for carousel state\r\n// ═══════════════════════════════════════════════════════════════════════════\r\n\r\nexport type CarouselPhase =\r\n | 'UNINITIALIZED' // Before first render/measurement\r\n | 'IDLE' // Ready for user interaction\r\n | 'SCROLLING' // Smooth scroll animation in progress\r\n | 'BOUNCING' // Edge bounce animation (finite carousels only)\r\n | 'PRE_TELEPORTING' // About to teleport (calculating offset)\r\n | 'TELEPORTING' // Mid-teleport (scroll position being adjusted)\r\n | 'DRAGGING' // User is actively dragging\r\n\r\n// ═══════════════════════════════════════════════════════════════════════════\r\n// CONTEXT - Consolidated state object (replaces 10+ scattered refs)\r\n// ═══════════════════════════════════════════════════════════════════════════\r\n\r\nexport interface CarouselContext {\r\n // Current phase (single source of truth)\r\n phase: CarouselPhase\r\n\r\n // Navigation state\r\n pendingTarget: number | null // Target scroll position for smooth scroll\r\n scrollDirection: -1 | 1 | null // Direction of current/last scroll\r\n\r\n // Teleport state (matches legacy refs for Phase 2-3 migration)\r\n teleportOffset: number | null // Amount to adjust after teleport\r\n isTeleporting: boolean // Whether mid-teleport adjustment is happening\r\n isPreTeleporting: boolean // Whether calculating teleport offset\r\n\r\n // Active item tracking\r\n lastActiveItemKey: string | null // Key of last emitted active item (for dedup)\r\n\r\n // Timer IDs (for cleanup - we store IDs, caller manages actual timers)\r\n snapTimeoutId: ReturnType<typeof setTimeout> | null\r\n scrollIdleTimeoutId: ReturnType<typeof setTimeout> | null\r\n bounceTimeoutId: ReturnType<typeof setTimeout> | null\r\n\r\n // Listener tracking (for cleanup)\r\n hasScrollEndListener: boolean\r\n}\r\n\r\n// Initial context state\r\nconst createInitialContext = (): CarouselContext => ({\r\n phase: 'UNINITIALIZED',\r\n pendingTarget: null,\r\n scrollDirection: null,\r\n teleportOffset: null,\r\n isTeleporting: false,\r\n isPreTeleporting: false,\r\n lastActiveItemKey: null,\r\n snapTimeoutId: null,\r\n scrollIdleTimeoutId: null,\r\n bounceTimeoutId: null,\r\n hasScrollEndListener: false,\r\n})\r\n\r\n// ═══════════════════════════════════════════════════════════════════════════\r\n// ACTIONS - All possible state transitions\r\n// ═══════════════════════════════════════════════════════════════════════════\r\n\r\nexport type CarouselAction =\r\n | { type: 'INITIALIZE' }\r\n | { type: 'ARROW_CLICK'; direction: -1 | 1; targetScroll: number }\r\n | { type: 'ITEM_CLICK'; targetScroll: number }\r\n | { type: 'SCROLL_COMPLETE' }\r\n | { type: 'USER_INTERRUPT' } // PointerDown, Wheel, TouchStart\r\n | { type: 'START_BOUNCE'; timeoutId: ReturnType<typeof setTimeout> }\r\n | { type: 'END_BOUNCE' }\r\n | { type: 'START_PRE_TELEPORT' }\r\n | { type: 'EXECUTE_TELEPORT'; offset: number }\r\n | { type: 'END_TELEPORT' }\r\n | { type: 'START_DRAG' }\r\n | { type: 'END_DRAG' }\r\n | { type: 'SET_SNAP_TIMEOUT'; timeoutId: ReturnType<typeof setTimeout> }\r\n | { type: 'CLEAR_SNAP_TIMEOUT' }\r\n | { type: 'SET_SCROLL_IDLE_TIMEOUT'; timeoutId: ReturnType<typeof setTimeout> }\r\n | { type: 'CLEAR_SCROLL_IDLE_TIMEOUT' }\r\n | { type: 'SET_SCROLL_END_LISTENER'; hasListener: boolean }\r\n | { type: 'SET_ACTIVE_ITEM_KEY'; key: string | null }\r\n | { type: 'SET_TELEPORTING'; value: boolean }\r\n | { type: 'SET_PRE_TELEPORTING'; value: boolean }\r\n | { type: 'SET_PENDING_TARGET'; target: number } // Unconditional update for pre-teleport\r\n\r\n// ═══════════════════════════════════════════════════════════════════════════\r\n// REDUCER - Pure function for state transitions (no side effects, no logging)\r\n// ═══════════════════════════════════════════════════════════════════════════\r\n\r\nexport function reduce(context: CarouselContext, action: CarouselAction): CarouselContext {\r\n switch (action.type) {\r\n case 'INITIALIZE': {\r\n return {\r\n ...context,\r\n phase: 'IDLE',\r\n }\r\n }\r\n\r\n case 'ARROW_CLICK': {\r\n // Can only start scrolling from IDLE or if already SCROLLING (rapid clicks)\r\n if (context.phase !== 'IDLE' && context.phase !== 'SCROLLING') {\r\n return context\r\n }\r\n return {\r\n ...context,\r\n phase: 'SCROLLING',\r\n pendingTarget: action.targetScroll,\r\n scrollDirection: action.direction,\r\n }\r\n }\r\n\r\n case 'ITEM_CLICK': {\r\n if (context.phase !== 'IDLE' && context.phase !== 'SCROLLING') {\r\n return context\r\n }\r\n return {\r\n ...context,\r\n phase: 'SCROLLING',\r\n pendingTarget: action.targetScroll,\r\n scrollDirection: null, // Direction not applicable for direct clicks\r\n }\r\n }\r\n\r\n case 'SCROLL_COMPLETE': {\r\n if (context.phase !== 'SCROLLING') {\r\n return context\r\n }\r\n return {\r\n ...context,\r\n phase: 'IDLE',\r\n pendingTarget: null,\r\n scrollDirection: null,\r\n hasScrollEndListener: false,\r\n }\r\n }\r\n\r\n case 'USER_INTERRUPT': {\r\n // User took control - cancel any programmatic scroll\r\n if (context.phase === 'SCROLLING') {\r\n return {\r\n ...context,\r\n phase: 'IDLE',\r\n pendingTarget: null,\r\n scrollDirection: null,\r\n }\r\n }\r\n return context\r\n }\r\n\r\n case 'START_BOUNCE': {\r\n if (context.phase !== 'IDLE') {\r\n return context\r\n }\r\n return {\r\n ...context,\r\n phase: 'BOUNCING',\r\n bounceTimeoutId: action.timeoutId,\r\n }\r\n }\r\n\r\n case 'END_BOUNCE': {\r\n if (context.phase !== 'BOUNCING') {\r\n return context\r\n }\r\n return {\r\n ...context,\r\n phase: 'IDLE',\r\n bounceTimeoutId: null,\r\n }\r\n }\r\n\r\n case 'START_PRE_TELEPORT': {\r\n // Can start pre-teleport from SCROLLING (arrow click near boundary)\r\n return {\r\n ...context,\r\n phase: 'PRE_TELEPORTING',\r\n }\r\n }\r\n\r\n case 'EXECUTE_TELEPORT': {\r\n if (context.phase !== 'PRE_TELEPORTING') {\r\n return context\r\n }\r\n return {\r\n ...context,\r\n phase: 'TELEPORTING',\r\n teleportOffset: action.offset,\r\n }\r\n }\r\n\r\n case 'END_TELEPORT': {\r\n if (context.phase !== 'TELEPORTING') {\r\n return context\r\n }\r\n // Return to SCROLLING if we have a pending target, otherwise IDLE\r\n const nextPhase = context.pendingTarget !== null ? 'SCROLLING' : 'IDLE'\r\n return {\r\n ...context,\r\n phase: nextPhase,\r\n teleportOffset: null,\r\n }\r\n }\r\n\r\n case 'START_DRAG': {\r\n return {\r\n ...context,\r\n phase: 'DRAGGING',\r\n pendingTarget: null, // Cancel any pending scroll\r\n scrollDirection: null,\r\n }\r\n }\r\n\r\n case 'END_DRAG': {\r\n if (context.phase !== 'DRAGGING') {\r\n return context\r\n }\r\n return {\r\n ...context,\r\n phase: 'IDLE',\r\n }\r\n }\r\n\r\n // Timer management actions (don't change phase)\r\n case 'SET_SNAP_TIMEOUT': {\r\n return { ...context, snapTimeoutId: action.timeoutId }\r\n }\r\n\r\n case 'CLEAR_SNAP_TIMEOUT': {\r\n return { ...context, snapTimeoutId: null }\r\n }\r\n\r\n case 'SET_SCROLL_IDLE_TIMEOUT': {\r\n return { ...context, scrollIdleTimeoutId: action.timeoutId }\r\n }\r\n\r\n case 'CLEAR_SCROLL_IDLE_TIMEOUT': {\r\n return { ...context, scrollIdleTimeoutId: null }\r\n }\r\n\r\n case 'SET_SCROLL_END_LISTENER': {\r\n return { ...context, hasScrollEndListener: action.hasListener }\r\n }\r\n\r\n case 'SET_ACTIVE_ITEM_KEY': {\r\n return { ...context, lastActiveItemKey: action.key }\r\n }\r\n\r\n case 'SET_TELEPORTING': {\r\n return { ...context, isTeleporting: action.value }\r\n }\r\n\r\n case 'SET_PRE_TELEPORTING': {\r\n if (action.value) {\r\n // Starting pre-teleport - just set the flag\r\n return { ...context, isPreTeleporting: true }\r\n } else {\r\n // Ending pre-teleport - transition back to SCROLLING if we have a pending target\r\n // This is critical: phase was PRE_TELEPORTING, now we need to resume scrolling\r\n const nextPhase = context.pendingTarget !== null ? 'SCROLLING' : 'IDLE'\r\n return {\r\n ...context,\r\n isPreTeleporting: false,\r\n phase: nextPhase\r\n }\r\n }\r\n }\r\n\r\n case 'SET_PENDING_TARGET': {\r\n // Unconditional update - used by preTeleport to set adjusted target\r\n // This bypasses phase checks since pre-teleport needs to update target\r\n // regardless of current state\r\n return { ...context, pendingTarget: action.target }\r\n }\r\n\r\n default: {\r\n // TypeScript exhaustiveness check\r\n const _exhaustive: never = action\r\n return context\r\n }\r\n }\r\n}\r\n\r\n// ═══════════════════════════════════════════════════════════════════════════\r\n// HOOK - Main coordinator hook\r\n// ═══════════════════════════════════════════════════════════════════════════\r\n\r\nexport interface UseCarouselCoordinatorOptions {\r\n /** Optional logger for debugging */\r\n logger?: CarouselLoggerInstance\r\n}\r\n\r\nexport interface UseCarouselCoordinatorReturn {\r\n /** Dispatch an action to transition state */\r\n transition: (action: CarouselAction) => CarouselContext\r\n /** Get current phase */\r\n getPhase: () => CarouselPhase\r\n /** Get full context (read-only snapshot) */\r\n getContext: () => Readonly<CarouselContext>\r\n /** Direct ref access (for passing to child hooks) */\r\n contextRef: React.MutableRefObject<CarouselContext>\r\n /** Check if currently in a \"busy\" phase (not idle) */\r\n isBusy: () => boolean\r\n /** Check if user interaction should be blocked */\r\n isBlocking: () => boolean\r\n}\r\n\r\n/**\r\n * Carousel Coordinator Hook\r\n * \r\n * Consolidates all carousel state into a single ref-based state machine.\r\n * No re-renders triggered - all state is in refs.\r\n * \r\n * @example\r\n * ```tsx\r\n * const { transition, getPhase, getContext } = useCarouselCoordinator({ logger })\r\n * \r\n * // Check state\r\n * if (getPhase() === 'IDLE') {\r\n * transition({ type: 'ARROW_CLICK', direction: 1, targetScroll: 500 })\r\n * }\r\n * \r\n * // Later, on scroll complete\r\n * transition({ type: 'SCROLL_COMPLETE' })\r\n * ```\r\n */\r\nexport function useCarouselCoordinator(options?: UseCarouselCoordinatorOptions): UseCarouselCoordinatorReturn {\r\n const contextRef = useRef<CarouselContext>(createInitialContext())\r\n const loggerRef = useRef(options?.logger)\r\n loggerRef.current = options?.logger\r\n\r\n const transition = useCallback((action: CarouselAction): CarouselContext => {\r\n const prevContext = contextRef.current\r\n const prevPhase = prevContext.phase\r\n const nextContext = reduce(prevContext, action)\r\n contextRef.current = nextContext\r\n\r\n // Log transition at hook level (not in pure reducer)\r\n if (loggerRef.current) {\r\n const arrow = prevPhase === nextContext.phase ? '•' : '→'\r\n loggerRef.current.log('COORDINATOR', `${prevPhase} ${arrow} ${action.type} ${arrow} ${nextContext.phase}`, {\r\n action: action.type,\r\n prevPhase,\r\n nextPhase: nextContext.phase,\r\n ...('targetScroll' in action ? { target: action.targetScroll } : {}),\r\n ...('direction' in action ? { direction: action.direction } : {}),\r\n })\r\n }\r\n\r\n return nextContext\r\n }, [])\r\n\r\n const getPhase = useCallback((): CarouselPhase => {\r\n return contextRef.current.phase\r\n }, [])\r\n\r\n const getContext = useCallback((): Readonly<CarouselContext> => {\r\n return contextRef.current\r\n }, [])\r\n\r\n const isBusy = useCallback((): boolean => {\r\n const phase = contextRef.current.phase\r\n return phase !== 'IDLE' && phase !== 'UNINITIALIZED'\r\n }, [])\r\n\r\n const isBlocking = useCallback((): boolean => {\r\n const phase = contextRef.current.phase\r\n return phase === 'BOUNCING' || phase === 'TELEPORTING'\r\n }, [])\r\n\r\n return {\r\n transition,\r\n getPhase,\r\n getContext,\r\n contextRef,\r\n isBusy,\r\n isBlocking,\r\n }\r\n}\r\n","import clsx from 'clsx'\r\nimport { useTranslation } from 'react-i18next'\r\n\r\ntype Direction = 'left' | 'right'\r\n\r\ninterface CarouselArrowProps {\r\n direction: Direction\r\n onClick: () => void\r\n disabled?: boolean\r\n className?: string\r\n}\r\n\r\nexport function CarouselArrow({\r\n direction,\r\n onClick,\r\n disabled = false,\r\n className,\r\n}: CarouselArrowProps) {\r\n const { t } = useTranslation()\r\n\r\n return (\r\n <button\r\n onClick={onClick}\r\n disabled={disabled}\r\n className={clsx(\r\n 'carousel-button', // Base class from global CSS\r\n direction === 'left' ? 'prev' : 'next', // Positioning classes\r\n 'disabled:opacity-0 disabled:cursor-not-allowed disabled:pointer-events-none', // State modifiers\r\n className,\r\n )}\r\n aria-label={direction === 'left' ? t('carousel.previous') : t('carousel.next')}\r\n onPointerDown={(e) => {\r\n // Prevent this event from bubbling to the container or triggering \"ghost\" clicks\r\n e.stopPropagation()\r\n // Prevent focus ring or text selection\r\n e.preventDefault()\r\n }}\r\n >\r\n {direction === 'left' ? (\r\n <svg\r\n width=\"24\"\r\n height=\"24\"\r\n viewBox=\"0 0 24 24\"\r\n fill=\"none\"\r\n stroke=\"currentColor\"\r\n strokeWidth=\"2\"\r\n >\r\n <path d=\"M15 18l-6-6 6-6\" />\r\n </svg>\r\n ) : (\r\n <svg\r\n width=\"24\"\r\n height=\"24\"\r\n viewBox=\"0 0 24 24\"\r\n fill=\"none\"\r\n stroke=\"currentColor\"\r\n strokeWidth=\"2\"\r\n >\r\n <path d=\"M9 18l6-6-6-6\" />\r\n </svg>\r\n )}\r\n </button>\r\n )\r\n}\r\n","import { DEBUG_CONFIG } from './config'\r\n\r\nexport type DebugChannel = keyof typeof DEBUG_CONFIG.CHANNELS\r\n\r\nexport type ChannelConfig = Partial<Record<DebugChannel, boolean>> | 'ALL'\r\n\r\ninterface LogEntry {\r\n id: number\r\n timestamp: number\r\n carouselId: string\r\n channel: DebugChannel\r\n message: string\r\n data?: unknown\r\n}\r\n\r\nexport interface LoggerConfig {\r\n /** Override channel activation for this instance (or 'ALL' for everything) */\r\n channels?: ChannelConfig\r\n /** Max entries in local buffer (default: 100) */\r\n bufferSize?: number\r\n}\r\n\r\n// Color map for different channels to make traces readable\r\nconst CHANNEL_COLORS: Record<DebugChannel, string> = {\r\n ALL: '#ffffff', // White (not typically logged directly)\r\n COORDINATOR: '#ff9800', // Orange\r\n LAYOUT: '#2196f3', // Blue\r\n TELEPORT: '#e91e63', // Pink\r\n VISUALS: '#9c27b0', // Purple\r\n NAV: '#4caf50', // Green\r\n INIT: '#f44336', // Red\r\n CACHE: '#795548', // Brown\r\n INTERACT: '#607d8b', // Blue Grey\r\n PERF: '#ff5722', // Deep Orange\r\n}\r\n\r\n// ═══════════════════════════════════════════════════════════════════════════\r\n// GLOBAL REGISTRY - Shared across all logger instances\r\n// ═══════════════════════════════════════════════════════════════════════════\r\n\r\nclass LoggerRegistry {\r\n private static instance: LoggerRegistry\r\n private globalHistory: LogEntry[] = []\r\n private counter: number = 0\r\n private maxGlobalHistory: number = 500\r\n\r\n private constructor() {\r\n if (typeof window !== 'undefined') {\r\n // Expose dump function to window for easy debugging\r\n // Usage: window.__DUMP_CAROUSEL_LOGS() or window.__DUMP_CAROUSEL_LOGS('hero')\r\n ; (window as any).__DUMP_CAROUSEL_LOGS = this.dump.bind(this)\r\n }\r\n }\r\n\r\n static getInstance(): LoggerRegistry {\r\n if (!LoggerRegistry.instance) {\r\n LoggerRegistry.instance = new LoggerRegistry()\r\n }\r\n return LoggerRegistry.instance\r\n }\r\n\r\n /** Get next unique ID for log entries */\r\n getNextId(): number {\r\n return ++this.counter\r\n }\r\n\r\n /** Add entry to global history */\r\n addToGlobal(entry: LogEntry): void {\r\n this.globalHistory.push(entry)\r\n if (this.globalHistory.length > this.maxGlobalHistory) {\r\n this.globalHistory.shift()\r\n }\r\n }\r\n\r\n /**\r\n * Dump history to console as a table.\r\n * @param filterId - Optional: filter to only show logs from a specific carousel ID\r\n */\r\n dump(filterId?: string): string {\r\n let entries = this.globalHistory\r\n if (filterId) {\r\n entries = entries.filter(e => e.carouselId === filterId)\r\n }\r\n\r\n if (entries.length === 0) {\r\n console.warn(`[CarouselLogger] No logs found${filterId ? ` for \"${filterId}\"` : ''}`)\r\n return 'No logs found'\r\n }\r\n\r\n console.group(`📸 Carousel Debug Snapshot${filterId ? ` [${filterId}]` : ' (all)'}`)\r\n console.table(\r\n entries.map(entry => ({\r\n Time: new Date(entry.timestamp).toISOString().split('T')[1],\r\n ID: entry.carouselId,\r\n Channel: entry.channel,\r\n Message: entry.message,\r\n Data: entry.data ? JSON.stringify(entry.data) : ''\r\n }))\r\n )\r\n console.groupEnd()\r\n return `Dumped ${entries.length} logs.`\r\n }\r\n\r\n /** Clear all global history */\r\n clear(): void {\r\n this.globalHistory = []\r\n }\r\n}\r\n\r\n// ═══════════════════════════════════════════════════════════════════════════\r\n// LOGGER INSTANCE - One per carousel\r\n// ═══════════════════════════════════════════════════════════════════════════\r\n\r\nexport class CarouselLoggerInstance {\r\n readonly id: string\r\n private localHistory: LogEntry[] = []\r\n private maxLocalHistory: number\r\n private channelOverrides: ChannelConfig | undefined\r\n private registry: LoggerRegistry\r\n\r\n constructor(id: string, config?: LoggerConfig) {\r\n this.id = id\r\n this.maxLocalHistory = config?.bufferSize ?? 100\r\n this.channelOverrides = config?.channels\r\n this.registry = LoggerRegistry.getInstance()\r\n }\r\n\r\n /**\r\n * Check if a channel should log to console.\r\n * Resolution order: instance override → global config\r\n */\r\n private shouldLog(channel: DebugChannel): boolean {\r\n // Force enable if overrides are present for this instance\r\n const forceEnable = this.channelOverrides !== undefined\r\n\r\n if (!DEBUG_CONFIG.ENABLED && !forceEnable) return false\r\n\r\n // Instance override takes priority\r\n if (this.channelOverrides !== undefined) {\r\n if (this.channelOverrides === 'ALL') return true\r\n if (this.channelOverrides[channel] !== undefined) {\r\n return this.channelOverrides[channel]!\r\n }\r\n }\r\n\r\n // Fall back to global config\r\n if (DEBUG_CONFIG.CHANNELS.ALL) return true\r\n return DEBUG_CONFIG.CHANNELS[channel] ?? false\r\n }\r\n\r\n /**\r\n * Log a message to history buffers and optionally to console.\r\n */\r\n log(channel: DebugChannel, message: string, data?: unknown): void {\r\n const forceEnable = this.channelOverrides !== undefined\r\n if (!DEBUG_CONFIG.ENABLED && !forceEnable) return\r\n\r\n // 1. Create entry\r\n const entry: LogEntry = {\r\n id: this.registry.getNextId(),\r\n timestamp: Date.now(),\r\n carouselId: this.id,\r\n channel,\r\n message,\r\n data\r\n }\r\n\r\n // 2. Add to both local and global history (Dual Write)\r\n this.localHistory.push(entry)\r\n if (this.localHistory.length > this.maxLocalHistory) {\r\n this.localHistory.shift()\r\n }\r\n this.registry.addToGlobal(entry)\r\n\r\n // 3. Trace to console if channel is enabled\r\n // 🛡️ PRODUCTION SAFETY: Strictly block console output in production UNLESS explicitly overridden\r\n if (this.shouldLog(channel) && (process.env.NODE_ENV !== 'production' || forceEnable)) {\r\n const color = CHANNEL_COLORS[channel] || '#888'\r\n console.log(\r\n `%c[${this.id}]%c[${channel}]%c ${message}`,\r\n 'color: #888; font-weight: bold',\r\n `color: ${color}; font-weight: bold`,\r\n 'color: inherit',\r\n data ?? ''\r\n )\r\n }\r\n }\r\n\r\n /**\r\n * Dump this instance's local history to console.\r\n */\r\n dump(): string {\r\n if (this.localHistory.length === 0) {\r\n console.warn(`[CarouselLogger:${this.id}] Local buffer is empty`)\r\n return 'No logs found'\r\n }\r\n\r\n console.group(`📸 Carousel Debug Snapshot [${this.id}]`)\r\n console.table(\r\n this.localHistory.map(entry => ({\r\n Time: new Date(entry.timestamp).toISOString().split('T')[1],\r\n Channel: entry.channel,\r\n Message: entry.message,\r\n Data: entry.data ? JSON.stringify(entry.data) : ''\r\n }))\r\n )\r\n console.groupEnd()\r\n return `Dumped ${this.localHistory.length} logs.`\r\n }\r\n\r\n /** Clear local history */\r\n clear(): void {\r\n this.localHistory = []\r\n }\r\n\r\n /**\r\n * Create a performance timer for tracking elapsed time.\r\n * Usage: \r\n * const timer = logger.createTimer()\r\n * // ... do work ...\r\n * logger.log('INIT', 'Completed', { elapsedMs: timer.elapsed() })\r\n */\r\n createTimer(): { elapsed: () => number; reset: () => void } {\r\n let startTime = performance.now()\r\n return {\r\n elapsed: () => Math.round(performance.now() - startTime),\r\n reset: () => { startTime = performance.now() }\r\n }\r\n }\r\n}\r\n\r\n// ═══════════════════════════════════════════════════════════════════════════\r\n// FACTORY FUNCTION\r\n// ═══════════════════════════════════════════════════════════════════════════\r\n\r\n/**\r\n * Create a new logger instance for a carousel.\r\n * \r\n * @param id - Unique identifier for this carousel (e.g., 'hero', 'related-products')\r\n * @param config - Optional configuration for channel overrides and buffer size\r\n * \r\n * @example\r\n * ```tsx\r\n * // In BaseCarousel\r\n * const logger = createLogger(debugId, { channels: { NAV: true } })\r\n * \r\n * // Pass to hooks\r\n * const navigation = useCarouselNavigation({ ..., logger })\r\n * ```\r\n */\r\nexport function createLogger(id: string, config?: LoggerConfig): CarouselLoggerInstance {\r\n return new CarouselLoggerInstance(id, config)\r\n}\r\n\r\n// ═══════════════════════════════════════════════════════════════════════════\r\n// BACKWARDS COMPATIBILITY - Default singleton export\r\n// ═══════════════════════════════════════════════════════════════════════════\r\n\r\n// Legacy singleton for existing code that doesn't pass debugId\r\nexport const carouselLogger = createLogger('default')\r\n"],"mappings":"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","names":["import_react","import_react","import_react","import_react","import_react","import_react","import_react","import_react","import_react","clsx","import_jsx_runtime","startIdx","scrollLeft"]}
|