{"version":3,"file":"index.mjs","sources":["../src/utils/calculations.ts","../src/hooks/useRoulette.ts","../src/utils/classNames.ts","../src/components/SpinRoulette.tsx"],"sourcesContent":["/**\n * Calculate optimal number of spins based on prize count and duration\n * More prizes = fewer spins to avoid very long animations\n * Shorter duration = fewer spins for better performance\n *\n * @param prizesLength - Total number of prizes\n * @param duration - Animation duration in milliseconds\n * @returns Optimal number of complete spins\n */\nexport const calculateOptimalSpins = (prizesLength: number, duration: number): number => {\n  // Base calculation: longer duration and fewer prizes = more spins\n  const durationFactor = duration / 5000; // 5000ms is baseline\n  const prizeFactor = Math.max(1, 10 / prizesLength); // Fewer prizes = more spins\n\n  const optimalSpins = Math.ceil(2 * durationFactor * prizeFactor);\n\n  // Clamp between 2 and 8 spins\n  return Math.min(Math.max(optimalSpins, 2), 8);\n};\n\n/**\n * Calculate the offset needed to position the winning prize in the center\n * Includes multiple full rotations for a realistic lottery/roulette effect\n *\n * @param prizeSize - Size of each prize item (width or height depending on orientation)\n * @param winningIndex - Index of the winning prize\n * @param containerSize - Size of the container viewport\n * @param prizesLength - Total number of prizes in the array\n * @param minSpins - Minimum number of complete spins before landing (default: 5)\n * @returns Offset in pixels\n */\nexport const calculateOffset = (\n  prizeSize: number,\n  winningIndex: number,\n  containerSize: number,\n  prizesLength: number,\n  minSpins: number = 5\n): number => {\n  // Calculate one complete rotation distance\n  const oneCompleteRotation = prizeSize * prizesLength;\n\n  // Add multiple full rotations for the spinning effect\n  const spinningDistance = oneCompleteRotation * minSpins;\n\n  // Calculate the position of the winning prize\n  const prizePosition = prizeSize * winningIndex;\n\n  // Center offset: position the prize in the middle of the viewport\n  const centerOffset = (containerSize - prizeSize) / 2;\n\n  // Final offset: spinning distance + prize position - center offset\n  // This makes it spin multiple times before stopping at the winner\n  return spinningDistance + prizePosition - centerOffset;\n};\n\n/**\n * Validate that the winning index is within bounds\n *\n * @param winningIndex - Index to validate\n * @param prizesLength - Total number of prizes\n * @throws Error if index is invalid\n */\nexport const validateWinningIndex = (\n  winningIndex: number,\n  prizesLength: number\n): void => {\n  if (!Number.isInteger(winningIndex)) {\n    throw new Error(`winningIndex must be an integer, received: ${winningIndex}`);\n  }\n\n  if (winningIndex < 0) {\n    throw new Error(`winningIndex must be non-negative, received: ${winningIndex}`);\n  }\n\n  if (winningIndex >= prizesLength) {\n    throw new Error(\n      `winningIndex (${winningIndex}) is out of bounds. Must be less than prizes.length (${prizesLength})`\n    );\n  }\n};\n\n/**\n * Generate a unique ID for React keys\n * Uses crypto.randomUUID if available, falls back to timestamp + random\n *\n * @returns Unique string ID\n */\nexport const generateId = (): string => {\n  if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n    return crypto.randomUUID();\n  }\n\n  // Fallback for environments without crypto.randomUUID\n  return `${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;\n};\n\n/**\n * Clamp a number between min and max values\n *\n * @param value - Value to clamp\n * @param min - Minimum value\n * @param max - Maximum value\n * @returns Clamped value\n */\nexport const clamp = (value: number, min: number, max: number): number => {\n  return Math.min(Math.max(value, min), max);\n};\n","import { useEffect, useMemo, useRef, useState } from 'react';\nimport { calculateOffset, validateWinningIndex } from '../utils/calculations';\nimport type { Prize, Orientation } from '../types';\n\ninterface UseRouletteOptions {\n  prizes: readonly Prize[];\n  winningIndex: number;\n  isSpinning: boolean;\n  duration: number;\n  orientation: Orientation;\n  prizeSize: number;\n  onComplete?: (() => void) | undefined;\n  onSpinStart?: (() => void) | undefined;\n  minSpins?: number;\n}\n\ninterface UseRouletteReturn {\n  containerRef: React.RefObject<HTMLDivElement>;\n  offset: number;\n  isAnimating: boolean;\n}\n\n/**\n * Core hook for managing roulette state and animations\n * Handles offset calculation, animation timing, and callbacks\n */\n\nexport const useRoulette = ({\n  prizes,\n  winningIndex,\n  isSpinning,\n  duration,\n  orientation,\n  prizeSize,\n  onComplete,\n  onSpinStart,\n  minSpins,\n}: UseRouletteOptions): UseRouletteReturn => {\n  const containerRef = useRef<HTMLDivElement>(null);\n  const [offset, setOffset] = useState(0);\n  const [isAnimating, setIsAnimating] = useState(false);\n  const animationTimeoutRef = useRef<NodeJS.Timeout>();\n  const callbackFiredRef = useRef(false);\n\n  // Calculate optimal spins if not provided\n  const effectiveSpins = useMemo(() => {\n    if (minSpins !== undefined) return minSpins;\n    // Auto-calculate based on prize count and duration\n    const durationFactor = duration / 5000; // 5000ms baseline\n    const prizeFactor = Math.max(1, 10 / prizes.length);\n    const optimal = Math.ceil(2 * durationFactor * prizeFactor);\n    return Math.min(Math.max(optimal, 2), 6); // 2-6 spins\n  }, [minSpins, duration, prizes.length]);\n\n  // Validate winning index whenever it changes\n  useEffect(() => {\n    validateWinningIndex(winningIndex, prizes.length);\n  }, [winningIndex, prizes.length]);\n\n  // Handle spinning state changes\n  useEffect(() => {\n    // Clear any pending timeouts\n    if (animationTimeoutRef.current) {\n      clearTimeout(animationTimeoutRef.current);\n    }\n\n    if (!isSpinning) {\n      // Don't reset offset - keep the final position\n      // This prevents the visual \"jump\" back to start\n      setIsAnimating(false);\n      callbackFiredRef.current = false;\n      return;\n    }\n\n    // Reset to start position for new spin (without animation)\n    setOffset(0);\n    setIsAnimating(false);\n    callbackFiredRef.current = false;\n\n    // Fire onSpinStart callback\n    if (onSpinStart) {\n      onSpinStart();\n    }\n\n    // Use requestAnimationFrame to ensure the reset happens before animation starts\n    requestAnimationFrame(() => {\n      requestAnimationFrame(() => {\n        // Get container size for centering calculation\n        const containerSize = containerRef.current\n          ? orientation === 'horizontal'\n            ? containerRef.current.clientWidth\n            : containerRef.current.clientHeight\n          : prizeSize;\n\n        // Calculate final offset using the provided winning index\n        // (SpinRoulette component already handles smart sampling)\n        const finalOffset = calculateOffset(\n          prizeSize,\n          winningIndex,\n          containerSize,\n          prizes.length,\n          effectiveSpins\n        );\n\n        // Start animation\n        setIsAnimating(true);\n        setOffset(finalOffset);\n\n        // Set timeout for animation completion\n        animationTimeoutRef.current = setTimeout(() => {\n          setIsAnimating(false);\n\n          // Fire onComplete callback only once\n          if (onComplete && !callbackFiredRef.current) {\n            callbackFiredRef.current = true;\n            onComplete();\n          }\n        }, duration);\n      });\n    });\n\n    // Cleanup timeout on unmount or when dependencies change\n    return () => {\n      if (animationTimeoutRef.current) {\n        clearTimeout(animationTimeoutRef.current);\n      }\n    };\n  }, [\n    isSpinning,\n    winningIndex,\n    prizeSize,\n    duration,\n    orientation,\n    onComplete,\n    onSpinStart,\n    prizes.length,\n    effectiveSpins,\n  ]);\n\n  return {\n    containerRef,\n    offset,\n    isAnimating,\n  };\n};\n","/**\n * Utility to combine class names conditionally\n * Similar to clsx/classnames but lightweight and type-safe\n * \n * @param classes - Array of class names (strings, undefined, null, false)\n * @returns Combined class name string\n * \n * @example\n * cn('base', isActive && 'active', undefined, 'extra')\n * // => 'base active extra'\n */\nexport const cn = (\n  ...classes: Array<string | undefined | null | false>\n): string => {\n  return classes.filter(Boolean).join(' ');\n};\n\n","import { useMemo } from 'react';\nimport { useRoulette } from '../hooks/useRoulette';\nimport { cn } from '../utils/classNames';\nimport type { SpinRouletteProps, Prize } from '../types';\n\n// Default values\nconst DEFAULT_DURATION = 5000;\nconst DEFAULT_EASING = 'cubic-bezier(0.25, 0.1, 0.25, 1)';\nconst DEFAULT_ORIENTATION = 'horizontal';\nconst DEFAULT_PRIZE_SIZE = 150;\nconst DEFAULT_MIN_SPINS = 5;\n\n// Performance optimization: Thresholds for smart sampling\nconst SMALL_DATASET_THRESHOLD = 100; // Below this, render all items normally\nconst SAMPLE_SIZE = 50; // Number of unique items to sample for large datasets\n\n/**\n * Smart sampling strategy for large datasets\n * Instead of rendering thousands of items, we create a representative sample\n * that provides the visual effect of a real lottery while maintaining performance\n *\n * @param prizes - Original prize array\n * @param winningIndex - Index of the winning prize in original array\n * @param minSpins - Number of complete spins\n * @returns Sampled array and adjusted winning index\n */\nconst createSmartSample = <T extends Prize>(\n  prizes: readonly T[],\n  winningIndex: number,\n  minSpins: number\n): { sampledPrizes: T[]; adjustedWinningIndex: number } => {\n  // For small datasets, use all prizes\n  if (prizes.length <= SMALL_DATASET_THRESHOLD) {\n    const repetitions = minSpins + 2;\n    const repeated = Array(repetitions).fill(prizes).flat() as T[];\n    return {\n      sampledPrizes: repeated,\n      adjustedWinningIndex: prizes.length * minSpins + winningIndex,\n    };\n  }\n\n  // For large datasets: intelligent sampling\n  // console.log('🎰 Large dataset detected. Using smart sampling strategy...');\n\n  // Step 1: Create a sample pool (first N items + winning item if not included)\n  const sampleSize = Math.min(SAMPLE_SIZE, prizes.length);\n  const samplePool: T[] = [];\n\n  // Add first N items\n  for (let i = 0; i < sampleSize; i++) {\n    const prize = prizes[i];\n    if (prize) samplePool.push(prize);\n  }\n\n  // Ensure winning item is in the pool\n  const winningPrize = prizes[winningIndex];\n  if (winningPrize && !samplePool.includes(winningPrize)) {\n    samplePool.push(winningPrize);\n  }\n\n  // Step 2: Build spinning animation with repeated samples\n  const sampledPrizes: T[] = [];\n\n  // Add multiple rotations of the sample (creates the \"spinning\" effect)\n  for (let spin = 0; spin < minSpins; spin++) {\n    // Cycle through sample pool\n    for (let i = 0; i < sampleSize; i++) {\n      const prize = samplePool[i % samplePool.length];\n      if (prize) sampledPrizes.push(prize);\n    }\n  }\n\n  // Step 3: Add final rotation that ends with the winning prize\n  // Calculate where to place the winner in this final rotation\n  const finalRotationSize = sampleSize;\n  const winnerPositionInFinalRotation = Math.floor(finalRotationSize / 2); // Middle of final rotation\n\n  // Fill final rotation\n  for (let i = 0; i < finalRotationSize; i++) {\n    if (i === winnerPositionInFinalRotation && winningPrize) {\n      sampledPrizes.push(winningPrize);\n    } else {\n      // Fill with random samples\n      const prize = samplePool[i % samplePool.length];\n      if (prize) sampledPrizes.push(prize);\n    }\n  }\n\n  // Add buffer items after winner\n  const bufferSize = 10;\n  for (let i = 0; i < bufferSize; i++) {\n    const prize = samplePool[i % samplePool.length];\n    if (prize) sampledPrizes.push(prize);\n  }\n\n  // Adjusted winning index is where we placed the winner\n  const adjustedWinningIndex =\n    sampledPrizes.length -\n    bufferSize -\n    (finalRotationSize - winnerPositionInFinalRotation);\n\n  // console.log('✅ Smart sampling complete:', {\n  //   originalSize: prizes.length,\n  //   sampledSize: sampledPrizes.length,\n  //   originalWinningIndex: winningIndex,\n  //   adjustedWinningIndex,\n  //   winningPrize: winningPrize?.label,\n  // });\n\n  return { sampledPrizes, adjustedWinningIndex };\n};\n\n/**\n * SpinRoulette - A headless linear roulette/spinner component\n *\n * Provides a customizable linear spinner that scrolls horizontally or vertically\n * to land on a winning prize. Fully headless for maximum styling flexibility.\n *\n * @example\n * ```tsx\n * <SpinRoulette\n *   prizes={prizes}\n *   winningIndex={3}\n *   isSpinning={isSpinning}\n *   onComplete={() => console.log('Done!')}\n *   orientation=\"horizontal\"\n * />\n * ```\n */\nexport const SpinRoulette = ({\n  prizes,\n  winningIndex,\n  isSpinning,\n  onComplete,\n  onSpinStart,\n  duration = DEFAULT_DURATION,\n  easing = DEFAULT_EASING,\n  orientation = DEFAULT_ORIENTATION,\n  prizeSize = DEFAULT_PRIZE_SIZE,\n  className,\n  prizeClassName,\n  indicatorClassName,\n  renderPrize,\n  renderIndicator,\n  minSpins = DEFAULT_MIN_SPINS,\n}: SpinRouletteProps) => {\n  // Validate props\n  if (prizes.length === 0) {\n    throw new Error('SpinRoulette: prizes array cannot be empty');\n  }\n\n  // Apply smart sampling strategy\n  const { sampledPrizes, adjustedWinningIndex } = useMemo(\n    () => createSmartSample(prizes, winningIndex, minSpins),\n    [prizes, winningIndex, minSpins]\n  );\n\n  // Use the roulette hook for state management with sampled data\n  // IMPORTANT: minSpins is set to 0 because createSmartSample already applied the rotations\n  const { containerRef, offset, isAnimating } = useRoulette({\n    prizes: sampledPrizes,\n    winningIndex: adjustedWinningIndex,\n    isSpinning,\n    duration,\n    orientation,\n    prizeSize,\n    onComplete,\n    onSpinStart,\n    minSpins: 0, // Already applied in createSmartSample\n  });\n\n  // Memoize inline styles for the prize list container\n  const listStyle = useMemo(() => {\n    const transform =\n      orientation === 'horizontal'\n        ? `translate3d(-${offset}px, 0, 0)`\n        : `translate3d(0, -${offset}px, 0)`;\n\n    return {\n      display: 'flex',\n      flexDirection:\n        orientation === 'horizontal' ? ('row' as const) : ('column' as const),\n      transition: isAnimating ? `transform ${duration}ms ${easing}` : 'none',\n      transform,\n      willChange: 'transform',\n    };\n  }, [offset, isAnimating, duration, easing, orientation]);\n\n  // Memoize prize items rendering (now using pre-optimized sampledPrizes)\n  const prizeItems = useMemo(() => {\n    return sampledPrizes.map((prize, globalIndex) => {\n      const prizeStyle = {\n        flexShrink: 0,\n        width: orientation === 'horizontal' ? `${prizeSize}px` : '100%',\n        height: orientation === 'vertical' ? `${prizeSize}px` : '100%',\n        ...prize.style,\n      };\n\n      const combinedPrizeClassName = cn(prizeClassName, prize.className);\n\n      return (\n        <div\n          key={`${prize.id}-rep-${globalIndex}`}\n          className={combinedPrizeClassName}\n          style={prizeStyle}\n          role=\"option\"\n          aria-label={prize.label}\n        >\n          {renderPrize ? (\n            renderPrize(prize as Prize)\n          ) : (\n            <div\n              style={{\n                display: 'flex',\n                flexDirection: 'column',\n                alignItems: 'center',\n                justifyContent: 'center',\n                width: '100%',\n                height: '100%',\n                padding: '1rem',\n              }}\n            >\n              {prize.image && (\n                <img\n                  src={prize.image}\n                  alt={prize.label}\n                  style={{\n                    maxWidth: '80%',\n                    maxHeight: '60%',\n                    objectFit: 'contain',\n                  }}\n                />\n              )}\n              <span\n                style={{\n                  marginTop: prize.image ? '0.5rem' : 0,\n                  fontSize: '0.875rem',\n                  fontWeight: 600,\n                  textAlign: 'center',\n                }}\n              >\n                {prize.label}\n              </span>\n            </div>\n          )}\n        </div>\n      );\n    });\n  }, [sampledPrizes, prizeSize, orientation, prizeClassName, renderPrize]);\n\n  // Default indicator styles\n  const defaultIndicatorStyle = {\n    position: 'absolute' as const,\n    pointerEvents: 'none' as const,\n    zIndex: 10,\n    ...(orientation === 'horizontal'\n      ? {\n          top: 0,\n          left: '50%',\n          transform: 'translateX(-50%)',\n          width: '2px',\n          height: '100%',\n          backgroundColor: 'rgba(239, 68, 68, 0.8)',\n        }\n      : {\n          left: 0,\n          top: '50%',\n          transform: 'translateY(-50%)',\n          height: '2px',\n          width: '100%',\n          backgroundColor: 'rgba(239, 68, 68, 0.8)',\n        }),\n  };\n\n  return (\n    <div\n      ref={containerRef}\n      className={className}\n      style={{\n        position: 'relative',\n        overflow: 'hidden',\n        width: '100%',\n        height: '100%',\n      }}\n      role=\"listbox\"\n      aria-label=\"Prize roulette\"\n      aria-busy={isAnimating}\n    >\n      {/* Center indicator/pointer */}\n      {renderIndicator ? (\n        renderIndicator()\n      ) : (\n        <div\n          className={indicatorClassName}\n          style={indicatorClassName ? undefined : defaultIndicatorStyle}\n          aria-hidden=\"true\"\n        />\n      )}\n\n      {/* Prize list */}\n      <div style={listStyle}>{prizeItems}</div>\n    </div>\n  );\n};\n\nSpinRoulette.displayName = 'SpinRoulette';\n"],"names":["calculateOffset","prizeSize","winningIndex","containerSize","prizesLength","minSpins","spinningDistance","prizePosition","centerOffset","validateWinningIndex","useRoulette","prizes","isSpinning","duration","orientation","onComplete","onSpinStart","containerRef","useRef","offset","setOffset","useState","isAnimating","setIsAnimating","animationTimeoutRef","callbackFiredRef","effectiveSpins","useMemo","durationFactor","prizeFactor","optimal","useEffect","finalOffset","cn","classes","DEFAULT_DURATION","DEFAULT_EASING","DEFAULT_ORIENTATION","DEFAULT_PRIZE_SIZE","DEFAULT_MIN_SPINS","SMALL_DATASET_THRESHOLD","SAMPLE_SIZE","createSmartSample","repetitions","sampleSize","samplePool","i","prize","winningPrize","sampledPrizes","spin","finalRotationSize","winnerPositionInFinalRotation","bufferSize","adjustedWinningIndex","SpinRoulette","easing","className","prizeClassName","indicatorClassName","renderPrize","renderIndicator","listStyle","transform","prizeItems","globalIndex","prizeStyle","combinedPrizeClassName","jsx","jsxs","defaultIndicatorStyle"],"mappings":"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"}