{"version":3,"file":"index.cjs","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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"}