@mks2508/waapi-animation-primitives 0.2.0 → 0.3.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/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["CSS_PROPERTY_DEFINITIONS: PropertyDefinition[]","baseContainerStyle: React.CSSProperties","options: Intl.NumberFormatOptions","result: CharItem[]","seq: number[]","diff: number","DebugProvider: React.FC<{ children: React.ReactNode }>","overlaps: string[]","SlidingText: React.FC<SlidingTextProps>","newSeparatorState: SeparatorState","exitAnimations: Animation[]","flipAnimations: Animation[]","AnimatedTokens: React.FC<AnimatedTokensProps>","additionalSeparators: string[]","visibleTokens","DEFAULT_DURATION","DEFAULT_EASING","animations: Animation[]","flipAnimations: Animation[]","siblingsToAnimate: string[]","exitAnimations: Animation[]","animations: Animation[]","added: string[]","result: ReactNode[]","layoutStyles: Record<ReorderLayout, CSSProperties>","records: ChildRecord[]","parts: ListPart[]","AnimatedTokensV2: React.FC<AnimatedTokensV2Props>","additionalSeparators: string[]","Reorder","DEFAULT_DURATION","DEFAULT_EASING","technique: MorphTechnique"],"sources":["../src/utils/animationUtils.ts","../src/styles/cssTokens.ts","../src/styles/injectStyles.ts","../src/components/SlidingNumber.tsx","../src/contexts/DebugContext.tsx","../src/utils/debugCapture.ts","../src/utils/choreographyTracker.ts","../src/components/SlidingText.tsx","../src/hooks/useWAAPIAnimations.ts","../src/components/AnimatedTokens.tsx","../src/core/useElementRegistry.ts","../src/core/usePositionCapture.ts","../src/core/useFLIPAnimation.ts","../src/core/useAnimationOrchestrator.ts","../src/primitives/reorder/useReorder.ts","../src/primitives/reorder/useReorderPresence.ts","../src/primitives/reorder/Reorder.tsx","../src/hooks/useListFormat.ts","../src/components/AnimatedTokensV2.tsx","../src/primitives/morph/MorphContext.tsx","../src/primitives/morph/techniques/useFLIPClipPath.ts","../src/primitives/morph/techniques/useCSSGridMorph.ts","../src/primitives/morph/techniques/useViewTransitions.ts","../src/primitives/morph/useMorph.ts","../src/primitives/morph/Morph.tsx","../src/styles/slidingText.styles.ts","../src/styles/animatedTokens.styles.ts"],"sourcesContent":["// ============================================\n// ANIMATION CONFIGURATION\n// Sistema configurable para iteración rápida\n// Research-based values from:\n// - Material Design Motion\n// - CSS-Tricks FLIP Technique\n// - Josh W. Comeau Spring Animations\n// - Easings.net\n// ============================================\n\n// ============================================\n// RESPONSIVE & ACCESSIBILITY HELPERS\n// ============================================\n\n/**\n * Obtiene duración de animación responsiva para el dispositivo actual\n * @param baseDuration - Duración base en ms\n * @returns Duración ajustada para dispositivo actual\n */\nexport const getResponsiveDuration = (baseDuration: number): number => {\n if (typeof window === 'undefined') return baseDuration;\n\n // Verificar si el usuario prefiere reducir movimiento\n const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;\n if (prefersReducedMotion) return 0;\n\n // Detectar dispositivo móvil y reducir duración para mejor performance\n const isMobile = window.innerWidth < 768; // md breakpoint\n if (isMobile) {\n return Math.round(baseDuration * 0.6); // 60% de la duración en móviles\n }\n\n return baseDuration;\n};\n\n/**\n * Obtiene stagger delay responsivo\n * @param baseDelay - Delay base en ms\n * @returns Delay ajustado para dispositivo actual\n */\nexport const getResponsiveStagger = (baseDelay: number): number => {\n if (typeof window === 'undefined') return baseDelay;\n\n const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;\n if (prefersReducedMotion) return 0;\n\n const isMobile = window.innerWidth < 768;\n return isMobile ? Math.round(baseDelay * 0.5) : baseDelay;\n};\n\n// TIMING (ms)\nexport const TIMING = {\n // Durations\n ENTER_DURATION: 200,\n EXIT_DURATION: 180, // Faster to not block FLIP\n COLLAPSE_DURATION: 200,\n FLIP_DURATION: 300, // Longer for spring effect\n\n // Stagger between characters\n ENTER_STAGGER: 15,\n EXIT_STAGGER: 0, // No stagger on exit (cleaner)\n\n // Delays\n COLLAPSE_DELAY: 30, // Start collapse after fade begins\n FLIP_DELAY_PERCENT: 0.25, // Start FLIP at 25% of exit\n\n // Thresholds\n MIN_DELTA_PX: 1, // Ignore sub-pixel movements\n} as const;\n\n// TRANSFORMS (px/deg)\nexport const TRANSFORMS = {\n OFFSET_Y_ENTER: 8,\n OFFSET_Y_EXIT: -8,\n OFFSET_X: 16,\n\n // Scale values (más visibles según guía)\n SCALE_ENTER: 0.85, // Para tokens que entran\n SCALE_EXIT: 0.85, // Más visible que 0.95\n\n ROTATE_EXIT: 0, // Optional: -2 for playful feel\n} as const;\n\n// EFFECTS\nexport const EFFECTS = {\n BLUR_ENTER: 4, // px\n BLUR_EXIT: 2, // Less blur on exit (faster)\n} as const;\n\n// EASINGS - Research-based (Material Design + CSS-Tricks + Josh W. Comeau)\nexport const EASINGS = {\n // Material Design (estándar industry)\n MATERIAL_DECELERATE: 'cubic-bezier(0, 0, 0.2, 1)', // Para elementos que entran\n MATERIAL_ACCELERATE: 'cubic-bezier(0.4, 0, 1, 1)', // Para elementos que salen\n MATERIAL_STANDARD: 'cubic-bezier(0.4, 0, 0.2, 1)', // Para movimientos estándar\n\n // Física realista (CSS-Tricks recommendation)\n EASE_REORDER: 'cubic-bezier(0.215, 0.61, 0.355, 1)', // Perfecto para FLIP/reordenamiento\n\n // Standard legacy (compatibilidad)\n EASE_OUT_CUBIC: 'cubic-bezier(0.33, 1, 0.68, 1)',\n EASE_IN_CUBIC: 'cubic-bezier(0.32, 0, 0.67, 0)',\n EASE_IN_OUT: 'cubic-bezier(0.42, 0, 0.58, 1)',\n EASE_OUT_EXPO: 'cubic-bezier(0.16, 1, 0.3, 1)',\n\n // FLIP + Spring optimizados\n EASE_FLIP: 'cubic-bezier(0.2, 0, 0.2, 1)', // Para FLIP technique\n\n // Spring via linear() - Josh W. Comeau (mantenemos para casos especiales)\n SPRING_GENTLE: `linear(0, 0.009, 0.035 2.1%, 0.141 4.4%, 0.723 12.9%, 0.938 16.7%, 1.017 19.4%, 1.067, 1.099 24.3%, 1.108 26%, 1.100, 1.078 30.1%, 1.049 32.5%, 0.994 37.3%, 0.981 40.2%, 0.974 43.4%, 0.975 50.2%, 0.997 62.5%, 1.001 74.7%, 1)`,\n\n SPRING_SNAPPY: `linear(0, 0.006, 0.024 2%, 0.096 4.2%, 0.397 9.3%, 0.861 15.8%, 1.002 18.7%, 1.093 21.4%, 1.143 24%, 1.156, 1.149 28.3%, 1.115 31.5%, 1.022 40%, 0.988 47.1%, 0.984 55.1%, 0.998 72.3%, 1.001 85.4%, 1)`,\n} as const;\n\n/**\n * Configuración de animación responsiva con accessibility\n */\nexport const RESPONSIVE_CONFIGS = {\n tokenEnter: {\n get duration() { return getResponsiveDuration(TIMING.ENTER_DURATION); },\n get stagger() { return getResponsiveStagger(TIMING.ENTER_STAGGER); },\n easing: EASINGS.MATERIAL_DECELERATE, // Material Design - entra rápido y se asienta\n blur: EFFECTS.BLUR_ENTER,\n offsetY: TRANSFORMS.OFFSET_Y_ENTER,\n scale: TRANSFORMS.SCALE_ENTER, // Nuevo scale más visible\n },\n tokenExit: {\n get duration() { return getResponsiveDuration(TIMING.EXIT_DURATION); },\n get stagger() { return getResponsiveStagger(TIMING.EXIT_STAGGER); },\n easing: EASINGS.MATERIAL_ACCELERATE, // Material Design - sale rápido\n blur: EFFECTS.BLUR_EXIT,\n offsetY: TRANSFORMS.OFFSET_Y_EXIT,\n scale: TRANSFORMS.SCALE_EXIT,\n },\n collapse: {\n get duration() { return getResponsiveDuration(TIMING.COLLAPSE_DURATION); },\n delay: TIMING.COLLAPSE_DELAY,\n easing: EASINGS.MATERIAL_STANDARD, // Material Design - estándar\n },\n flip: {\n get duration() { return getResponsiveDuration(TIMING.FLIP_DURATION); },\n delayPercent: TIMING.FLIP_DELAY_PERCENT,\n easing: EASINGS.EASE_REORDER, // Física realista para reordenamiento\n },\n} as const;\n\n// LEGACY DEFAULTS (for backward compatibility)\nexport const ANIMATION_DEFAULTS = {\n // Timing\n DURATION_ENTER: TIMING.ENTER_DURATION,\n DURATION_EXIT: TIMING.EXIT_DURATION,\n DURATION_FLIP: TIMING.FLIP_DURATION,\n STAGGER_DELAY: TIMING.ENTER_STAGGER,\n\n // Transform offsets\n OFFSET_VERTICAL: TRANSFORMS.OFFSET_Y_ENTER,\n OFFSET_HORIZONTAL: TRANSFORMS.OFFSET_X,\n\n // Blur\n BLUR_AMOUNT: EFFECTS.BLUR_ENTER,\n\n // Easing (updated to research-based values)\n EASING_ENTER: EASINGS.EASE_OUT_CUBIC,\n EASING_EXIT: EASINGS.EASE_IN_CUBIC,\n EASING_FLIP: EASINGS.SPRING_GENTLE,\n\n // Spring (para linear())\n SPRING_EASING: EASINGS.SPRING_GENTLE,\n};\n\n// ANIMATION CONFIGS (composite presets for specific use cases)\nexport const ANIMATION_CONFIGS = {\n tokenEnter: {\n duration: TIMING.ENTER_DURATION,\n stagger: TIMING.ENTER_STAGGER,\n easing: EASINGS.MATERIAL_DECELERATE, // Material Design\n blur: EFFECTS.BLUR_ENTER,\n offsetY: TRANSFORMS.OFFSET_Y_ENTER,\n scale: TRANSFORMS.SCALE_ENTER, // Scale más visible\n },\n tokenExit: {\n duration: TIMING.EXIT_DURATION,\n stagger: TIMING.EXIT_STAGGER,\n easing: EASINGS.MATERIAL_ACCELERATE, // Material Design - salida rápida\n blur: EFFECTS.BLUR_EXIT,\n offsetY: TRANSFORMS.OFFSET_Y_EXIT,\n scale: TRANSFORMS.SCALE_EXIT,\n },\n collapse: {\n duration: TIMING.COLLAPSE_DURATION,\n delay: TIMING.COLLAPSE_DELAY,\n easing: EASINGS.MATERIAL_STANDARD, // Material Design\n },\n flip: {\n duration: TIMING.FLIP_DURATION,\n delayPercent: TIMING.FLIP_DELAY_PERCENT,\n easing: EASINGS.EASE_REORDER, // Física realista para FLIP\n },\n} as const;\n\nexport const PRESETS = {\n newToken: {\n mode: 'character' as const,\n direction: 'vertical' as const,\n staggerDelay: 15,\n blur: true,\n widthAnimation: true,\n duration: 200,\n initial: 'initial' as const,\n },\n\n existingToken: {\n mode: 'none' as const,\n blur: false,\n widthAnimation: false,\n initial: false as const,\n },\n\n placeholder: {\n mode: 'word' as const,\n direction: 'vertical' as const,\n blur: true,\n widthAnimation: false,\n duration: 150,\n },\n\n separator: {\n duration: 100,\n widthAnimation: true,\n },\n};\n","import type React from 'react';\nimport { TIMING, TRANSFORMS, EFFECTS, EASINGS } from '../utils/animationUtils';\n\n// ============================================\n// CSS TOKENS - Single Source of Truth\n// Genera CSS variables y estilos desde animationUtils.ts\n// ============================================\n\n// ============================================\n// @property DEFINITIONS (Modern CSS 2024)\n// These define typed, animatable CSS custom properties\n// ============================================\n\nexport interface PropertyDefinition {\n name: string;\n syntax: '<time>' | '<length>' | '<number>' | '<integer>' | '<color>' | '<percentage>';\n inherits: boolean;\n initialValue: string;\n}\n\nexport const CSS_PROPERTY_DEFINITIONS: PropertyDefinition[] = [\n // Timing\n { name: '--waapi-duration-exit', syntax: '<time>', inherits: true, initialValue: `${TIMING.EXIT_DURATION}ms` },\n { name: '--waapi-duration-enter', syntax: '<time>', inherits: true, initialValue: `${TIMING.ENTER_DURATION}ms` },\n { name: '--waapi-duration-flip', syntax: '<time>', inherits: true, initialValue: `${TIMING.FLIP_DURATION}ms` },\n { name: '--waapi-duration-collapse', syntax: '<time>', inherits: true, initialValue: `${TIMING.COLLAPSE_DURATION}ms` },\n { name: '--waapi-stagger-enter', syntax: '<time>', inherits: true, initialValue: `${TIMING.ENTER_STAGGER}ms` },\n { name: '--waapi-stagger-exit', syntax: '<time>', inherits: true, initialValue: `${TIMING.EXIT_STAGGER}ms` },\n\n // Transforms\n { name: '--waapi-offset-y-enter', syntax: '<length>', inherits: true, initialValue: `${TRANSFORMS.OFFSET_Y_ENTER}px` },\n { name: '--waapi-offset-y-exit', syntax: '<length>', inherits: true, initialValue: `${TRANSFORMS.OFFSET_Y_EXIT}px` },\n { name: '--waapi-offset-x', syntax: '<length>', inherits: true, initialValue: `${TRANSFORMS.OFFSET_X}px` },\n { name: '--waapi-scale-exit', syntax: '<number>', inherits: true, initialValue: `${TRANSFORMS.SCALE_EXIT}` },\n { name: '--waapi-scale-enter', syntax: '<number>', inherits: true, initialValue: `${TRANSFORMS.SCALE_ENTER}` },\n\n // Effects\n { name: '--waapi-blur-enter', syntax: '<length>', inherits: true, initialValue: `${EFFECTS.BLUR_ENTER}px` },\n { name: '--waapi-blur-exit', syntax: '<length>', inherits: true, initialValue: `${EFFECTS.BLUR_EXIT}px` },\n\n // Stagger index (for progressive enhancement)\n { name: '--waapi-stagger-index', syntax: '<integer>', inherits: false, initialValue: '0' },\n];\n\n/**\n * Generates @property CSS rules from definitions\n * These enable typed, animatable CSS custom properties\n */\nexport const generatePropertyRules = (): string => {\n return CSS_PROPERTY_DEFINITIONS.map(def => `@property ${def.name} {\n syntax: '${def.syntax}';\n inherits: ${def.inherits};\n initial-value: ${def.initialValue};\n}`).join('\\n\\n');\n};\n\n// CSS Variable names (para type safety)\nexport const CSS_VAR_NAMES = {\n // Timing\n durationEnter: '--waapi-duration-enter',\n durationExit: '--waapi-duration-exit',\n durationCollapse: '--waapi-duration-collapse',\n durationFlip: '--waapi-duration-flip',\n staggerEnter: '--waapi-stagger-enter',\n staggerExit: '--waapi-stagger-exit',\n\n // Transforms\n offsetYEnter: '--waapi-offset-y-enter',\n offsetYExit: '--waapi-offset-y-exit',\n offsetX: '--waapi-offset-x',\n scaleExit: '--waapi-scale-exit',\n\n // Effects\n blurEnter: '--waapi-blur-enter',\n blurExit: '--waapi-blur-exit',\n\n // Easings\n easeEnter: '--waapi-ease-enter',\n easeExit: '--waapi-ease-exit',\n easeCollapse: '--waapi-ease-collapse',\n easeFlip: '--waapi-ease-flip',\n\n // Separator specific\n separatorDuration: '--waapi-separator-duration',\n separatorDelay: '--waapi-separator-delay',\n separatorEasing: '--waapi-separator-easing',\n} as const;\n\n// Tipo para las CSS variables\nexport type CSSVarName = typeof CSS_VAR_NAMES[keyof typeof CSS_VAR_NAMES];\n\n// ============================================\n// CSS VARIABLE VALUES (derivados de animationUtils)\n// ============================================\n\nexport const CSS_VAR_VALUES = {\n // Timing (convertido a CSS time)\n [CSS_VAR_NAMES.durationEnter]: `${TIMING.ENTER_DURATION}ms`,\n [CSS_VAR_NAMES.durationExit]: `${TIMING.EXIT_DURATION}ms`,\n [CSS_VAR_NAMES.durationCollapse]: `${TIMING.COLLAPSE_DURATION}ms`,\n [CSS_VAR_NAMES.durationFlip]: `${TIMING.FLIP_DURATION}ms`,\n [CSS_VAR_NAMES.staggerEnter]: `${TIMING.ENTER_STAGGER}ms`,\n [CSS_VAR_NAMES.staggerExit]: `${TIMING.EXIT_STAGGER}ms`,\n\n // Transforms (convertido a CSS units)\n [CSS_VAR_NAMES.offsetYEnter]: `${TRANSFORMS.OFFSET_Y_ENTER}px`,\n [CSS_VAR_NAMES.offsetYExit]: `${TRANSFORMS.OFFSET_Y_EXIT}px`,\n [CSS_VAR_NAMES.offsetX]: `${TRANSFORMS.OFFSET_X}px`,\n [CSS_VAR_NAMES.scaleExit]: `${TRANSFORMS.SCALE_EXIT}`,\n\n // Effects\n [CSS_VAR_NAMES.blurEnter]: `${EFFECTS.BLUR_ENTER}px`,\n [CSS_VAR_NAMES.blurExit]: `${EFFECTS.BLUR_EXIT}px`,\n\n // Easings (ya son strings válidos)\n [CSS_VAR_NAMES.easeEnter]: EASINGS.EASE_OUT_CUBIC,\n [CSS_VAR_NAMES.easeExit]: EASINGS.EASE_IN_CUBIC,\n [CSS_VAR_NAMES.easeCollapse]: EASINGS.EASE_IN_OUT,\n [CSS_VAR_NAMES.easeFlip]: EASINGS.EASE_FLIP,\n\n // Separator\n [CSS_VAR_NAMES.separatorDuration]: `${TIMING.FLIP_DURATION}ms`,\n [CSS_VAR_NAMES.separatorDelay]: `${Math.round(TIMING.EXIT_DURATION * TIMING.FLIP_DELAY_PERCENT)}ms`,\n [CSS_VAR_NAMES.separatorEasing]: EASINGS.EASE_FLIP,\n} as const;\n\n// ============================================\n// HELPER: Referencia a CSS variable\n// ============================================\n\nexport const cssVar = (name: CSSVarName): string => `var(${name})`;\n\n// ============================================\n// STYLE OBJECTS (usando CSS variables)\n// ============================================\n\nexport const baseContainerStyle: React.CSSProperties = {\n // Inyectar todas las CSS variables en el container\n ...CSS_VAR_VALUES as unknown as React.CSSProperties,\n};\n\n// ============================================\n// ANIMATED TOKENS STYLES\n// ============================================\n\nexport const animatedTokensBaseStyles = {\n container: {\n ...baseContainerStyle,\n display: 'flex',\n flexWrap: 'wrap',\n alignItems: 'center',\n gap: 0,\n } as React.CSSProperties,\n\n placeholder: {\n color: 'var(--muted-foreground)',\n fontStyle: 'italic',\n } as React.CSSProperties,\n\n tokenWrapper: {\n display: 'inline-flex',\n alignItems: 'center',\n } as React.CSSProperties,\n\n tokenWrapperExitCompleted: {\n position: 'absolute',\n opacity: 0,\n pointerEvents: 'none',\n } as React.CSSProperties,\n\n separator: {\n display: 'inline',\n whiteSpace: 'pre',\n marginLeft: '0.25em',\n willChange: 'transform, opacity',\n // NO CSS transitions - WAAPI handles all animations\n // This prevents conflicts between CSS and WAAPI\n } as React.CSSProperties,\n\n separatorExitCoordinated: {\n opacity: 0,\n transform: `translateY(-8px) scale(${TRANSFORMS.SCALE_EXIT})`,\n } as React.CSSProperties,\n\n separatorExitCompleted: {\n opacity: 0,\n position: 'absolute',\n pointerEvents: 'none',\n width: 0,\n overflow: 'hidden',\n } as React.CSSProperties,\n\n overflow: {\n display: 'inline-flex',\n alignItems: 'center',\n whiteSpace: 'nowrap',\n } as React.CSSProperties,\n} as const;\n\n// ============================================\n// SLIDING TEXT STYLES\n// ============================================\n\nexport const slidingTextBaseStyles = {\n container: {\n ...baseContainerStyle,\n display: 'inline-flex',\n overflow: 'hidden',\n verticalAlign: 'bottom',\n willChange: 'width',\n } as React.CSSProperties,\n\n content: {\n display: 'inline-flex',\n whiteSpace: 'pre',\n } as React.CSSProperties,\n\n token: {\n display: 'inline-block',\n willChange: 'transform, opacity, filter',\n backfaceVisibility: 'hidden',\n fontWeight: 500,\n } as React.CSSProperties,\n\n enterFrom: {\n opacity: 0,\n filter: `blur(${cssVar(CSS_VAR_NAMES.blurEnter)})`,\n } as React.CSSProperties,\n\n enterTo: {\n opacity: 1,\n transform: 'translate(0, 0)',\n filter: 'blur(0)',\n } as React.CSSProperties,\n\n exitActive: {\n opacity: 0,\n filter: `blur(${cssVar(CSS_VAR_NAMES.blurExit)})`,\n } as React.CSSProperties,\n\n // Direction variants\n verticalEnterFrom: {\n transform: `translateY(${cssVar(CSS_VAR_NAMES.offsetYEnter)})`,\n } as React.CSSProperties,\n\n verticalExitActive: {\n transform: `translateY(${cssVar(CSS_VAR_NAMES.offsetYExit)})`,\n } as React.CSSProperties,\n\n horizontalEnterFrom: {\n transform: `translateX(${cssVar(CSS_VAR_NAMES.offsetX)})`,\n } as React.CSSProperties,\n\n horizontalExitActive: {\n transform: `translateX(calc(${cssVar(CSS_VAR_NAMES.offsetX)} * -1))`,\n } as React.CSSProperties,\n} as const;\n\n// ============================================\n// RESPONSIVE OVERRIDES (para media queries en JS)\n// ============================================\n\nexport const responsiveOverrides = {\n mobile: {\n [CSS_VAR_NAMES.durationEnter]: '120ms',\n [CSS_VAR_NAMES.durationExit]: '100ms',\n [CSS_VAR_NAMES.durationCollapse]: '120ms',\n [CSS_VAR_NAMES.durationFlip]: '180ms',\n [CSS_VAR_NAMES.staggerEnter]: '8ms',\n [CSS_VAR_NAMES.blurEnter]: '2px',\n [CSS_VAR_NAMES.blurExit]: '1px',\n [CSS_VAR_NAMES.offsetYEnter]: '4px',\n [CSS_VAR_NAMES.offsetYExit]: '-4px',\n } as React.CSSProperties,\n\n smallMobile: {\n [CSS_VAR_NAMES.durationEnter]: '80ms',\n [CSS_VAR_NAMES.durationExit]: '60ms',\n [CSS_VAR_NAMES.durationFlip]: '120ms',\n [CSS_VAR_NAMES.staggerEnter]: '4ms',\n [CSS_VAR_NAMES.blurEnter]: '1px',\n [CSS_VAR_NAMES.blurExit]: '0px',\n [CSS_VAR_NAMES.offsetYEnter]: '2px',\n [CSS_VAR_NAMES.offsetYExit]: '-2px',\n } as React.CSSProperties,\n\n reducedMotion: {\n [CSS_VAR_NAMES.durationEnter]: '0ms',\n [CSS_VAR_NAMES.durationExit]: '0ms',\n [CSS_VAR_NAMES.durationCollapse]: '0ms',\n [CSS_VAR_NAMES.durationFlip]: '0ms',\n [CSS_VAR_NAMES.staggerEnter]: '0ms',\n [CSS_VAR_NAMES.staggerExit]: '0ms',\n [CSS_VAR_NAMES.blurEnter]: '0px',\n [CSS_VAR_NAMES.blurExit]: '0px',\n [CSS_VAR_NAMES.offsetYEnter]: '0px',\n [CSS_VAR_NAMES.offsetYExit]: '0px',\n [CSS_VAR_NAMES.scaleExit]: '1',\n } as React.CSSProperties,\n\n highContrast: {\n [CSS_VAR_NAMES.blurEnter]: '0px',\n [CSS_VAR_NAMES.blurExit]: '0px',\n } as React.CSSProperties,\n} as const;\n\n// ============================================\n// HELPER: Obtener estilos responsivos\n// ============================================\n\nexport const getResponsiveTokenStyles = (): React.CSSProperties => {\n if (typeof window === 'undefined') return CSS_VAR_VALUES as unknown as React.CSSProperties;\n\n const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;\n if (prefersReducedMotion) {\n return { ...CSS_VAR_VALUES, ...responsiveOverrides.reducedMotion } as unknown as React.CSSProperties;\n }\n\n const prefersHighContrast = window.matchMedia('(prefers-contrast: high)').matches;\n if (prefersHighContrast) {\n return { ...CSS_VAR_VALUES, ...responsiveOverrides.highContrast } as unknown as React.CSSProperties;\n }\n\n const width = window.innerWidth;\n if (width <= 480) {\n return { ...CSS_VAR_VALUES, ...responsiveOverrides.smallMobile } as unknown as React.CSSProperties;\n }\n if (width <= 768) {\n return { ...CSS_VAR_VALUES, ...responsiveOverrides.mobile } as unknown as React.CSSProperties;\n }\n\n return CSS_VAR_VALUES as unknown as React.CSSProperties;\n};\n\n// ============================================\n// CSS STRING GENERATOR (para inyección)\n// ============================================\n\nexport const generateCSSVariables = (): string => {\n const vars = Object.entries(CSS_VAR_VALUES)\n .map(([key, value]) => ` ${key}: ${value};`)\n .join('\\n');\n\n return `:root {\\n${vars}\\n}`;\n};\n\nexport const generateResponsiveCSS = (): string => {\n const propertyRules = generatePropertyRules();\n const baseVars = generateCSSVariables();\n\n const mobileVars = Object.entries(responsiveOverrides.mobile)\n .map(([key, value]) => ` ${key}: ${value};`)\n .join('\\n');\n\n const smallMobileVars = Object.entries(responsiveOverrides.smallMobile)\n .map(([key, value]) => ` ${key}: ${value};`)\n .join('\\n');\n\n const reducedMotionVars = Object.entries(responsiveOverrides.reducedMotion)\n .map(([key, value]) => ` ${key}: ${value};`)\n .join('\\n');\n\n const highContrastVars = Object.entries(responsiveOverrides.highContrast)\n .map(([key, value]) => ` ${key}: ${value};`)\n .join('\\n');\n\n return `\n/* ============================================\n @property DEFINITIONS (Modern CSS 2024)\n Enables typed, animatable CSS custom properties\n Baseline: July 2024 - All major browsers\n ============================================ */\n${propertyRules}\n\n/* ============================================\n CSS VARIABLES (derived from animationUtils.ts SSOT)\n ============================================ */\n${baseVars}\n\n@media (max-width: 768px) {\n :root {\n${mobileVars}\n }\n}\n\n@media (max-width: 480px) {\n :root {\n${smallMobileVars}\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n :root {\n${reducedMotionVars}\n }\n}\n\n@media (prefers-contrast: high) {\n :root {\n${highContrastVars}\n }\n}\n`.trim();\n};\n","import { generateResponsiveCSS } from './cssTokens';\n\nconst STYLE_ID = 'waapi-animation-primitives-vars';\n\nlet injected = false;\n\n/**\n * Inyecta las CSS variables en el documento.\n * Se llama automáticamente al importar el package.\n * Es idempotente - solo inyecta una vez.\n */\nexport const injectCSSVariables = (): void => {\n if (typeof document === 'undefined') return;\n if (injected) return;\n if (document.getElementById(STYLE_ID)) {\n injected = true;\n return;\n }\n\n const style = document.createElement('style');\n style.id = STYLE_ID;\n style.textContent = generateResponsiveCSS();\n document.head.appendChild(style);\n injected = true;\n};\n\n/**\n * Remueve las CSS variables inyectadas.\n * Útil para cleanup en tests o hot reload.\n */\nexport const removeCSSVariables = (): void => {\n if (typeof document === 'undefined') return;\n\n const style = document.getElementById(STYLE_ID);\n if (style) {\n style.remove();\n injected = false;\n }\n};\n\n/**\n * Fuerza la re-inyección de CSS variables.\n * Útil cuando cambian los valores dinámicamente.\n */\nexport const reinjectCSSVariables = (): void => {\n removeCSSVariables();\n injectCSSVariables();\n};\n\n// Auto-inject on import (solo en browser)\nif (typeof document !== 'undefined') {\n // Defer injection to avoid blocking\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', injectCSSVariables);\n } else {\n injectCSSVariables();\n }\n}\n","\"use client\"\n\nimport { useEffect, useRef, useMemo, useLayoutEffect } from \"react\"\n\nexport interface FormatOptions {\n style?: \"decimal\" | \"currency\" | \"percent\"\n currency?: string\n locale?: string\n minimumFractionDigits?: number\n maximumFractionDigits?: number\n useGrouping?: boolean\n}\n\nexport interface SlidingNumberProps {\n value: number\n duration?: number\n fontSize?: string\n fontWeight?: string\n color?: string\n digitHeight?: number\n stagger?: number\n motionBlur?: boolean\n format?: FormatOptions\n trend?: -1 | 0 | 1\n animationConfig?: {\n overshoot?: number\n mass?: number\n stiffness?: number\n }\n}\n\ninterface CharItem {\n char: string\n key: string\n isDigit: boolean\n position: number\n}\n\nfunction parseNumberToChars(value: number, format?: FormatOptions): CharItem[] {\n const locale = format?.locale || \"en-US\"\n const options: Intl.NumberFormatOptions = {\n style: format?.style || \"decimal\",\n minimumFractionDigits: format?.minimumFractionDigits,\n maximumFractionDigits: format?.maximumFractionDigits,\n useGrouping: format?.useGrouping ?? true,\n }\n\n if (format?.style === \"currency\" && format?.currency) {\n options.currency = format.currency\n }\n\n const formatter = new Intl.NumberFormat(locale, options)\n const parts = formatter.formatToParts(value)\n\n const result: CharItem[] = []\n\n let integerDigitCount = 0\n parts.forEach((part) => {\n if (part.type === \"integer\") {\n integerDigitCount += part.value.length\n }\n })\n\n let integerIndex = integerDigitCount\n let fractionIndex = 0\n let groupCount = 0\n\n parts.forEach((part, partIdx) => {\n if (part.type === \"integer\") {\n for (const char of part.value) {\n result.push({\n char,\n key: `int-${integerIndex}`,\n isDigit: true,\n position: integerIndex,\n })\n integerIndex--\n }\n } else if (part.type === \"fraction\") {\n for (const char of part.value) {\n fractionIndex++\n result.push({\n char,\n key: `frac-${fractionIndex}`,\n isDigit: true,\n position: -fractionIndex,\n })\n }\n } else if (part.type === \"decimal\") {\n result.push({ char: part.value, key: \"decimal\", isDigit: false, position: 0 })\n } else if (part.type === \"group\") {\n groupCount++\n result.push({ char: part.value, key: `group-${groupCount}`, isDigit: false, position: 0 })\n } else if (part.type === \"currency\") {\n result.push({ char: part.value, key: `currency-${partIdx}`, isDigit: false, position: 0 })\n } else if (part.type === \"percentSign\") {\n result.push({ char: part.value, key: \"percent\", isDigit: false, position: 0 })\n } else {\n result.push({ char: part.value, key: `symbol-${partIdx}`, isDigit: false, position: 0 })\n }\n })\n\n return result\n}\n\nexport function SlidingNumber({\n value,\n duration = 700,\n fontSize = \"3rem\",\n fontWeight = \"700\",\n color = \"#000\",\n digitHeight = 60,\n stagger = 30,\n motionBlur = true,\n format,\n trend = 0,\n animationConfig,\n}: SlidingNumberProps) {\n const computedEasing = useMemo(() => {\n if (animationConfig) {\n const { overshoot = 1, stiffness = 1, mass = 1 } = animationConfig\n const p1 = 0.34 * stiffness\n const p2 = 1 + 0.56 * overshoot\n const p3 = 0.64 / mass\n return `cubic-bezier(${Math.min(p1, 1).toFixed(2)}, ${Math.min(p2, 2).toFixed(2)}, ${Math.min(p3, 1).toFixed(2)}, 1)`\n }\n return \"cubic-bezier(0.34, 1.56, 0.64, 1)\"\n }, [animationConfig])\n\n const currentChars = useMemo(() => parseNumberToChars(value, format), [value, format])\n const prevKeysRef = useRef<Set<string>>(new Set())\n const isFirstRenderRef = useRef(true)\n\n const enteringKeys = useMemo(() => {\n if (isFirstRenderRef.current) return new Set<string>()\n const entering = new Set<string>()\n currentChars.forEach((c) => {\n if (!prevKeysRef.current.has(c.key)) {\n entering.add(c.key)\n }\n })\n return entering\n }, [currentChars])\n\n useEffect(() => {\n isFirstRenderRef.current = false\n prevKeysRef.current = new Set(currentChars.map((c) => c.key))\n }, [currentChars])\n\n const getDelay = (position: number) => {\n const absPos = Math.abs(position)\n return (absPos - 1) * stagger\n }\n\n return (\n <div\n style={{\n display: \"inline-flex\",\n alignItems: \"center\",\n fontSize,\n fontWeight,\n color,\n fontVariantNumeric: \"tabular-nums\",\n fontFamily: \"system-ui, -apple-system, sans-serif\",\n overflow: \"hidden\",\n }}\n >\n {currentChars.map((item) => {\n const isEntering = enteringKeys.has(item.key)\n\n if (item.isDigit) {\n return (\n <Digit\n key={item.key}\n digit={Number.parseInt(item.char)}\n duration={duration}\n digitHeight={digitHeight}\n delay={getDelay(item.position)}\n motionBlur={motionBlur}\n easing={computedEasing}\n isEntering={isEntering}\n trend={trend}\n />\n )\n }\n\n return (\n <Symbol key={item.key} char={item.char} isEntering={isEntering} duration={duration} easing={computedEasing} />\n )\n })}\n </div>\n )\n}\n\nfunction Symbol({\n char,\n isEntering,\n duration,\n easing,\n}: { char: string; isEntering: boolean; duration: number; easing: string }) {\n const ref = useRef<HTMLSpanElement>(null)\n const hasAnimatedRef = useRef(false)\n\n useLayoutEffect(() => {\n if (!ref.current || !isEntering || hasAnimatedRef.current) return\n hasAnimatedRef.current = true\n\n ref.current.animate(\n [\n { opacity: 0, transform: \"scale(0.8)\" },\n { opacity: 1, transform: \"scale(1)\" },\n ],\n {\n duration: duration * 0.3,\n easing,\n fill: \"forwards\",\n },\n )\n }, [isEntering, duration, easing])\n\n return (\n <span\n ref={ref}\n style={{\n display: \"inline-block\",\n whiteSpace: \"pre\",\n opacity: isEntering ? 0 : 1,\n }}\n >\n {char}\n </span>\n )\n}\n\ninterface DigitProps {\n digit: number\n duration: number\n digitHeight: number\n delay: number\n motionBlur: boolean\n easing: string\n isEntering: boolean\n trend: -1 | 0 | 1\n}\n\nfunction Digit({ digit, duration, digitHeight, delay, motionBlur, easing, isEntering, trend }: DigitProps) {\n const columnRef = useRef<HTMLDivElement>(null)\n const containerRef = useRef<HTMLDivElement>(null)\n const animationRef = useRef<Animation | null>(null)\n const filterId = useRef(`blur-${Math.random().toString(36).slice(2, 9)}`).current\n\n const currentOffsetRef = useRef<number | null>(null)\n const targetDigitRef = useRef(digit)\n const hasInitializedRef = useRef(false)\n\n const sequence = useMemo(() => {\n const seq: number[] = []\n for (let cycle = -1; cycle <= 1; cycle++) {\n for (let d = 0; d <= 9; d++) {\n seq.push(d)\n }\n }\n return seq\n }, [])\n\n useLayoutEffect(() => {\n if (!hasInitializedRef.current) {\n hasInitializedRef.current = true\n const offset = -(digit + 10) * digitHeight\n currentOffsetRef.current = offset\n targetDigitRef.current = digit\n\n if (columnRef.current) {\n columnRef.current.style.transform = `translateY(${offset}px)`\n }\n }\n }, [digit, digitHeight])\n\n useLayoutEffect(() => {\n if (!containerRef.current || !isEntering) return\n\n containerRef.current.animate(\n [\n { opacity: 0, transform: \"scale(0.5) translateY(-20px)\" },\n { opacity: 1, transform: \"scale(1) translateY(0)\" },\n ],\n {\n duration: duration * 0.4,\n easing,\n fill: \"forwards\",\n },\n )\n }, [isEntering, duration, easing])\n\n useEffect(() => {\n targetDigitRef.current = digit\n\n if (!hasInitializedRef.current) return\n\n const currentDigit =\n currentOffsetRef.current !== null\n ? (((Math.round(-currentOffsetRef.current / digitHeight) - 10) % 10) + 10) % 10\n : digit\n\n if (digit === currentDigit && currentOffsetRef.current !== null) {\n return\n }\n\n animateToDigit(digit)\n }, [digit, digitHeight])\n\n const animateToDigit = (target: number) => {\n if (!columnRef.current || !containerRef.current) return\n\n if (animationRef.current) {\n const computedStyle = getComputedStyle(columnRef.current)\n const matrix = new DOMMatrix(computedStyle.transform)\n currentOffsetRef.current = matrix.m42\n animationRef.current.cancel()\n animationRef.current = null\n columnRef.current.style.transform = `translateY(${currentOffsetRef.current}px)`\n }\n\n const rawIndex = currentOffsetRef.current !== null ? -currentOffsetRef.current / digitHeight : target + 10\n\n const currentDigitFromOffset = (((Math.round(rawIndex) - 10) % 10) + 10) % 10\n const from = currentDigitFromOffset\n\n if (target === from && currentOffsetRef.current !== null) {\n const normalizedOffset = -(target + 10) * digitHeight\n if (columnRef.current) {\n const currentAnim = animationRef.current as Animation | null\n if (currentAnim) {\n currentAnim.cancel()\n }\n columnRef.current.style.transform = `translateY(${normalizedOffset}px)`\n }\n currentOffsetRef.current = normalizedOffset\n return\n }\n\n let diff: number\n if (trend === 1) {\n diff = target >= from ? target - from : 10 - from + target\n } else if (trend === -1) {\n diff = target <= from ? target - from : target - from - 10\n } else {\n diff = target - from\n if (diff > 5) diff -= 10\n else if (diff < -5) diff += 10\n }\n\n const startOffset = currentOffsetRef.current ?? -(from + 10) * digitHeight\n const endOffset = startOffset - diff * digitHeight\n\n const blurEl = document.getElementById(filterId)?.querySelector(\"feGaussianBlur\")\n if (motionBlur && blurEl) {\n const intensity = Math.min(Math.abs(diff) * 1.2, 6)\n blurEl.setAttribute(\"stdDeviation\", `0,${intensity}`)\n containerRef.current.style.filter = `url(#${filterId})`\n }\n\n const anim = columnRef.current.animate(\n [{ transform: `translateY(${startOffset}px)` }, { transform: `translateY(${endOffset}px)` }],\n {\n duration,\n delay,\n easing,\n fill: \"forwards\",\n },\n )\n\n animationRef.current = anim\n\n anim.onfinish = () => {\n const normalizedOffset = -(target + 10) * digitHeight\n if (columnRef.current) {\n anim.cancel()\n columnRef.current.style.transform = `translateY(${normalizedOffset}px)`\n }\n currentOffsetRef.current = normalizedOffset\n\n if (containerRef.current) {\n containerRef.current.style.filter = \"none\"\n }\n if (blurEl) {\n blurEl.setAttribute(\"stdDeviation\", \"0,0\")\n }\n\n animationRef.current = null\n\n if (targetDigitRef.current !== target) {\n requestAnimationFrame(() => {\n animateToDigit(targetDigitRef.current)\n })\n }\n }\n\n anim.oncancel = () => {\n animationRef.current = null\n }\n }\n\n return (\n <>\n <svg style={{ position: \"absolute\", width: 0, height: 0 }} aria-hidden=\"true\">\n <defs>\n <filter id={filterId}>\n <feGaussianBlur in=\"SourceGraphic\" stdDeviation=\"0,0\" />\n </filter>\n </defs>\n </svg>\n\n <div\n ref={containerRef}\n style={{\n position: \"relative\",\n height: `${digitHeight}px`,\n overflow: \"hidden\",\n width: \"0.65em\",\n textAlign: \"center\",\n opacity: isEntering ? 0 : 1,\n }}\n >\n <div\n ref={columnRef}\n style={{\n position: \"absolute\",\n left: 0,\n right: 0,\n willChange: \"transform\",\n }}\n >\n {sequence.map((d, i) => (\n <div\n key={i}\n style={{\n height: `${digitHeight}px`,\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n }}\n >\n {d}\n </div>\n ))}\n </div>\n </div>\n </>\n )\n}\n","import React, { createContext, useContext, useState, useCallback, useRef, useMemo, useEffect } from 'react';\n\n// ============================================\n// ENHANCED DEBUG EVENT TYPES\n// ============================================\n\n// Timing data for measuring actual vs expected animation duration\nexport interface TimingData {\n startTime: number;\n endTime?: number;\n expectedDuration: number;\n actualDuration?: number;\n deviation?: number;\n deviationPercent?: number;\n}\n\n// Style comparison data\nexport interface StyleData {\n property: string;\n expected: string;\n actual: string;\n matches: boolean;\n}\n\n// Position data with full DOMRect info\nexport interface PositionData {\n element: string;\n x: number;\n y: number;\n width: number;\n height: number;\n delta?: { x: number; y: number };\n}\n\n// Animation metadata\nexport interface AnimationData {\n name: string;\n phase: 'start' | 'running' | 'complete' | 'cancelled';\n progress?: number;\n easing?: string;\n fill?: string;\n}\n\n// All event types\nexport type DebugEventType =\n // Token events\n | 'token-add'\n | 'token-remove'\n | 'token-reorder'\n | 'token-exit-start'\n | 'token-exit-complete'\n | 'token-dom-remove'\n | 'overflow-token-remove'\n // FLIP events\n | 'flip-animation'\n | 'flip-animation-complete'\n | 'flip-all-animations-complete'\n | 'flip-executing-callback'\n | 'flip-measure-start'\n | 'flip-position-measured'\n | 'flip-invalid-rect'\n | 'flip-invalid-delta'\n | 'flip-manual-trigger'\n | 'flip-capture-positions'\n | 'flip-position-captured'\n | 'flip-skipped'\n // WAAPI events\n | 'waapi-exit-start'\n | 'waapi-exit-complete'\n | 'waapi-flip-animation'\n // Separated timing events\n | 'exit-fade-complete'\n | 'orchestration-complete'\n // Enhanced quantitative events\n | 'animation-start-detailed'\n | 'animation-complete-detailed'\n | 'animation-timing'\n | 'style-capture'\n | 'style-mismatch'\n | 'position-capture'\n | 'position-delta'\n | 'choreography-overlap'\n | 'choreography-sequence'\n // Enter animation events\n | 'text-enter-start'\n | 'text-enter-complete'\n // Reorder events\n | 'reorder-item-enter'\n | 'reorder-item-exit'\n | 'reorder-item-exit-complete'\n | 'reorder-flip-start'\n | 'reorder-flip-complete'\n | 'reorder-flip-before-capture'\n | 'reorder-flip-after-capture'\n | 'reorder-flip-after-positions'\n | 'reorder-flip-after-precaptured'\n | 'reorder-flip-delta'\n | 'reorder-flip-reflow-trigger'\n | 'reorder-flip-skipped'\n | 'reorder-flip-filter'\n | 'reorder-stagger-start'\n | 'reorder-ghost-created'\n | 'reorder-ghost-removed'\n | 'reorder-register-element'\n | 'reorder-animation-start'\n | 'reorder-animation-complete'\n | 'reorder-animation-setup'\n | 'reorder-animation-created'\n | 'reorder-animation-init'\n | 'reorder-animation-error'\n | 'reorder-exit-check'\n | 'reorder-exit-rejected'\n | 'reorder-reflow-triggered'\n | 'reorder-transform-applied'\n | 'reorder-display-none'\n | 'reorder-position-absolute'\n | 'reorder-after-immediate'\n | 'reorder-exit-cleanup'\n | 'reorder-await-start'\n | 'reorder-await-complete'\n | 'reorder-enter-start'\n | 'reorder-enter-complete'\n // Separator events\n | 'separator-animation-start'\n | 'separator-animation-state-only'\n | 'separator-animation-skipped'\n | 'separator-flip-start'\n | 'separator-animation-complete'\n | 'separator-state-cleanup'\n // Test events\n | 'test-suite-start'\n | 'test-suite-complete'\n | 'test-suite-error'\n | 'test-suite-stopped'\n | 'test-start'\n | 'test-complete'\n | 'test-error'\n | 'test-progress'\n | 'test-flip-capture'\n | 'test-export'\n | 'test-manual-remove'\n // Legacy events\n | 'triggering-flip-before-absolute'\n | 'state-change'\n | 'render'\n | 'animation-complete-called'\n | 'scheduling-raf'\n | 'raf-executed'\n | 'component-unmounted'\n | 'token-removing-from-layout'\n | 'exit-completed-ids-updated'\n | 'exit-completed-change'\n | 'registering-callback'\n | 'callback-fired';\n\nexport interface DebugEvent {\n timestamp: number;\n perfNow?: number; // High-precision timing via performance.now()\n type: DebugEventType;\n source: string;\n message: string;\n\n // Enhanced quantitative data (optional)\n timing?: TimingData;\n styles?: StyleData[];\n position?: PositionData;\n animation?: AnimationData;\n\n // Generic data field (existing)\n data?: Record<string, unknown>;\n}\n\ninterface DebugContextValue {\n events: DebugEvent[];\n isEnabled: boolean;\n enableDebug: () => void;\n disableDebug: () => void;\n toggleDebug: () => void;\n logEvent: (event: Omit<DebugEvent, 'timestamp' | 'perfNow'>) => void;\n clearEvents: () => void;\n getEventLog: () => string;\n exportToCSV: () => string;\n // P3: Collect mode for tests\n startCollecting: () => void;\n stopCollecting: () => DebugEvent[];\n isCollecting: boolean;\n // Utility: flush pending events immediately\n flushEvents: () => void;\n}\n\nconst DebugContext = createContext<DebugContextValue | null>(null);\n\nexport const useDebug = () => {\n const context = useContext(DebugContext);\n if (!context) {\n throw new Error('useDebug must be used within DebugProvider');\n }\n return context;\n};\n\nexport const DebugProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {\n const [events, setEvents] = useState<DebugEvent[]>([]);\n const [isEnabled, setIsEnabled] = useState(true);\n const [isCollecting, setIsCollecting] = useState(false);\n const isEnabledRef = useRef(isEnabled);\n\n // RAF micro-batching refs\n const bufferRef = useRef<DebugEvent[]>([]);\n const rafIdRef = useRef<number | null>(null);\n\n // Collect mode ref for tests\n const collectedEventsRef = useRef<DebugEvent[]>([]);\n const isCollectingRef = useRef(false);\n\n // Keep refs in sync with state\n isEnabledRef.current = isEnabled;\n isCollectingRef.current = isCollecting;\n\n // Flush buffered events to state (called by RAF or manually)\n const flushBuffer = useCallback(() => {\n if (bufferRef.current.length === 0) return;\n\n const eventsToFlush = [...bufferRef.current];\n bufferRef.current = [];\n rafIdRef.current = null;\n\n setEvents(prev => [...prev, ...eventsToFlush]);\n }, []);\n\n // Cleanup RAF on unmount\n useEffect(() => {\n return () => {\n if (rafIdRef.current !== null) {\n cancelAnimationFrame(rafIdRef.current);\n // Flush remaining events on unmount\n if (bufferRef.current.length > 0) {\n setEvents(prev => [...prev, ...bufferRef.current]);\n bufferRef.current = [];\n }\n }\n };\n }, []);\n\n const logEvent = useCallback((event: Omit<DebugEvent, 'timestamp' | 'perfNow'>) => {\n if (!__DEBUG__) return; // Guard: skip entirely in production\n\n // Use ref to check if enabled to avoid dependency on isEnabled state\n if (!isEnabledRef.current) return;\n\n const fullEvent: DebugEvent = {\n ...event,\n timestamp: Date.now(),\n perfNow: performance.now(), // High-precision timing\n };\n\n // If in collect mode, also save to collected events\n if (isCollectingRef.current) {\n collectedEventsRef.current.push(fullEvent);\n }\n\n // Buffer the event instead of setting state immediately\n bufferRef.current.push(fullEvent);\n\n // Schedule flush on next animation frame (if not already scheduled)\n if (rafIdRef.current === null) {\n rafIdRef.current = requestAnimationFrame(flushBuffer);\n }\n\n // Console logging with color coding (immediate, outside of React state)\n const colors: Record<string, string> = {\n // Token events\n 'token-add': '#22c55e',\n 'token-remove': '#ef4444',\n 'token-reorder': '#f59e0b',\n 'token-exit-start': '#f97316',\n 'token-exit-complete': '#8b5cf6',\n 'token-dom-remove': '#dc2626',\n // FLIP events\n 'flip-animation': '#3b82f6',\n 'flip-animation-complete': '#06b6d4',\n 'flip-all-animations-complete': '#10b981',\n 'flip-executing-callback': '#14b8a6',\n 'flip-measure-start': '#0ea5e9',\n 'flip-position-measured': '#3b82f6',\n 'flip-invalid-rect': '#f59e0b',\n 'flip-invalid-delta': '#ef4444',\n 'flip-manual-trigger': '#06b6d4',\n 'flip-capture-positions': '#8b5cf6',\n 'flip-position-captured': '#a78bfa',\n // WAAPI events\n 'waapi-exit-start': '#f97316',\n 'waapi-exit-complete': '#22c55e',\n 'waapi-flip-animation': '#3b82f6',\n // Separated timing events\n 'exit-fade-complete': '#f97316',\n 'orchestration-complete': '#10b981',\n // Enhanced quantitative events\n 'animation-start-detailed': '#f97316',\n 'animation-complete-detailed': '#22c55e',\n 'animation-timing': '#06b6d4',\n 'style-capture': '#8b5cf6',\n 'style-mismatch': '#ef4444',\n 'position-capture': '#3b82f6',\n 'position-delta': '#0ea5e9',\n 'choreography-overlap': '#f59e0b',\n 'choreography-sequence': '#14b8a6',\n // Enter animation events\n 'text-enter-start': '#22c55e',\n 'text-enter-complete': '#10b981',\n // Reorder events\n 'reorder-item-enter': '#22c55e',\n 'reorder-item-exit': '#ef4444',\n 'reorder-item-exit-complete': '#f97316',\n 'reorder-flip-start': '#3b82f6',\n 'reorder-flip-complete': '#06b6d4',\n 'reorder-flip-before-capture': '#8b5cf6',\n 'reorder-flip-after-capture': '#a78bfa',\n 'reorder-flip-delta': '#c4b5fd',\n 'reorder-flip-reflow-trigger': '#f59e0b',\n 'reorder-flip-skipped': '#ef4444',\n 'reorder-flip-filter': '#a855f7',\n 'reorder-stagger-start': '#a855f7',\n 'reorder-ghost-created': '#f59e0b',\n 'reorder-ghost-removed': '#dc2626',\n 'reorder-register-element': '#6b7280',\n 'reorder-animation-start': '#f97316',\n 'reorder-animation-complete': '#10b981',\n 'reorder-animation-setup': '#f59e0b',\n 'reorder-animation-created': '#3b82f6',\n 'reorder-animation-init': '#3b82f6',\n 'reorder-animation-error': '#ef4444',\n 'reorder-exit-check': '#6b7280',\n 'reorder-exit-rejected': '#f59e0b',\n 'reorder-exit-cleanup': '#22c55e',\n 'reorder-reflow-triggered': '#06b6d4',\n 'reorder-transform-applied': '#8b5cf6',\n 'reorder-display-none': '#f97316',\n 'reorder-position-absolute': '#fb923c',\n 'reorder-after-immediate': '#a78bfa',\n 'reorder-flip-after-positions': '#c4b5fd',\n 'reorder-flip-after-precaptured': '#c4b5fd',\n 'reorder-await-start': '#8b5cf6',\n 'reorder-await-complete': '#a78bfa',\n 'reorder-enter-start': '#22c55e',\n 'reorder-enter-complete': '#10b981',\n // Test events\n 'test-suite-start': '#8b5cf6',\n 'test-suite-complete': '#10b981',\n 'test-suite-error': '#ef4444',\n 'test-suite-stopped': '#f59e0b',\n 'test-start': '#3b82f6',\n 'test-complete': '#22c55e',\n 'test-error': '#ef4444',\n 'test-progress': '#06b6d4',\n 'test-flip-capture': '#a78bfa',\n 'test-export': '#14b8a6',\n 'test-manual-remove': '#f97316',\n // Legacy events\n 'triggering-flip-before-absolute': '#f97316',\n 'state-change': '#06b6d4',\n 'render': '#6b7280',\n 'animation-complete-called': '#8b5cf6',\n 'scheduling-raf': '#a855f7',\n 'raf-executed': '#c084fc',\n 'component-unmounted': '#ef4444',\n 'token-removing-from-layout': '#f97316',\n 'exit-completed-ids-updated': '#fb923c',\n 'exit-completed-change': '#f59e0b',\n 'registering-callback': '#34d399',\n 'callback-fired': '#10b981',\n };\n\n console.log(\n `%c[${event.type}] %c${event.source}%c: ${event.message}`,\n `color: ${colors[event.type] || '#6b7280'}; font-weight: bold`,\n 'color: #9ca3af',\n 'color: inherit',\n event.data || ''\n );\n }, [flushBuffer]); // Depends on flushBuffer for RAF scheduling\n\n const clearEvents = useCallback(() => {\n if (!__DEBUG__) return;\n setEvents([]);\n }, []);\n\n const enableDebug = useCallback(() => {\n if (!__DEBUG__) return;\n setIsEnabled(true);\n }, []);\n\n const disableDebug = useCallback(() => {\n if (!__DEBUG__) return;\n setIsEnabled(false);\n }, []);\n\n const toggleDebug = useCallback(() => {\n if (!__DEBUG__) return;\n setIsEnabled(prev => !prev);\n }, []);\n\n // P3: Collect mode for tests - start collecting events\n const startCollecting = useCallback(() => {\n if (!__DEBUG__) return;\n collectedEventsRef.current = [];\n setIsCollecting(true);\n }, []);\n\n // P3: Collect mode for tests - stop and return collected events\n const stopCollecting = useCallback((): DebugEvent[] => {\n if (!__DEBUG__) return [];\n setIsCollecting(false);\n const collected = [...collectedEventsRef.current];\n collectedEventsRef.current = [];\n return collected;\n }, []);\n\n // Utility: flush pending buffered events immediately\n const flushEvents = useCallback(() => {\n if (!__DEBUG__) return;\n if (rafIdRef.current !== null) {\n cancelAnimationFrame(rafIdRef.current);\n rafIdRef.current = null;\n }\n flushBuffer();\n }, [flushBuffer]);\n\n const exportToCSV = useCallback(() => {\n if (!__DEBUG__) return '';\n\n // CSV Header - includes all quantitative data + perfNow\n const headers = [\n 'Index', 'Timestamp', 'PerfNow', 'Time', 'Type', 'Source', 'Message',\n 'Expected_Duration', 'Actual_Duration', 'Deviation', 'Deviation_Percent',\n 'Animation_Name', 'Animation_Phase', 'Animation_Easing',\n 'Element', 'Pos_X', 'Pos_Y', 'Width', 'Height', 'Delta_X', 'Delta_Y',\n 'Style_Checks', 'Style_Matches', 'Style_Mismatches',\n 'Data'\n ];\n\n // Sort events by timestamp before processing\n const sortedEvents = [...events].sort((a, b) => a.timestamp - b.timestamp);\n\n // CSV Rows - includes all captured data\n const rows = sortedEvents.map((e, i) => {\n const time = new Date(e.timestamp).toLocaleTimeString('en-US', {\n hour12: false,\n hour: '2-digit',\n minute: '2-digit',\n second: '2-digit'\n });\n\n // Flatten data object into key=value pairs\n const dataStr = e.data\n ? Object.entries(e.data)\n .map(([key, val]) => {\n if (Array.isArray(val)) {\n return `${key}=[${val.join(',')}]`;\n } else if (typeof val === 'object' && val !== null) {\n return `${key}=${JSON.stringify(val)}`;\n }\n return `${key}=${val}`;\n })\n .join(';')\n : '';\n\n // Style analysis\n const styleMatches = e.styles?.filter(s => s.matches).length ?? '';\n const styleMismatches = e.styles?.filter(s => !s.matches).length ?? '';\n\n return [\n i + 1,\n e.timestamp,\n e.perfNow?.toFixed(3) ?? '',\n time,\n e.type,\n e.source,\n e.message,\n e.timing?.expectedDuration ?? '',\n e.timing?.actualDuration?.toFixed(2) ?? '',\n e.timing?.deviation?.toFixed(2) ?? '',\n e.timing?.deviationPercent?.toFixed(2) ?? '',\n e.animation?.name ?? '',\n e.animation?.phase ?? '',\n e.animation?.easing ?? '',\n e.position?.element ?? '',\n e.position?.x?.toFixed(2) ?? '',\n e.position?.y?.toFixed(2) ?? '',\n e.position?.width?.toFixed(2) ?? '',\n e.position?.height?.toFixed(2) ?? '',\n e.position?.delta?.x?.toFixed(2) ?? '',\n e.position?.delta?.y?.toFixed(2) ?? '',\n e.styles?.length ?? '',\n styleMatches,\n styleMismatches,\n dataStr\n ];\n });\n\n // Convert to CSV format\n const csvContent = [\n headers.join(','),\n ...rows.map(row =>\n row.map(cell => {\n const cellStr = String(cell);\n if (cellStr.includes(',') || cellStr.includes('\"') || cellStr.includes('\\n')) {\n return `\"${cellStr.replace(/\"/g, '\"\"')}\"`;\n }\n return cellStr;\n }).join(',')\n )\n ].join('\\n');\n\n return csvContent;\n }, [events]);\n\n const getEventLog = useCallback(() => {\n if (!__DEBUG__) return '';\n\n return events.map((e, i) => {\n const time = new Date(e.timestamp).toLocaleTimeString();\n const parts: string[] = [\n `[${i + 1}] ${time} [${e.type}] ${e.source}: ${e.message}`\n ];\n\n if (e.timing) {\n const t = e.timing;\n parts.push(` Timing: ${t.expectedDuration}ms expected → ${t.actualDuration?.toFixed(1) ?? '?'}ms actual (${t.deviation && t.deviation >= 0 ? '+' : ''}${t.deviation?.toFixed(1) ?? '?'}ms)`);\n }\n\n if (e.animation) {\n parts.push(` Animation: ${e.animation.name} [${e.animation.phase}] easing=${e.animation.easing || 'default'}`);\n }\n\n if (e.position) {\n const p = e.position;\n let posStr = ` Position: ${p.element} @ (${p.x.toFixed(0)}, ${p.y.toFixed(0)}) ${p.width.toFixed(0)}x${p.height.toFixed(0)}`;\n if (p.delta) {\n posStr += ` Δ(${p.delta.x.toFixed(1)}, ${p.delta.y.toFixed(1)})`;\n }\n parts.push(posStr);\n }\n\n if (e.styles && e.styles.length > 0) {\n const matches = e.styles.filter(s => s.matches).length;\n const mismatches = e.styles.filter(s => !s.matches).length;\n parts.push(` Styles: ${e.styles.length} checks (${matches} match, ${mismatches} mismatch)`);\n e.styles.filter(s => !s.matches).forEach(s => {\n parts.push(` ✗ ${s.property}: expected \"${s.expected}\" got \"${s.actual}\"`);\n });\n }\n\n if (e.data && Object.keys(e.data).length > 0) {\n parts.push(` Data: ${JSON.stringify(e.data, null, 2).split('\\n').join('\\n ')}`);\n }\n\n return parts.join('\\n');\n }).join('\\n\\n');\n }, [events]);\n\n const contextValue = useMemo(() => ({\n events: __DEBUG__ ? events : [],\n isEnabled: __DEBUG__ ? isEnabled : false,\n isCollecting: __DEBUG__ ? isCollecting : false,\n enableDebug,\n disableDebug,\n toggleDebug,\n logEvent,\n clearEvents,\n getEventLog,\n exportToCSV,\n startCollecting,\n stopCollecting,\n flushEvents,\n }), [events, isEnabled, isCollecting, enableDebug, disableDebug, toggleDebug, logEvent, clearEvents, getEventLog, exportToCSV, startCollecting, stopCollecting, flushEvents]);\n\n return (\n <DebugContext.Provider value={contextValue}>\n {children}\n </DebugContext.Provider>\n );\n};\n","// ============================================\n// DEBUG CAPTURE UTILITIES\n// Functions for capturing quantitative animation data\n// ============================================\n\n// Types\nexport interface StyleCapture {\n opacity: string;\n transform: string;\n filter: string;\n width: string;\n height: string;\n marginRight: string;\n marginLeft: string;\n position: string;\n visibility: string;\n pointerEvents: string;\n}\n\nexport interface PositionCapture {\n x: number;\n y: number;\n width: number;\n height: number;\n top: number;\n left: number;\n right: number;\n bottom: number;\n}\n\nexport interface StyleComparison {\n property: string;\n expected: string;\n actual: string;\n matches: boolean;\n}\n\nexport interface TimingResult {\n startTime: number;\n endTime: number;\n expectedDuration: number;\n actualDuration: number;\n deviation: number;\n deviationPercent: number;\n}\n\nexport interface AnimationTimer {\n start: number;\n expectedDuration: number;\n end: () => TimingResult;\n}\n\n// ============================================\n// STYLE CAPTURE\n// ============================================\n\n/**\n * Capture relevant computed styles from an element\n * These are the styles we animate and need to verify\n */\nexport function captureComputedStyles(el: HTMLElement): StyleCapture {\n const computed = getComputedStyle(el);\n return {\n opacity: computed.opacity,\n transform: computed.transform,\n filter: computed.filter,\n width: computed.width,\n height: computed.height,\n marginRight: computed.marginRight,\n marginLeft: computed.marginLeft,\n position: computed.position,\n visibility: computed.visibility,\n pointerEvents: computed.pointerEvents,\n };\n}\n\n/**\n * Capture styles as a simple key-value object for logging\n */\nexport function captureStylesForLog(el: HTMLElement): Record<string, string> {\n const styles = captureComputedStyles(el);\n return {\n opacity: styles.opacity,\n transform: styles.transform === 'none' ? 'none' : styles.transform,\n filter: styles.filter === 'none' ? 'none' : styles.filter,\n width: styles.width,\n marginRight: styles.marginRight,\n position: styles.position,\n };\n}\n\n// ============================================\n// POSITION CAPTURE\n// ============================================\n\n/**\n * Capture complete position information from DOMRect\n */\nexport function capturePosition(el: HTMLElement): PositionCapture {\n const rect = el.getBoundingClientRect();\n return {\n x: Math.round(rect.x * 100) / 100,\n y: Math.round(rect.y * 100) / 100,\n width: Math.round(rect.width * 100) / 100,\n height: Math.round(rect.height * 100) / 100,\n top: Math.round(rect.top * 100) / 100,\n left: Math.round(rect.left * 100) / 100,\n right: Math.round(rect.right * 100) / 100,\n bottom: Math.round(rect.bottom * 100) / 100,\n };\n}\n\n/**\n * Capture position as simple object for logging\n */\nexport function capturePositionForLog(el: HTMLElement): Record<string, number> {\n const pos = capturePosition(el);\n return {\n x: pos.x,\n y: pos.y,\n w: pos.width,\n h: pos.height,\n };\n}\n\n/**\n * Calculate delta between two positions\n */\nexport function calculatePositionDelta(\n before: PositionCapture,\n after: PositionCapture\n): { deltaX: number; deltaY: number; deltaWidth: number; deltaHeight: number } {\n return {\n deltaX: Math.round((before.left - after.left) * 100) / 100,\n deltaY: Math.round((before.top - after.top) * 100) / 100,\n deltaWidth: Math.round((before.width - after.width) * 100) / 100,\n deltaHeight: Math.round((before.height - after.height) * 100) / 100,\n };\n}\n\n// ============================================\n// STYLE COMPARISON\n// ============================================\n\n/**\n * Compare expected styles vs actual computed styles\n * Used to verify animations applied correctly\n */\nexport function compareStyles(\n expected: Record<string, string>,\n el: HTMLElement\n): StyleComparison[] {\n const actual = captureComputedStyles(el);\n\n return Object.entries(expected).map(([prop, expectedVal]) => {\n const actualVal = actual[prop as keyof StyleCapture] || '';\n\n // Normalize values for comparison\n const normalizedExpected = normalizeStyleValue(prop, expectedVal);\n const normalizedActual = normalizeStyleValue(prop, actualVal);\n\n return {\n property: prop,\n expected: expectedVal,\n actual: actualVal,\n matches: normalizedExpected === normalizedActual,\n };\n });\n}\n\n/**\n * Normalize style values for comparison\n * Handles browser quirks and formatting differences\n */\nfunction normalizeStyleValue(property: string, value: string): string {\n if (!value) return '';\n\n // Normalize opacity (some browsers return \"1\" vs \"1.0\")\n if (property === 'opacity') {\n return parseFloat(value).toString();\n }\n\n // Normalize transform (handle matrix vs translate)\n if (property === 'transform') {\n if (value === 'none') return 'none';\n // Extract translation values if present\n const match = value.match(/matrix\\(([^)]+)\\)/);\n if (match && match[1]) {\n const values = match[1].split(',').map(v => parseFloat(v.trim()));\n // matrix(a, b, c, d, tx, ty) - tx is values[4], ty is values[5]\n const tx = values[4];\n const ty = values[5];\n if (tx !== undefined && ty !== undefined) {\n return `translate(${Math.round(tx)}px, ${Math.round(ty)}px)`;\n }\n }\n return value;\n }\n\n // Normalize width/height (round to nearest pixel)\n if (property === 'width' || property === 'height' || property.includes('margin')) {\n const num = parseFloat(value);\n if (!isNaN(num)) {\n return `${Math.round(num)}px`;\n }\n }\n\n // Normalize filter blur\n if (property === 'filter') {\n if (value === 'none') return 'none';\n const blurMatch = value.match(/blur\\(([^)]+)\\)/);\n if (blurMatch && blurMatch[1]) {\n const blurValue = parseFloat(blurMatch[1]);\n return `blur(${Math.round(blurValue)}px)`;\n }\n }\n\n return value;\n}\n\n// ============================================\n// TIMING\n// ============================================\n\n/**\n * Create a timer for measuring actual animation duration\n * Uses performance.now() for high precision\n */\nexport function createAnimationTimer(expectedDuration: number): AnimationTimer {\n const startTime = performance.now();\n\n return {\n start: startTime,\n expectedDuration,\n end: () => {\n const endTime = performance.now();\n const actualDuration = endTime - startTime;\n const deviation = actualDuration - expectedDuration;\n\n return {\n startTime: Math.round(startTime * 100) / 100,\n endTime: Math.round(endTime * 100) / 100,\n expectedDuration,\n actualDuration: Math.round(actualDuration * 100) / 100,\n deviation: Math.round(deviation * 100) / 100,\n deviationPercent: Math.round((deviation / expectedDuration) * 10000) / 100,\n };\n },\n };\n}\n\n/**\n * Format timing result for display\n */\nexport function formatTimingResult(timing: TimingResult): string {\n const sign = timing.deviation >= 0 ? '+' : '';\n const status = Math.abs(timing.deviation) < 10 ? '✅' : '⚠️';\n return `Expected: ${timing.expectedDuration}ms | Actual: ${timing.actualDuration}ms | Deviation: ${sign}${timing.deviation}ms ${status}`;\n}\n\n// ============================================\n// ANIMATION INFO HELPERS\n// ============================================\n\n/**\n * Extract animation info from WAAPI Animation object\n */\nexport function captureAnimationInfo(animation: Animation): Record<string, unknown> {\n const timing = animation.effect?.getTiming?.();\n return {\n id: animation.id,\n playState: animation.playState,\n currentTime: animation.currentTime,\n playbackRate: animation.playbackRate,\n pending: animation.pending,\n duration: timing?.duration,\n easing: timing?.easing,\n fill: timing?.fill,\n };\n}\n\n/**\n * Capture all active animations on an element\n */\nexport function captureElementAnimations(el: HTMLElement): Record<string, unknown>[] {\n const animations = el.getAnimations();\n return animations.map(captureAnimationInfo);\n}\n","// ============================================\n// CHOREOGRAPHY TRACKER\n// Track animation timeline and detect overlaps\n// ============================================\n\nexport interface AnimationInfo {\n id: string;\n type: 'exit-fade' | 'exit-collapse' | 'flip' | 'enter' | 'width-collapse' | 'separator-exit';\n elementId: string;\n startTime: number;\n expectedDuration: number;\n endTime?: number;\n actualDuration?: number;\n}\n\nexport interface TimelineEvent {\n time: number;\n event: 'animation-start' | 'animation-end' | 'overlap-detected';\n animationId: string;\n type?: string;\n overlappingWith?: string[];\n expectedDuration?: number;\n actualDuration?: number;\n deviation?: number;\n}\n\nexport interface OverlapInfo {\n animation1: string;\n animation2: string;\n overlapStart: number;\n overlapDuration: number;\n}\n\nexport interface ChoreographySummary {\n totalAnimations: number;\n totalDuration: number;\n overlaps: OverlapInfo[];\n timeline: TimelineEvent[];\n activeAnimations: AnimationInfo[];\n}\n\n/**\n * ChoreographyTracker - Singleton for tracking animation choreography\n * Detects overlaps, generates timeline, measures actual vs expected timing\n */\nclass ChoreographyTrackerClass {\n private activeAnimations: Map<string, AnimationInfo> = new Map();\n private timeline: TimelineEvent[] = [];\n private completedAnimations: AnimationInfo[] = [];\n private sessionStartTime: number = 0;\n\n /**\n * Start a new tracking session (call when debug panel is cleared)\n */\n startSession(): void {\n if (!__DEBUG__) return;\n\n this.activeAnimations.clear();\n this.timeline = [];\n this.completedAnimations = [];\n this.sessionStartTime = performance.now();\n }\n\n /**\n * Get relative time since session start\n */\n private getRelativeTime(): number {\n return Math.round((performance.now() - this.sessionStartTime) * 100) / 100;\n }\n\n /**\n * Start tracking an animation\n */\n startAnimation(\n id: string,\n type: AnimationInfo['type'],\n elementId: string,\n expectedDuration: number\n ): void {\n if (!__DEBUG__) return;\n\n const startTime = this.getRelativeTime();\n\n const info: AnimationInfo = {\n id,\n type,\n elementId,\n startTime,\n expectedDuration,\n };\n\n this.activeAnimations.set(id, info);\n\n // Detect overlaps with currently running animations\n const overlaps = this.detectOverlaps(id);\n\n // Log start event\n this.timeline.push({\n time: startTime,\n event: 'animation-start',\n animationId: id,\n type,\n expectedDuration,\n overlappingWith: overlaps.length > 0 ? overlaps : undefined,\n });\n\n // Log overlap event if detected\n if (overlaps.length > 0) {\n this.timeline.push({\n time: startTime,\n event: 'overlap-detected',\n animationId: id,\n overlappingWith: overlaps,\n });\n }\n }\n\n /**\n * End tracking an animation\n */\n endAnimation(id: string): AnimationInfo | undefined {\n if (!__DEBUG__) return undefined;\n\n const info = this.activeAnimations.get(id);\n if (!info) return undefined;\n\n const endTime = this.getRelativeTime();\n const actualDuration = endTime - info.startTime;\n\n info.endTime = endTime;\n info.actualDuration = Math.round(actualDuration * 100) / 100;\n\n this.timeline.push({\n time: endTime,\n event: 'animation-end',\n animationId: id,\n type: info.type,\n expectedDuration: info.expectedDuration,\n actualDuration: info.actualDuration,\n deviation: Math.round((info.actualDuration - info.expectedDuration) * 100) / 100,\n });\n\n this.completedAnimations.push(info);\n this.activeAnimations.delete(id);\n\n return info;\n }\n\n /**\n * Detect which animations are overlapping with the given animation\n */\n private detectOverlaps(newAnimationId: string): string[] {\n const overlaps: string[] = [];\n\n this.activeAnimations.forEach((info, id) => {\n if (id !== newAnimationId) {\n overlaps.push(`${info.type}:${info.elementId}`);\n }\n });\n\n return overlaps;\n }\n\n /**\n * Get all overlap pairs with duration\n */\n getOverlaps(): OverlapInfo[] {\n if (!__DEBUG__) return [];\n\n const overlaps: OverlapInfo[] = [];\n const allAnimations = [...this.completedAnimations];\n\n // Also include active animations with estimated end time\n this.activeAnimations.forEach(info => {\n allAnimations.push({\n ...info,\n endTime: info.startTime + info.expectedDuration,\n actualDuration: info.expectedDuration,\n });\n });\n\n // Find overlapping pairs\n for (let i = 0; i < allAnimations.length; i++) {\n for (let j = i + 1; j < allAnimations.length; j++) {\n const a = allAnimations[i];\n const b = allAnimations[j];\n\n // Skip if either is undefined (TypeScript safety)\n if (!a || !b) continue;\n\n const aEnd = a.endTime || (a.startTime + a.expectedDuration);\n const bEnd = b.endTime || (b.startTime + b.expectedDuration);\n\n // Check if they overlap\n if (a.startTime < bEnd && aEnd > b.startTime) {\n const overlapStart = Math.max(a.startTime, b.startTime);\n const overlapEnd = Math.min(aEnd, bEnd);\n const overlapDuration = overlapEnd - overlapStart;\n\n if (overlapDuration > 0) {\n overlaps.push({\n animation1: `${a.type}:${a.elementId}`,\n animation2: `${b.type}:${b.elementId}`,\n overlapStart: Math.round(overlapStart * 100) / 100,\n overlapDuration: Math.round(overlapDuration * 100) / 100,\n });\n }\n }\n }\n }\n\n return overlaps;\n }\n\n /**\n * Get current timeline\n */\n getTimeline(): TimelineEvent[] {\n if (!__DEBUG__) return [];\n return [...this.timeline];\n }\n\n /**\n * Get active animation count\n */\n getActiveCount(): number {\n if (!__DEBUG__) return 0;\n return this.activeAnimations.size;\n }\n\n /**\n * Get active animations\n */\n getActiveAnimations(): AnimationInfo[] {\n if (!__DEBUG__) return [];\n return Array.from(this.activeAnimations.values());\n }\n\n /**\n * Get completed animations\n */\n getCompletedAnimations(): AnimationInfo[] {\n if (!__DEBUG__) return [];\n return [...this.completedAnimations];\n }\n\n /**\n * Get full choreography summary\n */\n getSummary(): ChoreographySummary {\n if (!__DEBUG__) {\n return {\n totalAnimations: 0,\n totalDuration: 0,\n overlaps: [],\n timeline: [],\n activeAnimations: [],\n };\n }\n\n const allAnimations = [...this.completedAnimations, ...this.getActiveAnimations()];\n\n let totalDuration = 0;\n if (allAnimations.length > 0) {\n const minStart = Math.min(...allAnimations.map(a => a.startTime));\n const maxEnd = Math.max(\n ...allAnimations.map(a => a.endTime || (a.startTime + a.expectedDuration))\n );\n totalDuration = maxEnd - minStart;\n }\n\n return {\n totalAnimations: allAnimations.length,\n totalDuration: Math.round(totalDuration * 100) / 100,\n overlaps: this.getOverlaps(),\n timeline: this.getTimeline(),\n activeAnimations: this.getActiveAnimations(),\n };\n }\n\n /**\n * Get timeline for visualization (normalized to 0-100%)\n */\n getTimelineForVisualization(): Array<{\n id: string;\n type: string;\n elementId: string;\n startPercent: number;\n widthPercent: number;\n duration: number;\n isActive: boolean;\n }> {\n if (!__DEBUG__) return [];\n\n const summary = this.getSummary();\n if (summary.totalDuration === 0) return [];\n\n const allAnimations = [...this.completedAnimations, ...this.getActiveAnimations()];\n const minStart = Math.min(...allAnimations.map(a => a.startTime));\n\n return allAnimations.map(anim => {\n const duration = anim.actualDuration || anim.expectedDuration;\n const startPercent = ((anim.startTime - minStart) / summary.totalDuration) * 100;\n const widthPercent = (duration / summary.totalDuration) * 100;\n\n return {\n id: anim.id,\n type: anim.type,\n elementId: anim.elementId,\n startPercent: Math.round(startPercent * 100) / 100,\n widthPercent: Math.round(widthPercent * 100) / 100,\n duration: Math.round(duration * 100) / 100,\n isActive: this.activeAnimations.has(anim.id),\n };\n });\n }\n}\n\n// Export singleton instance\nexport const choreographyTracker = new ChoreographyTrackerClass();\n\n// Export class for testing\nexport { ChoreographyTrackerClass };\n","import React, { useRef, useState, useLayoutEffect, useEffect } from 'react';\nimport { ANIMATION_DEFAULTS } from '../utils/animationUtils';\nimport { useDebug } from '../contexts/DebugContext';\nimport { capturePosition, createAnimationTimer } from '../utils/debugCapture';\nimport { choreographyTracker } from '../utils/choreographyTracker';\n\nexport interface SlidingTextProps {\n text: string;\n mode?: 'word' | 'character' | 'none';\n direction?: 'vertical' | 'horizontal';\n staggerDelay?: number;\n duration?: number;\n easing?: string;\n blur?: boolean;\n widthAnimation?: boolean;\n initial?: 'initial' | false;\n animate?: 'animate';\n exit?: 'exit';\n onAnimationComplete?: () => void;\n className?: string;\n style?: React.CSSProperties;\n}\n\nexport const SlidingText: React.FC<SlidingTextProps> = ({\n text,\n mode = 'word',\n direction = 'vertical',\n staggerDelay = ANIMATION_DEFAULTS.STAGGER_DELAY,\n duration = ANIMATION_DEFAULTS.DURATION_ENTER,\n easing = ANIMATION_DEFAULTS.EASING_ENTER,\n blur = true,\n widthAnimation = false,\n initial = 'initial',\n exit,\n className = '',\n style,\n}) => {\n const containerRef = useRef<HTMLDivElement>(null);\n const contentRef = useRef<HTMLDivElement>(null);\n const { logEvent } = useDebug();\n const enterTimerRef = useRef<ReturnType<typeof createAnimationTimer> | null>(null);\n const choreographyIdRef = useRef<string | null>(null);\n\n \n // Track if we've triggered the enter animation\n const hasTriggeredEnterRef = useRef(false);\n const [showAnimate, setShowAnimate] = useState(initial !== 'initial');\n\n // Trigger enter animation after mount\n useEffect(() => {\n if (initial === 'initial' && !hasTriggeredEnterRef.current) {\n hasTriggeredEnterRef.current = true;\n requestAnimationFrame(() => {\n setShowAnimate(true);\n });\n }\n }, [initial]);\n\n // Compute the actual visual state\n const visualState = exit === 'exit' ? 'exit' : (showAnimate ? 'animate' : 'initial');\n\n // Split text based on mode (moved up for logging)\n const elements = mode === 'character' ? text.split('') : [text];\n const totalEnterDuration = duration + (elements.length - 1) * staggerDelay;\n\n // Log enter animation start\n useEffect(() => {\n if (visualState === 'animate' && containerRef.current) {\n const container = containerRef.current;\n\n // Start timer for enter animation\n enterTimerRef.current = createAnimationTimer(totalEnterDuration);\n choreographyIdRef.current = `enter-${text.slice(0, 10)}-${Date.now()}`;\n\n // Start choreography tracking\n choreographyTracker.startAnimation(\n choreographyIdRef.current,\n 'enter',\n text,\n totalEnterDuration\n );\n\n // Capture initial state\n const initialPosition = capturePosition(container);\n\n logEvent({\n type: 'text-enter-start',\n source: 'SlidingText',\n message: `Enter animation starting for: \"${text}\"`,\n timing: {\n startTime: enterTimerRef.current.start,\n expectedDuration: totalEnterDuration\n },\n position: {\n element: text,\n x: initialPosition.x,\n y: initialPosition.y,\n width: initialPosition.width,\n height: initialPosition.height\n },\n animation: {\n name: 'enter',\n phase: 'start',\n easing: easing,\n fill: 'forwards'\n },\n data: {\n text,\n charCount: elements.length,\n mode,\n direction,\n staggerDelay,\n duration,\n totalDuration: totalEnterDuration,\n blur,\n widthAnimation\n }\n });\n\n // Schedule enter complete logging\n const completeTimer = setTimeout(() => {\n if (enterTimerRef.current && choreographyIdRef.current && containerRef.current) {\n const timingResult = enterTimerRef.current.end();\n choreographyTracker.endAnimation(choreographyIdRef.current);\n\n const finalPosition = capturePosition(containerRef.current);\n\n logEvent({\n type: 'text-enter-complete',\n source: 'SlidingText',\n message: `Enter animation complete for: \"${text}\"`,\n timing: {\n startTime: timingResult.startTime,\n endTime: timingResult.endTime,\n expectedDuration: timingResult.expectedDuration,\n actualDuration: timingResult.actualDuration,\n deviation: timingResult.deviation,\n deviationPercent: timingResult.deviationPercent\n },\n position: {\n element: text,\n x: finalPosition.x,\n y: finalPosition.y,\n width: finalPosition.width,\n height: finalPosition.height\n },\n animation: {\n name: 'enter',\n phase: 'complete',\n progress: 1,\n easing: easing,\n fill: 'forwards'\n },\n data: {\n text,\n deviationStatus: Math.abs(timingResult.deviation) < 10 ? 'OK' : 'WARNING'\n }\n });\n }\n }, totalEnterDuration + 50); // +50ms buffer\n\n return () => clearTimeout(completeTimer);\n }\n }, [visualState, text, mode, direction, staggerDelay, duration, easing, blur, widthAnimation, totalEnterDuration, elements.length, logEvent]);\n\n // Width Animation Logic\n useLayoutEffect(() => {\n if (!widthAnimation || !containerRef.current || !contentRef.current) return;\n\n const container = containerRef.current;\n const content = contentRef.current;\n\n if (visualState === 'initial') {\n container.style.width = '0px';\n } else if (visualState === 'animate') {\n const supportsInterpolateSize = CSS.supports('interpolate-size', 'allow-keywords');\n\n if (supportsInterpolateSize) {\n container.style.width = 'auto';\n container.style.transition = `width ${duration}ms ${easing}`;\n } else {\n const targetWidth = content.scrollWidth;\n container.style.width = `${targetWidth}px`;\n container.style.transition = `width ${duration}ms ${easing}`;\n\n const timer = setTimeout(() => {\n container.style.width = 'auto';\n }, duration);\n return () => clearTimeout(timer);\n }\n } else if (visualState === 'exit') {\n const currentWidth = container.getBoundingClientRect().width;\n container.style.width = `${currentWidth}px`;\n container.getBoundingClientRect(); // Force reflow\n container.style.width = '0px';\n container.style.transition = `width ${ANIMATION_DEFAULTS.DURATION_EXIT}ms ${ANIMATION_DEFAULTS.EASING_EXIT}`;\n }\n }, [visualState, widthAnimation, duration, easing, text]);\n\n const getTransitionStyle = (index: number): React.CSSProperties => {\n const delay = index * staggerDelay;\n const isExit = visualState === 'exit';\n const currentDuration = isExit ? ANIMATION_DEFAULTS.DURATION_EXIT : duration;\n const currentEasing = isExit ? ANIMATION_DEFAULTS.EASING_EXIT : easing;\n\n return {\n transition: `\n opacity ${currentDuration}ms ${currentEasing} ${delay}ms,\n transform ${currentDuration}ms ${currentEasing} ${delay}ms,\n filter ${currentDuration}ms ${currentEasing} ${delay}ms\n `,\n '--blur-amount': blur ? `${ANIMATION_DEFAULTS.BLUR_AMOUNT}px` : '0px',\n '--offset': direction === 'vertical' ? `${ANIMATION_DEFAULTS.OFFSET_VERTICAL}px` : `${ANIMATION_DEFAULTS.OFFSET_HORIZONTAL}px`\n } as React.CSSProperties;\n };\n\n return (\n <div\n ref={containerRef}\n className={`waapi-sliding-text-container ${className}`}\n style={style}\n >\n <div ref={contentRef} className={`waapi-sliding-text-content waapi-direction-${direction}`}>\n {elements.map((char, index) => (\n <span\n key={index}\n className={`waapi-sliding-text-token ${\n visualState === 'initial' ? 'enter-from' :\n visualState === 'animate' ? 'enter-to' :\n 'exit-active'\n }`}\n style={getTransitionStyle(index)}\n >\n {char}\n </span>\n ))}\n </div>\n </div>\n );\n};\n","import { useRef, useCallback, useEffect } from 'react';\nimport { TIMING, TRANSFORMS, EFFECTS, EASINGS } from '../utils/animationUtils';\nimport { useDebug } from '../contexts/DebugContext';\nimport {\n captureComputedStyles,\n capturePosition,\n createAnimationTimer,\n compareStyles\n} from '../utils/debugCapture';\nimport { choreographyTracker } from '../utils/choreographyTracker';\n\ninterface SeparatorState {\n tokenId: string;\n isVisible: boolean;\n isAnimating: boolean;\n animationPhase: 'idle' | 'exit-coordinated' | 'flip-coordinated' | 'completed';\n startTime?: number;\n expectedEndTime?: number;\n}\n\ninterface AnimationConfig {\n onComplete: (id: string) => void;\n exitDuration?: number;\n flipDuration?: number;\n exitEasing?: string;\n flipEasing?: string;\n}\n\ninterface WapiState {\n animatingIds: Set<string>;\n positions: Map<string, DOMRect>;\n separatorStates: Map<string, SeparatorState>;\n}\n\nexport const useWAAPIAnimations = (config: AnimationConfig) => {\n const { logEvent } = useDebug();\n\n // Ref pattern para logEvent - evita re-ejecutar callbacks cuando logEvent cambia\n const logEventRef = useRef(logEvent);\n useEffect(() => {\n logEventRef.current = logEvent;\n }, [logEvent]);\n\n // Ref pattern para config - evita re-crear callbacks\n const configRef = useRef(config);\n useEffect(() => {\n configRef.current = config;\n }, [config]);\n\n const elementsRef = useRef<Map<string, HTMLElement>>(new Map());\n const separatorsRef = useRef<Map<string, HTMLElement | null>>(new Map());\n const stateRef = useRef<WapiState>({\n animatingIds: new Set(),\n positions: new Map(),\n separatorStates: new Map()\n });\n const activeAnimationsRef = useRef<Map<string, Animation[]>>(new Map());\n\n const registerElement = useCallback((id: string, el: HTMLElement | null) => {\n if (el) {\n elementsRef.current.set(id, el);\n } else {\n elementsRef.current.delete(id);\n }\n }, []);\n\n const capturePositions = useCallback((excludeIds: Set<string>) => {\n const positions = new Map<string, DOMRect>();\n elementsRef.current.forEach((el, id) => {\n if (!excludeIds.has(id)) {\n const rect = el.getBoundingClientRect();\n if (rect.width > 0 && rect.height > 0) {\n positions.set(id, rect);\n }\n }\n });\n stateRef.current.positions = positions;\n return positions;\n }, []);\n\n const cancelAnimations = useCallback((id: string) => {\n const animations = activeAnimationsRef.current.get(id);\n if (animations) {\n animations.forEach(anim => anim.cancel());\n activeAnimationsRef.current.delete(id);\n }\n }, []);\n\n const registerSeparator = useCallback((tokenId: string, el: HTMLElement | null) => {\n // Initialize separator state if it doesn't exist\n if (!stateRef.current.separatorStates.has(tokenId)) {\n stateRef.current.separatorStates.set(tokenId, {\n tokenId,\n isVisible: true,\n isAnimating: false,\n animationPhase: 'idle'\n });\n }\n\n if (el) {\n separatorsRef.current.set(tokenId, el);\n } else {\n separatorsRef.current.delete(tokenId);\n // Don't delete state on null - it might be pre-registration\n }\n }, []);\n\n const getSeparatorState = useCallback((tokenId: string): SeparatorState | undefined => {\n return stateRef.current.separatorStates.get(tokenId);\n }, []);\n\n const startSeparatorAnimation = useCallback(async (tokenId: string): Promise<void> => {\n const separatorEl = separatorsRef.current.get(tokenId);\n const existingSeparatorState = stateRef.current.separatorStates.get(tokenId);\n\n if (!separatorEl) {\n // Check if we have a separator state (pre-registered) but no DOM element\n if (existingSeparatorState) {\n logEventRef.current({\n type: 'separator-animation-state-only',\n source: 'useWAAPIAnimations.startSeparatorAnimation',\n message: `Separator state exists but no DOM element for token: ${tokenId} - marking as completed`,\n data: { tokenId, reason: 'state-no-element' }\n });\n\n // Mark separator as completed immediately since there's no DOM element to animate\n existingSeparatorState.animationPhase = 'completed';\n existingSeparatorState.isVisible = false;\n return;\n }\n\n logEventRef.current({\n type: 'separator-animation-skipped',\n source: 'useWAAPIAnimations.startSeparatorAnimation',\n message: `No separator element found for token: ${tokenId} - skipping animation`,\n data: { tokenId, reason: 'no-separator-element' }\n });\n return;\n }\n\n const exitDuration = configRef.current.exitDuration || TIMING.EXIT_DURATION;\n const exitEasing = configRef.current.exitEasing || EASINGS.EASE_IN_CUBIC;\n\n const newSeparatorState: SeparatorState = {\n tokenId,\n isVisible: true,\n isAnimating: true,\n animationPhase: 'exit-coordinated',\n startTime: performance.now(),\n expectedEndTime: performance.now() + exitDuration\n };\n\n stateRef.current.separatorStates.set(tokenId, newSeparatorState);\n\n const choreographyId = `separator-${tokenId}-${Date.now()}`;\n choreographyTracker.startAnimation(choreographyId, 'separator-exit', tokenId, exitDuration);\n\n logEventRef.current({\n type: 'separator-animation-start',\n source: 'useWAAPIAnimations.startSeparatorAnimation',\n message: `Separator animation starting for token: ${tokenId}`,\n timing: {\n startTime: newSeparatorState.startTime || Date.now(),\n expectedDuration: exitDuration\n },\n data: { tokenId, duration: exitDuration }\n });\n\n separatorEl.classList.add('exit-coordinated');\n\n const separatorAnim = separatorEl.animate([\n { opacity: 1, transform: 'translateY(0) scale(1)' },\n { opacity: 0, transform: `translateY(${TRANSFORMS.OFFSET_Y_EXIT}px) scale(${TRANSFORMS.SCALE_EXIT})` }\n ], {\n duration: exitDuration,\n easing: exitEasing,\n fill: 'forwards'\n });\n\n try {\n await separatorAnim.finished;\n } catch {\n return;\n }\n\n newSeparatorState.animationPhase = 'completed';\n newSeparatorState.isVisible = false;\n separatorEl.classList.add('exit-completed');\n\n choreographyTracker.endAnimation(choreographyId);\n\n logEventRef.current({\n type: 'separator-animation-complete',\n source: 'useWAAPIAnimations.startSeparatorAnimation',\n message: `Separator animation complete for token: ${tokenId}`,\n data: { tokenId, duration: exitDuration }\n });\n }, []);\n\n // CLEANUP: Remove separator states when tokens change\n const cleanupSeparatorStates = useCallback((visibleTokenIds: Set<string>) => {\n const currentSeparatorStates = Array.from(stateRef.current.separatorStates.keys());\n\n currentSeparatorStates.forEach(tokenId => {\n // Remove separator state if token doesn't exist or doesn't need separator\n if (!visibleTokenIds.has(tokenId)) {\n stateRef.current.separatorStates.delete(tokenId);\n logEventRef.current({\n type: 'separator-state-cleanup',\n source: 'useWAAPIAnimations.cleanupSeparatorStates',\n message: `Removed separator state for non-existent token: ${tokenId}`,\n data: { tokenId }\n });\n }\n });\n }, []);\n\n const startExit = useCallback(async (id: string, additionalSeparatorIds?: string[]) => {\n const el = elementsRef.current.get(id);\n if (!el || stateRef.current.animatingIds.has(id)) return;\n\n stateRef.current.animatingIds.add(id);\n\n // 1. Capture positions of REMAINING tokens BEFORE any DOM changes\n const excludeIds = new Set([id]);\n capturePositions(excludeIds);\n\n // 1.5. Start separator animation for this token and any additional ones (avoiding duplicates)\n const allSeparatorIds = new Set([id, ...(additionalSeparatorIds || [])]);\n allSeparatorIds.forEach(sepId => {\n startSeparatorAnimation(sepId);\n });\n\n // Use research-based timing values\n const exitDuration = configRef.current.exitDuration || TIMING.EXIT_DURATION;\n const exitEasing = configRef.current.exitEasing || EASINGS.EASE_IN_CUBIC;\n const flipDuration = configRef.current.flipDuration || TIMING.FLIP_DURATION;\n\n // Use research-based transform/effect values (declared early for logging)\n const exitOffsetY = TRANSFORMS.OFFSET_Y_EXIT;\n const exitScale = TRANSFORMS.SCALE_EXIT;\n const exitBlur = EFFECTS.BLUR_EXIT;\n const exitStagger = TIMING.EXIT_STAGGER;\n\n // Get wrapper dimensions for width collapse\n const wrapperRect = el.getBoundingClientRect();\n const computedStyle = getComputedStyle(el);\n const marginRight = parseFloat(computedStyle.marginRight) || 0;\n\n // Create separate timers for exit fade and total orchestration\n const exitFadeTimer = createAnimationTimer(exitDuration);\n const orchestrationTimer = createAnimationTimer(exitDuration + flipDuration);\n\n // Capture initial styles and position for quantitative analysis\n const initialStyles = captureComputedStyles(el);\n const initialPosition = capturePosition(el);\n\n // Start choreography tracking\n const choreographyId = `exit-${id}-${Date.now()}`;\n choreographyTracker.startAnimation(choreographyId, 'exit-fade', id, exitDuration);\n\n // Also track width collapse as separate animation\n const widthCollapseId = `width-${id}-${Date.now()}`;\n choreographyTracker.startAnimation(widthCollapseId, 'width-collapse', id, exitDuration);\n\n logEventRef.current({\n type: 'animation-start-detailed',\n source: 'useWAAPIAnimations.startExit',\n message: `Exit animation starting for: ${id}`,\n timing: {\n startTime: exitFadeTimer.start,\n expectedDuration: exitDuration\n },\n styles: Object.entries(initialStyles).map(([prop, val]) => ({\n property: prop,\n expected: val,\n actual: val,\n matches: true\n })),\n position: {\n element: id,\n x: initialPosition.x,\n y: initialPosition.y,\n width: initialPosition.width,\n height: initialPosition.height\n },\n animation: {\n name: 'exit',\n phase: 'start',\n easing: exitEasing,\n fill: 'forwards'\n },\n data: {\n id,\n capturedPositions: stateRef.current.positions.size,\n wrapperWidth: wrapperRect.width,\n marginRight,\n expectedFinalStyles: {\n opacity: '0',\n transform: `translateY(${exitOffsetY}px) scale(${exitScale})`,\n filter: `blur(${exitBlur}px)`,\n width: '0px',\n marginRight: '0px'\n }\n }\n });\n\n const exitAnimations: Animation[] = [];\n const flipAnimations: Animation[] = [];\n let flipStarted = false;\n\n // 2. Run exit animations SIMULTANEOUSLY:\n // - Character fade out (staggered)\n // - Width collapse\n // - Margin collapse\n\n const tokens = el.querySelectorAll('.sliding-text-token');\n\n if (tokens.length > 0) {\n // Animate each character - no stagger on exit for cleaner feel\n tokens.forEach((token, index) => {\n const delay = index * exitStagger;\n const anim = (token as HTMLElement).animate([\n { opacity: 1, transform: 'translateY(0) scale(1)', filter: 'blur(0px)' },\n { opacity: 0, transform: `translateY(${exitOffsetY}px) scale(${exitScale})`, filter: `blur(${exitBlur}px)` }\n ], {\n duration: exitDuration,\n easing: exitEasing,\n delay,\n fill: 'forwards'\n });\n exitAnimations.push(anim);\n });\n } else {\n // Fallback: animate the whole element with scale\n const anim = el.animate([\n { opacity: 1, transform: 'translateY(0) scale(1)' },\n { opacity: 0, transform: `translateY(${exitOffsetY}px) scale(${exitScale})` }\n ], {\n duration: exitDuration,\n easing: exitEasing,\n fill: 'forwards'\n });\n exitAnimations.push(anim);\n }\n\n // Width + margin collapse animation (simultaneous with fade)\n el.style.overflow = 'hidden';\n const widthAnim = el.animate([\n { width: `${wrapperRect.width}px`, marginRight: `${marginRight}px` },\n { width: '0px', marginRight: '0px' }\n ], {\n duration: exitDuration,\n easing: exitEasing,\n fill: 'forwards'\n });\n exitAnimations.push(widthAnim);\n\n activeAnimationsRef.current.set(id, exitAnimations);\n\n // 3. Start FLIP animations at 25% of exit (research-based overlap)\n const flipDelay = exitDuration * TIMING.FLIP_DELAY_PERCENT;\n\n const startFlipAnimations = () => {\n if (flipStarted) return;\n flipStarted = true;\n\n // Remove from layout flow\n el.style.position = 'absolute';\n el.style.opacity = '0';\n el.style.pointerEvents = 'none';\n\n // Check if there are any remaining elements to FLIP\n const remainingElements = Array.from(elementsRef.current.entries()).filter(([elemId]) => elemId !== id);\n\n if (remainingElements.length === 0) {\n // No elements to FLIP - skip FLIP animations\n logEventRef.current({\n type: 'flip-skipped',\n source: 'useWAAPIAnimations.startExit',\n message: `No remaining elements to FLIP - skipping animation`,\n data: { id }\n });\n return;\n }\n\n elementsRef.current.forEach((remainingEl, remainingId) => {\n if (remainingId === id) return;\n\n const prevRect = stateRef.current.positions.get(remainingId);\n if (!prevRect) return;\n\n const newRect = remainingEl.getBoundingClientRect();\n const deltaX = prevRect.left - newRect.left;\n const deltaY = prevRect.top - newRect.top;\n\n // Ignore sub-pixel movements\n if (Math.abs(deltaX) < 1 && Math.abs(deltaY) < 1) return;\n\n // Track FLIP in choreography\n const flipChoreographyId = `flip-${remainingId}-${Date.now()}`;\n choreographyTracker.startAnimation(flipChoreographyId, 'flip', remainingId, flipDuration);\n\n logEventRef.current({\n type: 'flip-animation',\n source: 'useWAAPIAnimations.startExit',\n message: `FLIP animation for: ${remainingId}`,\n timing: {\n startTime: performance.now(),\n expectedDuration: flipDuration\n },\n position: {\n element: remainingId,\n x: newRect.left,\n y: newRect.top,\n width: newRect.width,\n height: newRect.height,\n delta: { x: deltaX, y: deltaY }\n },\n animation: {\n name: 'flip',\n phase: 'start',\n easing: EASINGS.SPRING_GENTLE,\n fill: 'none'\n },\n data: {\n id: remainingId,\n deltaX: Math.round(deltaX * 100) / 100,\n deltaY: Math.round(deltaY * 100) / 100,\n prevPosition: {\n x: prevRect.left,\n y: prevRect.top,\n width: prevRect.width,\n height: prevRect.height\n },\n newPosition: {\n x: newRect.left,\n y: newRect.top,\n width: newRect.width,\n height: newRect.height\n }\n }\n });\n\n // Use spring easing for natural FLIP feel\n const flipAnim = remainingEl.animate([\n { transform: `translate3d(${deltaX}px, ${deltaY}px, 0)` },\n { transform: 'translate3d(0, 0, 0)' }\n ], {\n duration: flipDuration,\n easing: EASINGS.SPRING_GENTLE // Spring for natural overshoot\n });\n\n // Track FLIP completion\n flipAnim.onfinish = () => {\n choreographyTracker.endAnimation(flipChoreographyId);\n logEventRef.current({\n type: 'flip-animation-complete',\n source: 'useWAAPIAnimations.startExit',\n message: `FLIP complete for: ${remainingId}`,\n animation: {\n name: 'flip',\n phase: 'complete',\n progress: 1\n },\n data: { id: remainingId }\n });\n };\n\n flipAnimations.push(flipAnim);\n });\n };\n\n // Schedule FLIP to start during exit animation (overlap)\n const flipTimer = setTimeout(startFlipAnimations, flipDelay);\n\n // 4. Separate timing: exit-fade completes first, width-collapse coordinates with FLIP\n try {\n // Separate fade animations from width animation\n const fadeAnimations = exitAnimations.filter(anim => anim !== widthAnim);\n const widthAnimationPromise = widthAnim.finished;\n\n // Wait for fade animations to complete (exactly 180ms)\n await Promise.all(fadeAnimations.map(a => a.finished));\n\n // Log exit-fade completion (exactly 180ms)\n const exitFadeTiming = exitFadeTimer.end();\n logEventRef.current({\n type: 'exit-fade-complete',\n source: 'useWAAPIAnimations.startExit',\n message: `Exit fade complete for: ${id}`,\n timing: {\n startTime: exitFadeTiming.startTime,\n endTime: exitFadeTiming.endTime,\n expectedDuration: exitFadeTiming.expectedDuration,\n actualDuration: exitFadeTiming.actualDuration,\n deviation: exitFadeTiming.deviation,\n deviationPercent: exitFadeTiming.deviationPercent\n },\n animation: {\n name: 'exit-fade',\n phase: 'complete',\n progress: 1,\n easing: exitEasing\n },\n data: { id }\n });\n\n // Ensure FLIP started if exit finished early\n startFlipAnimations();\n\n // Wait for both FLIP and width-collapse to complete (can extend beyond 180ms)\n await Promise.all([\n ...flipAnimations.map(a => a.finished.catch(() => {})),\n widthAnimationPromise\n ]);\n } catch {\n // Animation was cancelled\n clearTimeout(flipTimer);\n stateRef.current.animatingIds.delete(id);\n return;\n }\n\n // End choreography tracking\n choreographyTracker.endAnimation(choreographyId);\n choreographyTracker.endAnimation(widthCollapseId);\n\n // Capture orchestration timing result (total time including FLIP)\n const orchestrationTiming = orchestrationTimer.end();\n\n // Capture final position for verification\n const finalPosition = capturePosition(el);\n\n // Compare expected vs actual final styles\n const expectedFinalStyles = {\n opacity: '0',\n width: '0px',\n marginRight: '0px'\n };\n const styleComparison = compareStyles(expectedFinalStyles, el);\n\n logEventRef.current({\n type: 'orchestration-complete',\n source: 'useWAAPIAnimations.startExit',\n message: `Orchestration complete for: ${id}`,\n timing: {\n startTime: orchestrationTiming.startTime,\n endTime: orchestrationTiming.endTime,\n expectedDuration: orchestrationTiming.expectedDuration,\n actualDuration: orchestrationTiming.actualDuration,\n deviation: orchestrationTiming.deviation,\n deviationPercent: orchestrationTiming.deviationPercent\n },\n styles: styleComparison,\n position: {\n element: id,\n x: finalPosition.x,\n y: finalPosition.y,\n width: finalPosition.width,\n height: finalPosition.height\n },\n animation: {\n name: 'orchestration',\n phase: 'complete',\n progress: 1,\n easing: exitEasing,\n fill: 'forwards'\n },\n data: {\n id,\n choreographySummary: choreographyTracker.getSummary(),\n deviationStatus: Math.abs(orchestrationTiming.deviation) < 100 ? 'OK' : 'WARNING',\n styleMatchCount: styleComparison.filter(s => s.matches).length,\n styleMismatchCount: styleComparison.filter(s => !s.matches).length\n }\n });\n\n // Log timing analysis event for orchestration\n logEventRef.current({\n type: 'animation-timing',\n source: 'useWAAPIAnimations.startExit',\n message: `Orchestration timing for: ${id}`,\n timing: {\n startTime: orchestrationTiming.startTime,\n endTime: orchestrationTiming.endTime,\n expectedDuration: orchestrationTiming.expectedDuration,\n actualDuration: orchestrationTiming.actualDuration,\n deviation: orchestrationTiming.deviation,\n deviationPercent: orchestrationTiming.deviationPercent\n },\n animation: {\n name: 'orchestration',\n phase: 'complete'\n },\n data: {\n id,\n status: Math.abs(orchestrationTiming.deviation) < 100 ? '✅ ON TIME' : '⚠️ SLOW'\n }\n });\n\n // 5. Cleanup and notify completion\n stateRef.current.animatingIds.delete(id);\n activeAnimationsRef.current.delete(id);\n elementsRef.current.delete(id);\n\n configRef.current.onComplete(id);\n }, [capturePositions]); // Removed config - using configRef\n\n const isAnimating = useCallback((id?: string) => {\n if (id) return stateRef.current.animatingIds.has(id);\n return stateRef.current.animatingIds.size > 0;\n }, []);\n\n // Cleanup on unmount\n useEffect(() => {\n return () => {\n activeAnimationsRef.current.forEach((animations) => {\n animations.forEach(anim => anim.cancel());\n });\n activeAnimationsRef.current.clear();\n };\n }, []);\n\n return {\n registerElement,\n startExit,\n isAnimating,\n cancelAnimations,\n // Separator management functions\n registerSeparator,\n getSeparatorState,\n startSeparatorAnimation,\n cleanupSeparatorStates\n };\n};\n","import React, { useEffect, useState, useRef, useCallback } from 'react';\nimport { SlidingText } from './SlidingText';\nimport { SlidingNumber } from './SlidingNumber';\nimport { useWAAPIAnimations } from '../hooks/useWAAPIAnimations';\nimport { PRESETS, ANIMATION_DEFAULTS } from '../utils/animationUtils';\nimport { useDebug } from '../contexts/DebugContext';\n\nexport interface Token {\n id: string;\n text: string;\n}\n\nexport interface AnimatedTokensProps {\n tokens: Token[];\n placeholder?: string;\n maxVisible?: number;\n textAnimationMode?: 'character' | 'word';\n textDirection?: 'vertical' | 'horizontal';\n textStaggerDelay?: number;\n separator?: string;\n enableWidthAnimation?: boolean;\n className?: string;\n tokenClassName?: string;\n placeholderClassName?: string;\n separatorClassName?: string;\n}\n\nexport const AnimatedTokens: React.FC<AnimatedTokensProps> = ({\n tokens,\n placeholder = 'No tokens',\n maxVisible,\n textAnimationMode = 'character',\n textDirection = 'vertical',\n textStaggerDelay = 15,\n separator = ', ',\n enableWidthAnimation = false,\n className = '',\n tokenClassName = '',\n placeholderClassName = '',\n separatorClassName = '',\n}) => {\n const { logEvent } = useDebug();\n const isMountedRef = useRef(true);\n\n // Ref pattern to avoid dependency issues\n const logEventRef = useRef(logEvent);\n useEffect(() => {\n logEventRef.current = logEvent;\n }, [logEvent]);\n\n useEffect(() => {\n isMountedRef.current = true;\n logEventRef.current({\n type: 'render',\n source: 'AnimatedTokens.mount',\n message: 'AnimatedTokens mounted',\n data: { tokenCount: tokens.length }\n });\n return () => {\n logEventRef.current({\n type: 'component-unmounted',\n source: 'AnimatedTokens.unmount',\n message: 'AnimatedTokens UNMOUNTING',\n data: { timestamp: Date.now() }\n });\n isMountedRef.current = false;\n };\n }, []); // Empty deps - only run on mount/unmount\n\n // Single state: displayTokens (includes tokens being animated out)\n const [displayTokens, setDisplayTokens] = useState<Token[]>(tokens);\n\n // Track which tokens are currently animating (via ref, not state)\n const animatingIdsRef = useRef<Set<string>>(new Set());\n\n // Stable callback for onComplete\n const handleAnimationComplete = useCallback((id: string) => {\n if (!isMountedRef.current) return;\n\n logEventRef.current({\n type: 'token-dom-remove',\n source: 'AnimatedTokens.onComplete',\n message: `Token removed from DOM: ${id}`,\n data: { id }\n });\n\n animatingIdsRef.current.delete(id);\n setDisplayTokens(prev => prev.filter(t => t.id !== id));\n }, []);\n\n // WAAPI-centric hook - all animation logic handled internally\n const {\n registerElement,\n startExit,\n isAnimating,\n // Separator management functions\n registerSeparator,\n getSeparatorState,\n cleanupSeparatorStates\n } = useWAAPIAnimations({\n onComplete: handleAnimationComplete\n });\n\n // Store hook functions in refs to avoid dependency issues\n const startExitRef = useRef(startExit);\n const getSeparatorStateRef = useRef(getSeparatorState);\n const cleanupSeparatorStatesRef = useRef(cleanupSeparatorStates);\n useEffect(() => {\n startExitRef.current = startExit;\n getSeparatorStateRef.current = getSeparatorState;\n cleanupSeparatorStatesRef.current = cleanupSeparatorStates;\n }, [startExit, getSeparatorState, cleanupSeparatorStates]);\n\n // Calculate visible tokens and overflow\n const activeTokens = displayTokens.filter(t => !animatingIdsRef.current.has(t.id) || !isAnimating(t.id));\n const visibleTokens = maxVisible ? displayTokens.slice(0, maxVisible) : displayTokens;\n const overflowCount = maxVisible ? Math.max(0, activeTokens.length - maxVisible) : 0;\n\n // Enhanced separator logic function\n const shouldShowSeparator = (tokenId: string, tokenIndex: number): boolean => {\n const isLastToken = tokenIndex >= visibleTokens.length - 1;\n if (isLastToken) return false;\n\n const separatorState = getSeparatorStateRef.current?.(tokenId);\n\n \n // Show separator if:\n // 1. No separator state (default to visible)\n // 2. Separator is idle or animating but not completed\n if (!separatorState) return true;\n\n switch (separatorState.animationPhase) {\n case 'idle':\n case 'exit-coordinated':\n case 'flip-coordinated':\n return true; // Still visible during coordinated animation\n case 'completed':\n return false; // Animation finished, hide separator\n default:\n return true;\n }\n };\n\n // Detect removals and additions - ONE useEffect, no RAF nesting\n useEffect(() => {\n const currentIds = new Set(tokens.map(t => t.id));\n const displayIds = new Set(displayTokens.map(t => t.id));\n\n // Clean up separator states for tokens that no longer exist\n const visibleTokenIds = new Set(visibleTokens.map(t => t.id));\n cleanupSeparatorStatesRef.current?.(visibleTokenIds);\n\n // Detect removals\n const removed = displayTokens.filter(t =>\n !currentIds.has(t.id) && !animatingIdsRef.current.has(t.id)\n );\n\n // Detect additions\n const added = tokens.filter(t => !displayIds.has(t.id));\n\n // Skip if no changes\n if (removed.length === 0 && added.length === 0) return;\n\n logEventRef.current({\n type: 'render',\n source: 'AnimatedTokens.useEffect',\n message: 'Token sync',\n data: {\n currentTokens: tokens.map(t => t.id),\n displayTokens: displayTokens.map(t => t.id),\n removed: removed.map(t => t.id),\n added: added.map(t => t.id),\n animating: Array.from(animatingIdsRef.current)\n }\n });\n\n // Handle removals - check if in overflow (no animation needed)\n removed.forEach(token => {\n const indexInDisplay = displayTokens.findIndex(dt => dt.id === token.id);\n const isInOverflow = maxVisible !== undefined && indexInDisplay >= maxVisible;\n\n if (isInOverflow) {\n logEventRef.current({\n type: 'overflow-token-remove',\n source: 'AnimatedTokens.useEffect',\n message: 'Removing overflow token without animation',\n data: { id: token.id }\n });\n setDisplayTokens(prev => prev.filter(t => t.id !== token.id));\n } else {\n // Find additional separators that need animation\n const additionalSeparators: string[] = [];\n const visibleTokens = displayTokens.slice(0, maxVisible);\n const tokenIndexInVisible = visibleTokens.findIndex(t => t.id === token.id);\n\n // Only animate separator of this token if it's NOT the last visible token\n // (last tokens don't have separators)\n let shouldAnimateOwnSeparator = tokenIndexInVisible < visibleTokens.length - 1;\n\n // If removing last visible token, the previous token loses its separator\n if (tokenIndexInVisible === visibleTokens.length - 1 && tokenIndexInVisible > 0) {\n const previousToken = visibleTokens[tokenIndexInVisible - 1];\n if (previousToken) {\n additionalSeparators.push(previousToken.id);\n }\n }\n\n \n logEventRef.current({\n type: 'token-remove',\n source: 'AnimatedTokens.useEffect',\n message: `Token marked for exit: ${token.text}`,\n data: {\n id: token.id,\n text: token.text,\n additionalSeparators,\n shouldAnimateOwnSeparator,\n wasLastVisible: tokenIndexInVisible === visibleTokens.length - 1\n }\n });\n animatingIdsRef.current.add(token.id);\n\n // Pass separators to animate: additional ones + potentially own separator\n const separatorsToAnimate = shouldAnimateOwnSeparator\n ? [token.id, ...additionalSeparators]\n : additionalSeparators;\n\n startExitRef.current(token.id, separatorsToAnimate);\n }\n });\n\n // Handle additions\n if (added.length > 0) {\n added.forEach(t => {\n logEventRef.current({\n type: 'token-add',\n source: 'AnimatedTokens.useEffect',\n message: `New token detected: ${t.text}`,\n data: { id: t.id, text: t.text }\n });\n });\n\n // Pre-register separators for newly added tokens to ensure they have state when rendered\n added.forEach(t => {\n const tokenIndex = tokens.findIndex(token => token.id === t.id);\n const needsSeparator = maxVisible\n ? tokenIndex < Math.min(maxVisible, tokens.length) - 1\n : tokenIndex < tokens.length - 1;\n\n if (needsSeparator) {\n // Pre-register separator state to ensure it's available for shouldShowSeparator\n registerSeparator(t.id, null);\n }\n });\n\n // Merge added tokens while preserving order from tokens prop\n const exitingTokens = displayTokens.filter(t => !currentIds.has(t.id));\n let result = [...tokens];\n\n // Insert exiting tokens at their original positions\n exitingTokens.forEach(et => {\n const oldIdx = displayTokens.findIndex(t => t.id === et.id);\n if (oldIdx !== -1 && oldIdx <= result.length) {\n result.splice(oldIdx, 0, et);\n }\n });\n\n if (JSON.stringify(result.map(t => t.id)) !== JSON.stringify(displayTokens.map(t => t.id))) {\n setDisplayTokens(result);\n }\n }\n }, [tokens, maxVisible]); // Removed startExit and logEvent - using refs\n\n // Overflow state tracking for enter/exit animations\n const prevOverflowCount = useRef(0);\n const isOverflowEntering = overflowCount > 0 && prevOverflowCount.current === 0;\n const isOverflowExiting = overflowCount === 0 && prevOverflowCount.current > 0;\n\n useEffect(() => {\n prevOverflowCount.current = overflowCount;\n }, [overflowCount]);\n\n const showPlaceholder = displayTokens.length === 0 && !isAnimating() && !!placeholder;\n\n return (\n <div className={`waapi-animated-tokens-container ${className}`}>\n {showPlaceholder && (\n <SlidingText\n key=\"placeholder\"\n text={placeholder}\n mode={PRESETS.placeholder.mode}\n direction={PRESETS.placeholder.direction}\n blur={PRESETS.placeholder.blur}\n duration={PRESETS.placeholder.duration}\n initial=\"initial\"\n animate=\"animate\"\n className={`waapi-token-placeholder ${placeholderClassName}`}\n />\n )}\n\n {visibleTokens.map((token, index) => {\n const isExiting = animatingIdsRef.current.has(token.id);\n\n // Enhanced separator logic: show separator if not last token AND separator is not completed\n const showSeparator = shouldShowSeparator(token.id, index);\n\n return (\n <div\n key={token.id}\n className={`waapi-token-wrapper ${tokenClassName} ${isExiting ? 'exit-active' : ''}`}\n ref={el => registerElement(token.id, el as HTMLElement)}\n >\n <SlidingText\n text={token.text}\n mode={isExiting ? 'none' : textAnimationMode}\n direction={textDirection}\n staggerDelay={textStaggerDelay}\n duration={ANIMATION_DEFAULTS.DURATION_ENTER}\n blur={PRESETS.newToken.blur}\n widthAnimation={!isExiting && enableWidthAnimation}\n initial={isExiting ? false : 'initial'}\n animate=\"animate\"\n />\n {showSeparator && (\n <span\n className={`waapi-token-separator ${separatorClassName}`}\n ref={el => registerSeparator(token.id, el)}\n >\n {separator}\n </span>\n )}\n </div>\n );\n })}\n\n {(overflowCount > 0 || isOverflowExiting) && (\n <div\n className={`waapi-token-overflow ${tokenClassName} ${isOverflowEntering ? 'entering' : ''} ${isOverflowExiting ? 'exiting' : ''}`}\n ref={el => registerElement('overflow-counter', el as HTMLElement)}\n >\n {visibleTokens.length > 0 && (\n <span className={`waapi-token-separator ${separatorClassName}`}>\n {separator}\n </span>\n )}\n <span className={`waapi-token-separator ${separatorClassName}`}>+</span>\n <SlidingNumber\n value={overflowCount}\n duration={ANIMATION_DEFAULTS.DURATION_ENTER}\n fontSize=\"inherit\"\n fontWeight=\"inherit\"\n color=\"inherit\"\n />\n <SlidingText\n text=\" more\"\n mode={textAnimationMode}\n direction={textDirection}\n staggerDelay={textStaggerDelay}\n duration={isOverflowExiting ? ANIMATION_DEFAULTS.DURATION_EXIT : ANIMATION_DEFAULTS.DURATION_ENTER}\n blur={PRESETS.newToken.blur}\n initial={isOverflowEntering ? 'initial' : false}\n animate=\"animate\"\n />\n </div>\n )}\n </div>\n );\n};\n","import { useRef, useCallback, useEffect } from 'react';\nimport type { ElementRegistryAPI, ElementRegistryCallbacks } from './types';\n\n/**\n * Hook for tracking DOM elements by ID\n * Extracted from useWAAPIAnimations element registration logic\n *\n * @example\n * ```tsx\n * const registry = useElementRegistry({\n * onRegister: (id) => console.log(`Registered: ${id}`),\n * onUnregister: (id) => console.log(`Unregistered: ${id}`)\n * });\n *\n * // In render:\n * <div ref={el => registry.register('item-1', el)} />\n * ```\n */\nexport function useElementRegistry(\n callbacks?: ElementRegistryCallbacks\n): ElementRegistryAPI {\n const elementsRef = useRef<Map<string, HTMLElement>>(new Map());\n\n const callbacksRef = useRef(callbacks);\n useEffect(() => {\n callbacksRef.current = callbacks;\n }, [callbacks]);\n\n const register = useCallback((id: string, el: HTMLElement | null) => {\n if (el) {\n elementsRef.current.set(id, el);\n callbacksRef.current?.onRegister?.(id, el);\n } else {\n if (elementsRef.current.has(id)) {\n elementsRef.current.delete(id);\n callbacksRef.current?.onUnregister?.(id);\n }\n }\n }, []);\n\n const unregister = useCallback((id: string) => {\n if (elementsRef.current.has(id)) {\n elementsRef.current.delete(id);\n callbacksRef.current?.onUnregister?.(id);\n }\n }, []);\n\n const get = useCallback((id: string): HTMLElement | undefined => {\n return elementsRef.current.get(id);\n }, []);\n\n const getAll = useCallback((): Map<string, HTMLElement> => {\n return new Map(elementsRef.current);\n }, []);\n\n const has = useCallback((id: string): boolean => {\n return elementsRef.current.has(id);\n }, []);\n\n const clear = useCallback(() => {\n const ids = Array.from(elementsRef.current.keys());\n elementsRef.current.clear();\n ids.forEach(id => callbacksRef.current?.onUnregister?.(id));\n }, []);\n\n return {\n register,\n unregister,\n get,\n getAll,\n has,\n clear,\n get size() {\n return elementsRef.current.size;\n }\n };\n}\n","import { useRef, useCallback } from 'react';\nimport { TIMING } from '../utils/animationUtils';\nimport type { PositionCaptureAPI, FLIPDelta, PositionCaptureOptions } from './types';\n\nconst DEFAULT_MIN_DELTA_PX = TIMING.MIN_DELTA_PX;\n\n/**\n * Hook for capturing element positions and calculating FLIP deltas\n * Extracted from useWAAPIAnimations position capture logic\n *\n * @example\n * ```tsx\n * const registry = useElementRegistry();\n * const positions = usePositionCapture(registry.getAll);\n *\n * // Before layout change:\n * const before = positions.capture();\n *\n * // After layout change:\n * const after = positions.capture();\n * const deltas = positions.calculateDeltas(before, after);\n * ```\n */\nexport function usePositionCapture(\n getElements: () => Map<string, HTMLElement>,\n options?: PositionCaptureOptions\n): PositionCaptureAPI {\n const lastCaptureRef = useRef<Map<string, DOMRect>>(new Map());\n const minDeltaPx = options?.minDeltaPx ?? DEFAULT_MIN_DELTA_PX;\n\n const capture = useCallback((excludeIds?: Set<string>): Map<string, DOMRect> => {\n const positions = new Map<string, DOMRect>();\n const elements = getElements();\n\n elements.forEach((el, id) => {\n if (excludeIds?.has(id)) return;\n\n const rect = el.getBoundingClientRect();\n if (rect.width > 0 && rect.height > 0) {\n positions.set(id, rect);\n }\n });\n\n lastCaptureRef.current = positions;\n return positions;\n }, [getElements]);\n\n const getPosition = useCallback((id: string): DOMRect | undefined => {\n return lastCaptureRef.current.get(id);\n }, []);\n\n const calculateDeltas = useCallback((\n before: Map<string, DOMRect>,\n after: Map<string, DOMRect>\n ): Map<string, FLIPDelta> => {\n const deltas = new Map<string, FLIPDelta>();\n\n after.forEach((newRect, id) => {\n const prevRect = before.get(id);\n if (!prevRect) return;\n\n const deltaX = prevRect.left - newRect.left;\n const deltaY = prevRect.top - newRect.top;\n const deltaWidth = prevRect.width - newRect.width;\n const deltaHeight = prevRect.height - newRect.height;\n\n const isSignificant =\n Math.abs(deltaX) >= minDeltaPx ||\n Math.abs(deltaY) >= minDeltaPx ||\n Math.abs(deltaWidth) >= minDeltaPx ||\n Math.abs(deltaHeight) >= minDeltaPx;\n\n deltas.set(id, {\n id,\n deltaX,\n deltaY,\n deltaWidth,\n deltaHeight,\n isSignificant\n });\n });\n\n return deltas;\n }, [minDeltaPx]);\n\n const getLastCapture = useCallback((): Map<string, DOMRect> => {\n return new Map(lastCaptureRef.current);\n }, []);\n\n const clear = useCallback(() => {\n lastCaptureRef.current.clear();\n }, []);\n\n return {\n capture,\n getPosition,\n calculateDeltas,\n getLastCapture,\n clear\n };\n}\n","import { useRef, useCallback, useEffect } from 'react';\nimport { TIMING, EASINGS } from '../utils/animationUtils';\nimport type { FLIPAnimationAPI, FLIPAnimationOptions, FLIPDelta } from './types';\n\nconst DEFAULT_DURATION = TIMING.FLIP_DURATION;\nconst DEFAULT_EASING = EASINGS.SPRING_GENTLE;\n\n/**\n * Hook for executing FLIP (First-Last-Invert-Play) animations\n * Extracted from useWAAPIAnimations FLIP logic\n *\n * Uses spring easing for natural motion with slight overshoot\n *\n * @example\n * ```tsx\n * const flip = useFLIPAnimation();\n *\n * // After calculating deltas:\n * await flip.animateAll(elements, deltas, {\n * duration: 300,\n * onComplete: (id) => console.log(`${id} completed`)\n * });\n * ```\n */\nexport function useFLIPAnimation(): FLIPAnimationAPI {\n const activeAnimationsRef = useRef<Map<string, Animation>>(new Map());\n const animatingIdsRef = useRef<Set<string>>(new Set());\n\n const animate = useCallback((\n element: HTMLElement,\n delta: FLIPDelta,\n options?: FLIPAnimationOptions\n ): Animation => {\n const duration = options?.duration ?? DEFAULT_DURATION;\n const easing = options?.easing ?? DEFAULT_EASING;\n\n if (activeAnimationsRef.current.has(delta.id)) {\n activeAnimationsRef.current.get(delta.id)?.cancel();\n }\n\n animatingIdsRef.current.add(delta.id);\n options?.onStart?.(delta.id);\n\n const animation = element.animate([\n { transform: `translate3d(${delta.deltaX}px, ${delta.deltaY}px, 0)` },\n { transform: 'translate3d(0, 0, 0)' }\n ], {\n duration,\n easing\n });\n\n activeAnimationsRef.current.set(delta.id, animation);\n\n animation.onfinish = () => {\n animatingIdsRef.current.delete(delta.id);\n activeAnimationsRef.current.delete(delta.id);\n options?.onComplete?.(delta.id);\n };\n\n animation.oncancel = () => {\n animatingIdsRef.current.delete(delta.id);\n activeAnimationsRef.current.delete(delta.id);\n };\n\n return animation;\n }, []);\n\n const animateAll = useCallback(async (\n elements: Map<string, HTMLElement>,\n deltas: Map<string, FLIPDelta>,\n options?: FLIPAnimationOptions\n ): Promise<void> => {\n const animations: Animation[] = [];\n\n deltas.forEach((delta, id) => {\n if (!delta.isSignificant) return;\n\n const element = elements.get(id);\n if (!element) return;\n\n const anim = animate(element, delta, options);\n animations.push(anim);\n });\n\n if (animations.length === 0) return;\n\n await Promise.all(\n animations.map(anim =>\n anim.finished.catch(() => {})\n )\n );\n }, [animate]);\n\n const cancel = useCallback((id: string) => {\n const animation = activeAnimationsRef.current.get(id);\n if (animation) {\n animation.cancel();\n activeAnimationsRef.current.delete(id);\n animatingIdsRef.current.delete(id);\n }\n }, []);\n\n const cancelAll = useCallback(() => {\n activeAnimationsRef.current.forEach(animation => animation.cancel());\n activeAnimationsRef.current.clear();\n animatingIdsRef.current.clear();\n }, []);\n\n const isAnimating = useCallback((id?: string): boolean => {\n if (id) return animatingIdsRef.current.has(id);\n return animatingIdsRef.current.size > 0;\n }, []);\n\n useEffect(() => {\n return () => {\n cancelAll();\n };\n }, [cancelAll]);\n\n // CRITICAL: Memoize return object to prevent cascading re-renders\n // that would cancel animations via useEffect cleanup chains\n const api = useRef<FLIPAnimationAPI>({\n animate,\n animateAll,\n cancel,\n cancelAll,\n isAnimating\n });\n\n // Update refs to latest callbacks (they're stable due to useCallback)\n api.current.animate = animate;\n api.current.animateAll = animateAll;\n api.current.cancel = cancel;\n api.current.cancelAll = cancelAll;\n api.current.isAnimating = isAnimating;\n\n return api.current;\n}\n","import { useRef, useCallback, useEffect } from 'react';\nimport { TIMING, TRANSFORMS, EFFECTS, EASINGS } from '../utils/animationUtils';\nimport { useElementRegistry } from './useElementRegistry';\nimport { usePositionCapture } from './usePositionCapture';\nimport { useFLIPAnimation } from './useFLIPAnimation';\nimport { useDebug } from '../contexts/DebugContext';\nimport type {\n AnimationOrchestratorAPI,\n AnimationOrchestratorConfig,\n ExitOptions,\n EnterOptions,\n OrchestratorState\n} from './types';\n\n/**\n * Main animation orchestrator hook\n * Composes useElementRegistry, usePositionCapture, and useFLIPAnimation\n *\n * Handles the full exit animation sequence:\n * 1. Capture positions of remaining elements\n * 2. Run exit animation (fade + width collapse)\n * 3. Start FLIP animations at 25% of exit (overlap)\n * 4. Cleanup and notify completion\n *\n * @example\n * ```tsx\n * const orchestrator = useAnimationOrchestrator({\n * onExitComplete: (id) => removeFromList(id)\n * });\n *\n * // Register elements:\n * <div ref={el => orchestrator.registerElement('item-1', el)} />\n *\n * // Trigger exit:\n * await orchestrator.startExit('item-1');\n * ```\n */\nexport function useAnimationOrchestrator(\n config?: AnimationOrchestratorConfig\n): AnimationOrchestratorAPI {\n const configRef = useRef(config);\n useEffect(() => {\n configRef.current = config;\n }, [config]);\n\n // Debug context - ref pattern to avoid callback recreation\n const { logEvent } = useDebug();\n const logEventRef = useRef(logEvent);\n useEffect(() => {\n logEventRef.current = logEvent;\n }, [logEvent]);\n\n const stateRef = useRef<OrchestratorState>({\n animatingIds: new Set(),\n positions: new Map(),\n activeAnimations: new Map()\n });\n\n const registry = useElementRegistry();\n const positions = usePositionCapture(registry.getAll, {\n minDeltaPx: config?.minDeltaPx ?? TIMING.MIN_DELTA_PX\n });\n const flip = useFLIPAnimation();\n\n const exitDuration = config?.exitDuration ?? TIMING.EXIT_DURATION;\n const flipDuration = config?.flipDuration ?? TIMING.FLIP_DURATION;\n const exitEasing = config?.exitEasing ?? EASINGS.EASE_IN_CUBIC;\n\n const cancelAnimation = useCallback((id: string) => {\n const animations = stateRef.current.activeAnimations.get(id);\n if (animations) {\n animations.forEach(anim => anim.cancel());\n stateRef.current.activeAnimations.delete(id);\n }\n stateRef.current.animatingIds.delete(id);\n flip.cancel(id);\n }, [flip]);\n\n const cancelAllAnimations = useCallback(() => {\n stateRef.current.activeAnimations.forEach(animations => {\n animations.forEach(anim => anim.cancel());\n });\n stateRef.current.activeAnimations.clear();\n stateRef.current.animatingIds.clear();\n flip.cancelAll();\n }, [flip]);\n\n // Get behavior configuration with defaults\n const flipBehavior = config?.flipBehavior ?? 'all';\n const exitPositionStrategy = config?.exitPositionStrategy ?? 'absolute-fixed';\n\n /**\n * NEW FLIP IMPLEMENTATION - Based on research from:\n * - Paul Lewis: FLIP Your Animations (aerotwist.com)\n * - react-flip-move (github.com/joshwcomeau/react-flip-move)\n * - Framer Motion popLayout mode\n *\n * Key changes from old implementation:\n * 1. NO display:none - use position:absolute directly\n * 2. Double RAF for reliable reflow waiting\n * 3. Correct position calculation using getBoundingClientRect\n * 4. Sequential FLIP: capture AFTER only after reflow settles\n */\n const startExit = useCallback(async (id: string, options?: ExitOptions): Promise<void> => {\n const el = registry.get(id);\n\n if (__DEBUG__) {\n logEventRef.current({\n type: 'reorder-exit-check',\n source: 'useAnimationOrchestrator.startExit',\n message: `Exit check for ${id}: element=${!!el}, alreadyAnimating=${stateRef.current.animatingIds.has(id)}`,\n data: {\n id,\n elementFound: !!el,\n alreadyAnimating: stateRef.current.animatingIds.has(id),\n registrySize: registry.size,\n animatingIds: Array.from(stateRef.current.animatingIds),\n flipBehavior,\n exitPositionStrategy\n }\n });\n }\n\n if (!el || stateRef.current.animatingIds.has(id)) {\n if (__DEBUG__ && stateRef.current.animatingIds.has(id)) {\n logEventRef.current({\n type: 'reorder-exit-rejected',\n source: 'useAnimationOrchestrator.startExit',\n message: `Exit rejected for ${id} - already animating`,\n data: {\n id,\n reason: 'already-animating',\n currentAnimatingIds: Array.from(stateRef.current.animatingIds)\n }\n });\n }\n return;\n }\n\n stateRef.current.animatingIds.add(id);\n\n const duration = options?.duration ?? exitDuration;\n const easing = options?.easing ?? exitEasing;\n\n // ========================================\n // STEP 1: FIRST - Capture ALL positions BEFORE any DOM/CSS changes\n // CRITICAL: Must capture BEFORE setting data attributes!\n // CSS rule [data-reorder-state=\"exiting\"][data-exit-position=\"absolute\"]\n // would trigger position:absolute and cause reflow before we measure\n // ========================================\n const allElements = registry.getAll();\n const beforePositions = new Map<string, DOMRect>();\n\n // Capture position of exiting element FIRST\n const exitingRect = el.getBoundingClientRect();\n beforePositions.set(id, exitingRect);\n\n // Capture positions of all siblings\n allElements.forEach((element, elemId) => {\n if (elemId !== id) {\n const rect = element.getBoundingClientRect();\n if (rect.width > 0 && rect.height > 0) {\n beforePositions.set(elemId, rect);\n }\n }\n });\n\n const parent = el.parentElement;\n\n // NOW set data attributes (this triggers CSS position:absolute)\n el.dataset.reorderState = 'exiting';\n if (exitPositionStrategy === 'absolute-fixed') {\n el.dataset.exitPosition = 'absolute';\n }\n\n if (__DEBUG__) {\n logEventRef.current({\n type: 'reorder-flip-before-capture',\n source: 'useAnimationOrchestrator.startExit',\n message: `FIRST: All positions captured including exiting element`,\n data: {\n exitingId: id,\n exitingPosition: { x: exitingRect.left, y: exitingRect.top },\n siblingCount: beforePositions.size - 1,\n positions: Object.fromEntries([...beforePositions].map(([k, v]) => [k, { x: v.left, y: v.top }]))\n }\n });\n }\n\n // ========================================\n // STEP 2: Apply position:absolute to exiting element\n // Calculate position relative to parent BEFORE it re-centers\n // ========================================\n const parentRectBefore = parent?.getBoundingClientRect() || { left: 0, top: 0 };\n const absoluteLeft = exitingRect.left - parentRectBefore.left;\n const absoluteTop = exitingRect.top - parentRectBefore.top;\n\n if (exitPositionStrategy === 'absolute-fixed') {\n el.style.position = 'absolute';\n el.style.left = `${absoluteLeft}px`;\n el.style.top = `${absoluteTop}px`;\n el.style.width = `${exitingRect.width}px`;\n el.style.height = `${exitingRect.height}px`;\n el.style.margin = '0';\n }\n\n // Capture parent position AFTER setting absolute (parent may re-center)\n const parentRectAfter = parent?.getBoundingClientRect() || { left: 0, top: 0 };\n const parentDeltaX = parentRectAfter.left - parentRectBefore.left;\n const parentDeltaY = parentRectAfter.top - parentRectBefore.top;\n\n // Compensate for parent movement with transform\n if (Math.abs(parentDeltaX) > 0.5 || Math.abs(parentDeltaY) > 0.5) {\n el.style.transform = `translate(${-parentDeltaX}px, ${-parentDeltaY}px)`;\n }\n\n if (__DEBUG__) {\n logEventRef.current({\n type: 'reorder-position-absolute',\n source: 'useAnimationOrchestrator.startExit',\n message: `Position absolute with parent compensation`,\n data: {\n id,\n position: { left: absoluteLeft, top: absoluteTop },\n parentDelta: { x: parentDeltaX, y: parentDeltaY },\n width: exitingRect.width,\n height: exitingRect.height\n }\n });\n }\n\n // ========================================\n // STEP 3: SYNCHRONOUS reflow - NO RAF!\n // getBoundingClientRect forces synchronous layout recalculation\n // This MUST happen before any paint to avoid visual jumping\n // ========================================\n // Force synchronous reflow by reading layout\n // This ensures siblings have moved BEFORE we apply transforms\n // and BEFORE browser paints - user never sees the jump\n\n // ========================================\n // STEP 4: LAST - Capture AFTER positions IMMEDIATELY (same frame!)\n // ========================================\n const afterPositions = new Map<string, DOMRect>();\n allElements.forEach((element, elemId) => {\n if (elemId !== id) {\n // getBoundingClientRect forces synchronous layout\n const rect = element.getBoundingClientRect();\n if (rect.width > 0 && rect.height > 0) {\n afterPositions.set(elemId, rect);\n }\n }\n });\n\n if (__DEBUG__) {\n logEventRef.current({\n type: 'reorder-flip-after-positions',\n source: 'useAnimationOrchestrator.startExit',\n message: `LAST: After positions captured (synchronous)`,\n data: {\n captureSource: 'synchronous-reflow',\n capturedIds: [...afterPositions.keys()],\n positions: Object.fromEntries([...afterPositions].map(([k, v]) => [k, { x: v.left, y: v.top }])),\n perfNow: performance.now()\n }\n });\n }\n\n // ========================================\n // STEP 5: INVERT + PLAY - Calculate deltas and animate siblings\n // ========================================\n const flipAnimations: Animation[] = [];\n\n if (flipBehavior !== 'none') {\n // Determine which siblings to animate based on flipBehavior\n let siblingsToAnimate: string[] = [];\n\n if (flipBehavior === 'all') {\n siblingsToAnimate = [...afterPositions.keys()];\n } else if (flipBehavior === 'siblings-after') {\n // Get elements sorted by ACTUAL DOM order using compareDocumentPosition\n const elementsWithIds = Array.from(allElements.entries());\n elementsWithIds.sort((a, b) => {\n const posA = a[1];\n const posB = b[1];\n const position = posA.compareDocumentPosition(posB);\n return position & Node.DOCUMENT_POSITION_FOLLOWING ? -1 : 1;\n });\n\n const sortedKeys = elementsWithIds.map(([k]) => k);\n const exitingIndex = sortedKeys.indexOf(id);\n\n if (exitingIndex !== -1) {\n siblingsToAnimate = sortedKeys.slice(exitingIndex + 1).filter(k => afterPositions.has(k));\n }\n\n if (__DEBUG__) {\n logEventRef.current({\n type: 'reorder-flip-filter',\n source: 'useAnimationOrchestrator.startExit',\n message: `Filtered to siblings-after (DOM order)`,\n data: {\n exitingIndex,\n sortedKeys,\n siblingsToAnimate,\n filteredCount: siblingsToAnimate.length\n }\n });\n }\n }\n\n // Apply FLIP to each sibling - only those with significant deltas will animate\n siblingsToAnimate.forEach(siblingId => {\n const before = beforePositions.get(siblingId);\n const after = afterPositions.get(siblingId);\n\n if (!before || !after) return;\n\n // Calculate delta: FIRST - LAST (how much to \"invert\" to get back to first)\n const deltaX = before.left - after.left;\n const deltaY = before.top - after.top;\n\n // Only animate if movement is significant\n const isSignificant = Math.abs(deltaX) > 1 || Math.abs(deltaY) > 1;\n\n if (__DEBUG__) {\n logEventRef.current({\n type: 'reorder-flip-delta',\n source: 'useAnimationOrchestrator.startExit',\n message: `Delta for ${siblingId}: (${deltaX.toFixed(1)}, ${deltaY.toFixed(1)}) significant=${isSignificant}`,\n position: { element: siblingId, x: after.left, y: after.top, width: after.width, height: after.height, delta: { x: deltaX, y: deltaY } },\n data: {\n id: siblingId,\n deltaX,\n deltaY,\n isSignificant\n }\n });\n }\n\n if (isSignificant) {\n const siblingEl = allElements.get(siblingId);\n if (siblingEl) {\n siblingEl.dataset.reorderState = 'flipping';\n\n // CRITICAL: Apply initial transform SYNCHRONOUSLY via CSS\n // This ensures the element appears in \"before\" position on first paint\n // element.animate() does NOT apply initial keyframe synchronously!\n siblingEl.style.transform = `translate(${deltaX}px, ${deltaY}px)`;\n\n // Now animate FROM current transform TO none\n const anim = siblingEl.animate([\n { transform: `translate(${deltaX}px, ${deltaY}px)` },\n { transform: 'none' }\n ], {\n duration: flipDuration,\n easing: EASINGS.MATERIAL_DECELERATE,\n fill: 'forwards'\n });\n\n flipAnimations.push(anim);\n\n // Reset state and clear inline transform when animation completes\n anim.finished.then(() => {\n siblingEl.style.transform = '';\n siblingEl.dataset.reorderState = 'idle';\n }).catch(() => {\n siblingEl.style.transform = '';\n siblingEl.dataset.reorderState = 'idle';\n });\n\n if (__DEBUG__) {\n logEventRef.current({\n type: 'reorder-transform-applied',\n source: 'useAnimationOrchestrator.startExit',\n message: `FLIP animation started for ${siblingId}`,\n data: {\n element: siblingId,\n transform: `translate(${deltaX}px, ${deltaY}px)`,\n deltaX,\n deltaY,\n perfNow: performance.now()\n }\n });\n }\n }\n }\n });\n }\n\n // ========================================\n // STEP 6: Animate exiting element (fade out)\n // ========================================\n const exitAnimations: Animation[] = [];\n const tokens = el.querySelectorAll('.sliding-text-token');\n\n if (__DEBUG__) {\n logEventRef.current({\n type: 'reorder-animation-setup',\n source: 'useAnimationOrchestrator.startExit',\n message: `Exit animation setup for ${id}`,\n data: {\n id,\n tokensCount: tokens.length,\n duration,\n easing,\n flipAnimationsCount: flipAnimations.length\n }\n });\n }\n\n if (tokens.length > 0) {\n tokens.forEach((token, index) => {\n const delay = index * TIMING.EXIT_STAGGER;\n const anim = (token as HTMLElement).animate([\n { opacity: 1, transform: 'translateY(0) scale(1)', filter: 'blur(0px)' },\n {\n opacity: 0,\n transform: `translateY(${TRANSFORMS.OFFSET_Y_EXIT}px) scale(${TRANSFORMS.SCALE_EXIT})`,\n filter: `blur(${EFFECTS.BLUR_EXIT}px)`\n }\n ], {\n duration,\n easing,\n delay,\n fill: 'forwards'\n });\n exitAnimations.push(anim);\n });\n } else {\n const anim = el.animate([\n { opacity: 1, transform: 'scale(1)' },\n { opacity: 0, transform: `scale(${TRANSFORMS.SCALE_EXIT})` }\n ], {\n duration,\n easing,\n fill: 'forwards'\n });\n exitAnimations.push(anim);\n }\n\n stateRef.current.activeAnimations.set(id, [...exitAnimations, ...flipAnimations]);\n\n // ========================================\n // STEP 7: Wait for all animations to complete\n // ========================================\n try {\n if (__DEBUG__) {\n logEventRef.current({\n type: 'reorder-await-start',\n source: 'useAnimationOrchestrator.startExit',\n message: `Waiting for exit + FLIP animations`,\n data: {\n id,\n exitAnimationsCount: exitAnimations.length,\n flipAnimationsCount: flipAnimations.length\n }\n });\n }\n\n // Wait for all animations (exit + FLIP) to complete\n await Promise.all([\n ...exitAnimations.map(a => a.finished),\n ...flipAnimations.map(a => a.finished)\n ]);\n\n if (__DEBUG__) {\n logEventRef.current({\n type: 'reorder-await-complete',\n source: 'useAnimationOrchestrator.startExit',\n message: `All animations completed for ${id}`,\n data: {\n id,\n elementStillConnected: el.isConnected\n }\n });\n }\n\n } catch (error) {\n if (__DEBUG__) {\n logEventRef.current({\n type: 'reorder-animation-error',\n source: 'useAnimationOrchestrator.startExit',\n message: `Animation error/cancelled for ${id}`,\n data: {\n id,\n error: String(error),\n elementStillConnected: el.isConnected\n }\n });\n }\n stateRef.current.animatingIds.delete(id);\n stateRef.current.activeAnimations.delete(id);\n return;\n }\n\n // ========================================\n // STEP 8: Cleanup\n // ========================================\n stateRef.current.animatingIds.delete(id);\n stateRef.current.activeAnimations.delete(id);\n\n el.dataset.reorderState = 'completed';\n\n // Clean up inline styles\n if (exitPositionStrategy === 'absolute-fixed') {\n el.style.removeProperty('position');\n el.style.removeProperty('left');\n el.style.removeProperty('top');\n el.style.removeProperty('width');\n el.style.removeProperty('height');\n el.style.removeProperty('margin');\n }\n\n delete el.dataset.exitPosition;\n registry.unregister(id);\n\n if (__DEBUG__) {\n logEventRef.current({\n type: 'reorder-exit-cleanup',\n source: 'useAnimationOrchestrator.startExit',\n message: `Exit complete for ${id}`,\n data: {\n id,\n finalState: el.dataset.reorderState\n }\n });\n }\n\n options?.onComplete?.();\n configRef.current?.onExitComplete?.(id);\n }, [registry, exitDuration, exitEasing, flipDuration, flipBehavior, exitPositionStrategy]);\n\n /**\n * Start enter animation for an element\n *\n * Architecture: CSS + WAAPI\n * - Assumes element already has data-reorder-state=\"entering\" (set by Reorder.tsx)\n * - CSS [data-reorder-state=\"entering\"] { opacity: 0; transform: translateY(-8px) scale(0.95); }\n * - WAAPI animates FROM that state TO visible\n * - Sets data-reorder-state=\"idle\" on completion (CSS handles final state)\n */\n const startEnter = useCallback(async (id: string, options?: EnterOptions): Promise<void> => {\n const el = registry.get(id);\n if (!el) {\n if (__DEBUG__) {\n logEventRef.current({\n type: 'reorder-animation-error',\n source: 'useAnimationOrchestrator.startEnter',\n message: `Element not found for ${id}`,\n data: { id, registrySize: registry.size }\n });\n }\n return;\n }\n\n // Element should already have data-reorder-state=\"entering\" from Reorder.tsx\n // CSS hides it. If not set, set it now (fallback)\n if (!el.dataset.reorderState) {\n el.dataset.reorderState = 'entering';\n }\n\n const enterDuration = configRef.current?.enterDuration ?? TIMING.ENTER_DURATION;\n const enterEasing = configRef.current?.enterEasing ?? EASINGS.MATERIAL_DECELERATE;\n\n const duration = options?.duration ?? enterDuration;\n const easing = options?.easing ?? enterEasing;\n const stagger = options?.stagger ?? TIMING.ENTER_STAGGER;\n\n if (__DEBUG__) {\n logEventRef.current({\n type: 'reorder-enter-start',\n source: 'useAnimationOrchestrator.startEnter',\n message: `Enter animation for ${id}`,\n data: {\n id,\n duration,\n easing,\n stagger,\n currentState: el.dataset.reorderState\n }\n });\n }\n\n const tokens = el.querySelectorAll('.sliding-text-token');\n const animations: Animation[] = [];\n\n // Animation keyframes match CSS entering state → visible\n // CSS: opacity: 0, transform: translateY(-8px) scale(0.95)\n const fromKeyframe = {\n opacity: 0,\n transform: 'translateY(-8px) scale(0.95)'\n };\n const toKeyframe = {\n opacity: 1,\n transform: 'none'\n };\n\n if (tokens.length > 0) {\n tokens.forEach((token, index) => {\n const delay = index * stagger;\n (token as HTMLElement).dataset.reorderIndex = String(index);\n\n const anim = (token as HTMLElement).animate([\n { ...fromKeyframe, filter: `blur(${EFFECTS.BLUR_ENTER}px)` },\n { ...toKeyframe, filter: 'blur(0px)' }\n ], {\n duration,\n easing,\n delay,\n fill: 'forwards'\n });\n animations.push(anim);\n });\n } else {\n const anim = el.animate([fromKeyframe, toKeyframe], {\n duration,\n easing,\n fill: 'forwards'\n });\n animations.push(anim);\n }\n\n try {\n await Promise.all(animations.map(a => a.finished));\n } catch {\n // Animation cancelled\n el.dataset.reorderState = 'idle';\n return;\n }\n\n // Animation complete - CSS idle state takes over\n el.dataset.reorderState = 'idle';\n\n if (__DEBUG__) {\n logEventRef.current({\n type: 'reorder-enter-complete',\n source: 'useAnimationOrchestrator.startEnter',\n message: `Enter complete for ${id}`,\n data: { id, finalState: 'idle' }\n });\n }\n\n options?.onComplete?.();\n configRef.current?.onEnterComplete?.(id);\n }, [registry]);\n\n const isAnimating = useCallback((id?: string): boolean => {\n if (id) return stateRef.current.animatingIds.has(id);\n return stateRef.current.animatingIds.size > 0;\n }, []);\n\n const capturePositions = useCallback((excludeIds?: Set<string>) => {\n return positions.capture(excludeIds);\n }, [positions]);\n\n useEffect(() => {\n return () => {\n cancelAllAnimations();\n };\n }, [cancelAllAnimations]);\n\n return {\n registry,\n positions,\n flip,\n registerElement: registry.register,\n startExit,\n startEnter,\n isAnimating,\n cancelAnimation,\n cancelAllAnimations,\n capturePositions\n };\n}\n","import { useRef, useEffect } from 'react';\nimport { useAnimationOrchestrator } from '../../core/useAnimationOrchestrator';\nimport { useDebug } from '../../contexts/DebugContext';\nimport type { UseReorderReturn, UseReorderConfig } from './types';\n\n/**\n * Hook for managing reorderable lists with FLIP animations.\n *\n * Architecture: Thin wrapper around useAnimationOrchestrator\n * - Delegates all animation logic to orchestrator\n * - Provides stable API surface for Reorder component\n *\n * @example\n * ```tsx\n * const reorder = useReorder({\n * onComplete: (id) => removeFromList(id)\n * });\n *\n * // Register elements:\n * <div ref={el => reorder.registerElement('item-1', el)}>\n * Item 1\n * </div>\n *\n * // Trigger exit:\n * await reorder.startItemExit('item-1');\n * ```\n */\nexport function useReorder(config?: UseReorderConfig): UseReorderReturn {\n const { logEvent } = useDebug();\n const logEventRef = useRef(logEvent);\n useEffect(() => {\n logEventRef.current = logEvent;\n }, [logEvent]);\n\n const orchestrator = useAnimationOrchestrator({\n enterDuration: config?.enterDuration,\n exitDuration: config?.exitDuration,\n flipDuration: config?.flipDuration,\n enterEasing: config?.enterEasing,\n exitEasing: config?.exitEasing,\n flipEasing: config?.flipEasing,\n flipBehavior: config?.flipBehavior,\n onExitComplete: config?.onComplete\n });\n\n useEffect(() => {\n if (__DEBUG__) {\n logEventRef.current({\n type: 'render',\n source: 'useReorder.mount',\n message: 'useReorder hook mounted',\n data: {\n config: {\n enterDuration: config?.enterDuration,\n exitDuration: config?.exitDuration,\n flipDuration: config?.flipDuration\n }\n }\n });\n }\n\n return () => {\n if (__DEBUG__) {\n logEventRef.current({\n type: 'component-unmounted',\n source: 'useReorder.unmount',\n message: 'useReorder hook unmounting',\n data: {\n registrySize: orchestrator.registry.size\n }\n });\n }\n };\n }, []);\n\n // Return orchestrator methods with stable API names\n return {\n ...orchestrator,\n registerElement: orchestrator.registerElement,\n startItemExit: orchestrator.startExit,\n startItemEnter: orchestrator.startEnter\n };\n}\n","import {\n useRef,\n useCallback,\n useMemo,\n useLayoutEffect,\n useEffect,\n Children,\n isValidElement,\n cloneElement,\n type ReactNode,\n type ReactElement\n} from 'react';\nimport { useReorder } from './useReorder';\nimport { useDebug } from '../../contexts/DebugContext';\nimport type {\n UseReorderPresenceConfig,\n UseReorderPresenceReturn\n} from './types';\n\n/**\n * Hook for managing presence animations in reorderable lists\n *\n * IMPORTANT: This hook follows the AnimatedTokens pattern where:\n * - Elements stay in DOM until animation completes\n * - Consumer must keep items in their state until onAnimationComplete fires\n * - No \"ghost\" cloning - animations run on actual DOM elements\n *\n * @example\n * ```tsx\n * const [items, setItems] = useState(INITIAL_ITEMS);\n *\n * const { presentChildren, triggerExit } = useReorderPresence(\n * items.map(item => <div key={item.id}>{item.name}</div>),\n * {\n * onAnimationComplete: (id) => {\n * // Only remove from state AFTER animation completes\n * setItems(prev => prev.filter(item => item.id !== id));\n * }\n * }\n * );\n *\n * const handleDelete = (id: string) => {\n * // DON'T remove from state - just trigger animation\n * triggerExit(id);\n * };\n * ```\n */\nexport function useReorderPresence(\n children: ReactNode,\n config: UseReorderPresenceConfig = {}\n): UseReorderPresenceReturn {\n const {\n autoAnimate = true,\n stagger,\n enterDuration,\n exitDuration,\n flipDuration,\n enterEasing,\n exitEasing,\n flipEasing\n } = config;\n\n // Debug context - uses ref pattern to avoid recreating callbacks\n const { logEvent } = useDebug();\n const logEventRef = useRef(logEvent);\n useEffect(() => {\n logEventRef.current = logEvent;\n }, [logEvent]);\n\n // Config ref pattern\n const configRef = useRef(config);\n useEffect(() => {\n configRef.current = config;\n }, [config]);\n\n // Track animating IDs via REF (not state!) - like AnimatedTokens\n const exitingIdsRef = useRef<Set<string>>(new Set());\n const enteringIdsRef = useRef<Set<string>>(new Set());\n\n // Track previous children keys for change detection\n const prevKeysRef = useRef<Set<string>>(new Set());\n\n // Helper to calculate stagger delay\n const getStaggerDelay = useCallback((index: number, type: 'enter' | 'exit'): number => {\n if (!stagger) return 0;\n if (typeof stagger === 'number') return stagger * index;\n return (stagger[type] ?? 0) * index;\n }, [stagger]);\n\n // Stable completion handler - calls consumer's onAnimationComplete\n const handleAnimationComplete = useCallback((id: string) => {\n if (__DEBUG__) {\n logEventRef.current({\n type: 'reorder-animation-complete',\n source: 'useReorderPresence.handleAnimationComplete',\n message: `Animation complete, notifying consumer: ${id}`,\n data: { id, remainingExiting: exitingIdsRef.current.size - 1 }\n });\n }\n\n exitingIdsRef.current.delete(id);\n configRef.current.onAnimationComplete?.(id);\n }, []);\n\n // Create reorder instance with completion handler\n const reorder = useReorder({\n enterDuration,\n exitDuration,\n flipDuration,\n enterEasing,\n exitEasing,\n flipEasing,\n onComplete: handleAnimationComplete\n });\n\n // Extract current children keys\n const currentKeys = useMemo(() => {\n const keys = new Set<string>();\n Children.forEach(children, child => {\n if (isValidElement(child) && child.key != null) {\n keys.add(String(child.key));\n }\n });\n return keys;\n }, [children]);\n\n // Detect and process changes - following AnimatedTokens pattern\n useLayoutEffect(() => {\n const prevKeys = prevKeysRef.current;\n\n // Detect additions (in current but not in prev)\n const added: string[] = [];\n currentKeys.forEach(key => {\n if (!prevKeys.has(key) && !enteringIdsRef.current.has(key)) {\n added.push(key);\n }\n });\n\n // Note: We DON'T detect removals here anymore!\n // Removals are triggered manually via triggerExit()\n // The element stays in children until consumer removes it in onAnimationComplete\n\n // Log detected changes\n if (__DEBUG__ && added.length > 0) {\n logEventRef.current({\n type: 'token-reorder',\n source: 'useReorderPresence.detectChanges',\n message: `Detected ${added.length} additions`,\n data: {\n added,\n currentCount: currentKeys.size,\n previousCount: prevKeys.size,\n autoAnimate,\n stagger\n }\n });\n }\n\n // Process additions - track entering, run WAAPI animation with stagger\n if (added.length > 0) {\n added.forEach((key, index) => {\n const delay = getStaggerDelay(index, 'enter');\n\n const processEnter = () => {\n enteringIdsRef.current.add(key);\n configRef.current.onItemEnter?.(key);\n\n if (__DEBUG__) {\n logEventRef.current({\n type: 'reorder-item-enter',\n source: 'useReorderPresence.processAdditions',\n message: `Processing enter for: ${key}`,\n timing: {\n startTime: performance.now(),\n expectedDuration: configRef.current.enterDuration ?? 300\n },\n data: { key, index, enteringCount: enteringIdsRef.current.size }\n });\n }\n\n // Start WAAPI enter animation (deferred to allow DOM to mount)\n requestAnimationFrame(() => {\n reorder.startItemEnter(key).then(() => {\n enteringIdsRef.current.delete(key);\n\n if (__DEBUG__) {\n logEventRef.current({\n type: 'reorder-animation-complete',\n source: 'useReorderPresence.processAdditions',\n message: `Enter animation finished: ${key}`,\n data: { key, remainingEntering: enteringIdsRef.current.size }\n });\n }\n });\n });\n };\n\n if (delay > 0) {\n setTimeout(processEnter, delay);\n } else {\n processEnter();\n }\n });\n }\n\n // Update prev keys for next comparison\n prevKeysRef.current = new Set(currentKeys);\n }, [currentKeys, autoAnimate, reorder, getStaggerDelay]);\n\n // Manual exit trigger - consumer calls this BEFORE removing from state\n const triggerExit = useCallback((id: string) => {\n // Already exiting? Skip\n if (exitingIdsRef.current.has(id)) {\n if (__DEBUG__) {\n logEventRef.current({\n type: 'reorder-item-exit',\n source: 'useReorderPresence.triggerExit',\n message: `Exit already in progress: ${id}`,\n data: { id, reason: 'already-exiting' }\n });\n }\n return;\n }\n\n // Check element is registered\n if (!reorder.registry.has(id)) {\n if (__DEBUG__) {\n logEventRef.current({\n type: 'reorder-item-exit',\n source: 'useReorderPresence.triggerExit',\n message: `Element not registered: ${id}`,\n data: { id, reason: 'not-registered' }\n });\n }\n return;\n }\n\n // Mark as exiting\n exitingIdsRef.current.add(id);\n configRef.current.onItemExit?.(id);\n\n if (__DEBUG__) {\n logEventRef.current({\n type: 'reorder-item-exit',\n source: 'useReorderPresence.triggerExit',\n message: `Exit triggered: ${id}`,\n timing: {\n startTime: performance.now(),\n expectedDuration: configRef.current.exitDuration ?? 200\n },\n data: { id, exitingCount: exitingIdsRef.current.size }\n });\n }\n\n // Start exit animation on the EXISTING element (already in DOM)\n reorder.startItemExit(id);\n }, [reorder]);\n\n // State queries\n const isExiting = useCallback((id: string): boolean => {\n return exitingIdsRef.current.has(id);\n }, []);\n\n const isEntering = useCallback((id: string): boolean => {\n return enteringIdsRef.current.has(id);\n }, []);\n\n const getExitingIds = useCallback((): string[] => {\n return Array.from(exitingIdsRef.current);\n }, []);\n\n const getEnteringIds = useCallback((): string[] => {\n return Array.from(enteringIdsRef.current);\n }, []);\n\n // presentChildren - just the children as-is, with data attribute for exiting\n const presentChildren = useMemo(() => {\n const result: ReactNode[] = [];\n\n Children.forEach(children, child => {\n if (!isValidElement(child)) {\n result.push(child);\n return;\n }\n\n const key = child.key != null ? String(child.key) : null;\n const isCurrentlyExiting = key != null && exitingIdsRef.current.has(key);\n\n // Clone to add data attribute for exiting state (CSS can use this)\n if (isCurrentlyExiting) {\n result.push(\n cloneElement(child as ReactElement<{ 'data-reorder-state'?: string }>, {\n 'data-reorder-state': 'exiting'\n })\n );\n } else {\n result.push(child);\n }\n });\n\n return result;\n }, [children]);\n\n // Mount/unmount logging\n useEffect(() => {\n if (__DEBUG__) {\n logEventRef.current({\n type: 'render',\n source: 'useReorderPresence.mount',\n message: 'useReorderPresence hook mounted',\n data: {\n childCount: currentKeys.size,\n autoAnimate,\n stagger,\n config: { enterDuration, exitDuration, flipDuration }\n }\n });\n }\n\n return () => {\n if (__DEBUG__) {\n logEventRef.current({\n type: 'component-unmounted',\n source: 'useReorderPresence.unmount',\n message: 'useReorderPresence hook unmounting',\n data: {\n exitingCount: exitingIdsRef.current.size,\n enteringCount: enteringIdsRef.current.size\n }\n });\n }\n };\n }, []);\n\n return {\n presentChildren,\n triggerExit,\n isExiting,\n isEntering,\n exitingIds: getExitingIds(),\n enteringIds: getEnteringIds(),\n reorder\n };\n}\n","import React, {\n useMemo,\n useState,\n useEffect,\n useRef,\n useCallback,\n useImperativeHandle,\n Children,\n isValidElement,\n cloneElement,\n forwardRef,\n type CSSProperties,\n type ReactNode,\n type ReactElement\n} from 'react';\nimport { useReorder } from './useReorder';\nimport { useDebug } from '../../contexts/DebugContext';\nimport type { ReorderProps, DurationConfig, ReorderLayout } from './types';\n\nfunction getDuration(config: DurationConfig | undefined, type: 'enter' | 'exit'): number | undefined {\n if (config === undefined) return undefined;\n if (typeof config === 'number') return config;\n return config[type];\n}\n\nconst layoutStyles: Record<ReorderLayout, CSSProperties> = {\n auto: { position: 'relative' },\n horizontal: { display: 'flex', flexDirection: 'row', flexWrap: 'wrap', alignItems: 'center', position: 'relative' },\n vertical: { display: 'flex', flexDirection: 'column', position: 'relative' },\n grid: { display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(100px, 1fr))', gap: '8px', position: 'relative' }\n};\n\ninterface ChildRecord {\n key: string;\n element: ReactElement;\n}\n\n/**\n * Agnostic container for reorderable items with FLIP animations.\n *\n * Architecture: CSS + WAAPI (no React state for animation tracking)\n * - CSS handles initial states ([data-reorder-state=\"entering\"] starts hidden)\n * - WAAPI handles all animations\n * - React only manages displayChildren (what's in DOM)\n *\n * @example\n * ```tsx\n * <Reorder\n * layout=\"horizontal\"\n * onItemExit={(id) => handleRemove(id)}\n * >\n * {items.map(item => (\n * <div key={item.id}>{item.name}</div>\n * ))}\n * </Reorder>\n * ```\n */\nconst ReorderRoot = forwardRef<{\n startItemExit: (id: string) => Promise<void>;\n startItemEnter: (id: string) => Promise<void>;\n isAnimating: (id?: string) => boolean;\n}, ReorderProps>(({\n children,\n autoAnimate: _autoAnimate = true,\n stagger,\n duration,\n layout = 'auto',\n className = '',\n flipBehavior,\n exitPositionStrategy,\n onItemExit,\n onItemEnter\n}, ref) => {\n void _autoAnimate;\n const { logEvent } = useDebug();\n const logEventRef = useRef(logEvent);\n useEffect(() => {\n logEventRef.current = logEvent;\n }, [logEvent]);\n\n // Config refs to avoid recreating callbacks\n const onItemExitRef = useRef(onItemExit);\n const onItemEnterRef = useRef(onItemEnter);\n useEffect(() => {\n onItemExitRef.current = onItemExit;\n onItemEnterRef.current = onItemEnter;\n }, [onItemExit, onItemEnter]);\n\n // Completion handler - ONLY removes from displayChildren\n // This is called by orchestrator when exit animation finishes\n const handleAnimationComplete = useCallback((id: string) => {\n if (__DEBUG__) {\n logEventRef.current({\n type: 'reorder-animation-complete',\n source: 'Reorder.handleAnimationComplete',\n message: `Exit complete, removing from DOM: ${id}`,\n data: { id }\n });\n }\n setDisplayChildren(prev => prev.filter(child => child.key !== id));\n }, []);\n\n // Low-level reorder hook\n const reorder = useReorder({\n enterDuration: getDuration(duration, 'enter'),\n exitDuration: getDuration(duration, 'exit'),\n flipBehavior,\n exitPositionStrategy,\n onComplete: handleAnimationComplete\n });\n\n // Expose reorder methods via ref\n useImperativeHandle(ref, () => ({\n startItemExit: reorder.startItemExit,\n startItemEnter: reorder.startItemEnter,\n isAnimating: reorder.isAnimating\n }), [reorder.startItemExit, reorder.startItemEnter, reorder.isAnimating]);\n\n // Store reorder functions in refs for stable callbacks\n const startExitRef = useRef(reorder.startItemExit);\n const startEnterRef = useRef(reorder.startItemEnter);\n useEffect(() => {\n startExitRef.current = reorder.startItemExit;\n startEnterRef.current = reorder.startItemEnter;\n }, [reorder.startItemExit, reorder.startItemEnter]);\n\n // Extract current children as records\n const currentChildren = useMemo<ChildRecord[]>(() => {\n const records: ChildRecord[] = [];\n Children.forEach(children, child => {\n if (isValidElement(child) && child.key != null) {\n records.push({\n key: String(child.key),\n element: child as ReactElement\n });\n }\n });\n return records;\n }, [children]);\n\n // Internal display state - ONLY state we track\n // This is the single source of truth for what's in the DOM\n const [displayChildren, setDisplayChildren] = useState<ChildRecord[]>(currentChildren);\n\n // Track which keys are currently exiting (to avoid re-triggering)\n // This is a ref, not state - no re-renders needed\n const exitingKeysRef = useRef<Set<string>>(new Set());\n\n // Calculate stagger delay\n const getStaggerDelay = useCallback((index: number, type: 'enter' | 'exit'): number => {\n if (!stagger) return 0;\n if (typeof stagger === 'number') return stagger * index;\n return (stagger[type] ?? 0) * index;\n }, [stagger]);\n\n // Detect changes and sync displayChildren\n useEffect(() => {\n const currentKeys = new Set(currentChildren.map(c => c.key));\n const displayKeys = new Set(displayChildren.map(c => c.key));\n\n // Removals: in display but not in current, and not already exiting\n const removed = displayChildren.filter(c =>\n !currentKeys.has(c.key) && !exitingKeysRef.current.has(c.key)\n );\n\n // Additions: in current but not in display\n const added = currentChildren.filter(c => !displayKeys.has(c.key));\n\n // No structural changes - check for content updates\n if (removed.length === 0 && added.length === 0) {\n const hasUpdates = currentChildren.some((c) => {\n const displayChild = displayChildren.find(dc => dc.key === c.key);\n return displayChild && displayChild.element !== c.element;\n });\n\n if (hasUpdates) {\n setDisplayChildren(prev => prev.map(dc => {\n if (exitingKeysRef.current.has(dc.key)) return dc;\n const currentChild = currentChildren.find(c => c.key === dc.key);\n return currentChild ?? dc;\n }));\n }\n return;\n }\n\n if (__DEBUG__) {\n logEventRef.current({\n type: 'token-reorder',\n source: 'Reorder.detectChanges',\n message: `Detected ${removed.length} removals, ${added.length} additions`,\n data: {\n removed: removed.map(c => c.key),\n added: added.map(c => c.key),\n currentCount: currentChildren.length,\n displayCount: displayChildren.length\n }\n });\n }\n\n // Handle removals - start exit animations\n // Don't remove from displayChildren yet - orchestrator.onComplete does that\n removed.forEach((child, index) => {\n const delay = getStaggerDelay(index, 'exit');\n exitingKeysRef.current.add(child.key);\n\n const processExit = () => {\n onItemExitRef.current?.(child.key);\n\n if (__DEBUG__) {\n logEventRef.current({\n type: 'reorder-item-exit',\n source: 'Reorder.processRemovals',\n message: `Exit triggered: ${child.key}`,\n data: { key: child.key }\n });\n }\n\n // Start exit animation - orchestrator sets data-reorder-state=\"exiting\"\n startExitRef.current(child.key).finally(() => {\n exitingKeysRef.current.delete(child.key);\n });\n };\n\n if (delay > 0) {\n setTimeout(processExit, delay);\n } else {\n processExit();\n }\n });\n\n // Handle additions - merge into display\n if (added.length > 0) {\n // Merge: current children + exiting children at their original positions\n const exitingChildren = displayChildren.filter(c => !currentKeys.has(c.key));\n let result = [...currentChildren];\n\n exitingChildren.forEach(ec => {\n const oldIdx = displayChildren.findIndex(c => c.key === ec.key);\n if (oldIdx !== -1 && oldIdx <= result.length) {\n result.splice(oldIdx, 0, ec);\n }\n });\n\n setDisplayChildren(result);\n\n // Enter animations are started in the ref callback when element mounts\n // This ensures the element exists in DOM before animation starts\n added.forEach((child, index) => {\n const delay = getStaggerDelay(index, 'enter');\n\n const processEnter = () => {\n onItemEnterRef.current?.(child.key);\n\n if (__DEBUG__) {\n logEventRef.current({\n type: 'reorder-item-enter',\n source: 'Reorder.processAdditions',\n message: `Enter scheduled: ${child.key}`,\n data: { key: child.key }\n });\n }\n };\n\n if (delay > 0) {\n setTimeout(processEnter, delay);\n } else {\n processEnter();\n }\n });\n }\n }, [currentChildren, displayChildren, getStaggerDelay]);\n\n // Build present children with data attributes and ref registration\n const presentChildren = useMemo<ReactNode[]>(() => {\n const currentKeys = new Set(currentChildren.map(c => c.key));\n\n return displayChildren.map(({ key, element }) => {\n // Determine if this is a new element (not yet animated in)\n const isNewElement = !currentKeys.has(key) ? false : true;\n // Check if currently exiting\n const isExiting = exitingKeysRef.current.has(key);\n\n const elementWithRef = cloneElement(element as ReactElement<any>, {\n ref: (el: HTMLElement | null) => {\n if (el) {\n // Register with reorder system\n reorder.registerElement(key, el);\n\n // Check current state from DOM\n const currentState = el.dataset.reorderState;\n\n // If no state yet and this is a new element, set entering and animate\n if (!currentState && isNewElement && !isExiting) {\n el.dataset.reorderState = 'entering';\n // CSS hides it, WAAPI will animate to visible\n startEnterRef.current(key);\n }\n } else {\n // Unregister on unmount\n reorder.registerElement(key, null);\n }\n\n // Preserve original ref\n const originalRef = (element as any).ref;\n if (typeof originalRef === 'function') {\n originalRef(el);\n } else if (originalRef && typeof originalRef === 'object') {\n (originalRef as React.MutableRefObject<HTMLElement | null>).current = el;\n }\n },\n 'data-reorder-id': key\n });\n\n return elementWithRef;\n });\n }, [displayChildren, currentChildren, reorder.registerElement]);\n\n const layoutClass = layout !== 'auto' ? `reorder--${layout}` : '';\n\n return (\n <div\n className={`waapi-reorder-container reorder ${layoutClass} ${className}`}\n style={layoutStyles[layout]}\n >\n {presentChildren}\n </div>\n );\n});\n\nReorderRoot.displayName = 'Reorder';\n\nexport { ReorderRoot as Reorder };\nexport type { ReorderProps } from './types';\n","import { useMemo } from 'react';\n\nexport type ListFormatType = 'conjunction' | 'disjunction' | 'unit';\nexport type ListFormatStyle = 'long' | 'short' | 'narrow';\n\n// Type declarations for Intl.ListFormat (ES2021+)\ninterface IntlListFormatPart {\n type: 'element' | 'literal';\n value: string;\n}\n\ninterface IntlListFormatOptions {\n type?: ListFormatType;\n style?: ListFormatStyle;\n}\n\ninterface IntlListFormat {\n format(list: string[]): string;\n formatToParts(list: string[]): IntlListFormatPart[];\n}\n\ninterface IntlListFormatConstructor {\n new (locale?: string | string[], options?: IntlListFormatOptions): IntlListFormat;\n}\n\ndeclare global {\n namespace Intl {\n const ListFormat: IntlListFormatConstructor;\n }\n}\n\nexport interface UseListFormatOptions {\n locale?: string;\n type?: ListFormatType;\n style?: ListFormatStyle;\n separator?: string;\n}\n\nexport interface ListPart {\n type: 'element' | 'literal';\n value: string;\n index: number;\n}\n\nconst getDefaultLocale = (): string => {\n if (typeof navigator !== 'undefined' && navigator.language) {\n return navigator.language;\n }\n return 'en';\n};\n\n/**\n * Hook for locale-aware list formatting using Intl.ListFormat\n *\n * Provides parts that can be rendered individually with animations.\n * Uses browser's Intl.ListFormat API for proper locale handling.\n *\n * @example\n * ```tsx\n * // Automatic locale detection\n * const parts = useListFormat(['Apple', 'Banana', 'Cherry']);\n * // en: [{ type: 'element', value: 'Apple' }, { type: 'literal', value: ', ' }, ...]\n *\n * // Spanish\n * const parts = useListFormat(['Apple', 'Banana', 'Cherry'], { locale: 'es' });\n * // es: [..., { type: 'literal', value: ' y ' }, { type: 'element', value: 'Cherry' }]\n *\n * // Disjunction (or)\n * const parts = useListFormat(['A', 'B', 'C'], { type: 'disjunction' });\n * // en: \"A, B, or C\"\n *\n * // Manual override\n * const parts = useListFormat(['A', 'B', 'C'], { separator: ' | ' });\n * // \"A | B | C\"\n * ```\n */\nexport function useListFormat(\n items: string[],\n options: UseListFormatOptions = {}\n): ListPart[] {\n const {\n locale,\n type = 'conjunction',\n style = 'long',\n separator\n } = options;\n\n return useMemo(() => {\n if (items.length === 0) return [];\n\n // Manual override mode: use fixed separator\n if (separator !== undefined) {\n const parts: ListPart[] = [];\n items.forEach((item, i) => {\n parts.push({ type: 'element', value: item, index: i });\n if (i < items.length - 1) {\n parts.push({ type: 'literal', value: separator, index: i });\n }\n });\n return parts;\n }\n\n // Use Intl.ListFormat for locale-aware formatting\n const resolvedLocale = locale ?? getDefaultLocale();\n\n try {\n const formatter = new Intl.ListFormat(resolvedLocale, { type, style });\n const formatted = formatter.formatToParts(items);\n\n let elementIndex = 0;\n return formatted.map((part): ListPart => {\n if (part.type === 'element') {\n return {\n type: 'element',\n value: part.value,\n index: elementIndex++\n };\n }\n // 'literal' type - separators\n return {\n type: 'literal',\n value: part.value,\n index: Math.max(0, elementIndex - 1)\n };\n });\n } catch {\n // Fallback if Intl.ListFormat fails (shouldn't happen in modern browsers)\n const parts: ListPart[] = [];\n items.forEach((item, i) => {\n parts.push({ type: 'element', value: item, index: i });\n if (i < items.length - 1) {\n parts.push({ type: 'literal', value: ', ', index: i });\n }\n });\n return parts;\n }\n }, [items, locale, type, style, separator]);\n}\n","import { useMemo, useRef, useEffect, useCallback } from 'react';\nimport { SlidingText } from './SlidingText';\nimport { SlidingNumber } from './SlidingNumber';\nimport { Reorder } from '../primitives/reorder';\nimport { useListFormat, type ListFormatType, type ListFormatStyle } from '../hooks/useListFormat';\nimport { PRESETS, ANIMATION_DEFAULTS, TIMING, TRANSFORMS, EASINGS } from '../utils/animationUtils';\nimport { useDebug } from '../contexts/DebugContext';\n\ninterface SeparatorState {\n animationPhase: 'idle' | 'exit-coordinated' | 'flip-coordinated' | 'completed';\n}\n\nexport interface Token {\n id: string;\n text: string;\n}\n\nexport interface AnimatedTokensV2Props {\n tokens: Token[];\n placeholder?: string;\n maxVisible?: number;\n textAnimationMode?: 'character' | 'word';\n textDirection?: 'vertical' | 'horizontal';\n textStaggerDelay?: number;\n\n // Locale-aware formatting (Intl.ListFormat)\n locale?: string;\n listType?: ListFormatType;\n listStyle?: ListFormatStyle;\n\n // Manual separator override (ignores locale if set)\n separator?: string;\n\n enableWidthAnimation?: boolean;\n className?: string;\n tokenClassName?: string;\n placeholderClassName?: string;\n separatorClassName?: string;\n}\n\n/**\n * AnimatedTokensV2 - Locale-aware animated token list\n *\n * Uses Intl.ListFormat for proper locale-aware separators.\n * Supports conjunction (\"and\"), disjunction (\"or\"), and unit (space) types.\n *\n * @example\n * ```tsx\n * // Automatic locale (browser default)\n * <AnimatedTokensV2 tokens={tags} />\n * // en-US: \"Apple, Banana, and Cherry\"\n * // es-ES: \"Apple, Banana y Cherry\"\n *\n * // Specific locale\n * <AnimatedTokensV2 tokens={tags} locale=\"es\" />\n *\n * // Disjunction (or)\n * <AnimatedTokensV2 tokens={tags} listType=\"disjunction\" />\n * // \"Apple, Banana, or Cherry\"\n *\n * // Manual override\n * <AnimatedTokensV2 tokens={tags} separator=\" | \" />\n * // \"Apple | Banana | Cherry\"\n * ```\n */\nexport const AnimatedTokensV2: React.FC<AnimatedTokensV2Props> = ({\n tokens,\n placeholder = 'No tokens',\n maxVisible,\n textAnimationMode = 'character',\n textDirection = 'vertical',\n textStaggerDelay = 15,\n locale,\n listType = 'conjunction',\n listStyle = 'long',\n separator,\n enableWidthAnimation = false,\n className = '',\n tokenClassName = '',\n placeholderClassName = '',\n separatorClassName = '',\n}) => {\n const { logEvent } = useDebug();\n const logEventRef = useRef(logEvent);\n useEffect(() => {\n logEventRef.current = logEvent;\n }, [logEvent]);\n\n // Track exiting token IDs with REF (not state) - prevents unnecessary re-renders\n const exitingIdsRef = useRef<Set<string>>(new Set());\n\n // Track separator states for coordination\n const separatorStatesRef = useRef<Map<string, SeparatorState>>(new Map());\n const separatorRefs = useRef<Map<string, HTMLSpanElement>>(new Map());\n\n // Visible tokens\n const visibleTokens = useMemo(\n () => (maxVisible ? tokens.slice(0, maxVisible) : tokens),\n [tokens, maxVisible]\n );\n\n const overflowCount = useMemo(\n () => (maxVisible ? Math.max(0, tokens.length - maxVisible) : 0),\n [tokens.length, maxVisible]\n );\n\n // Use locale-aware list formatting\n const listParts = useListFormat(\n visibleTokens.map(t => t.text),\n { locale, type: listType, style: listStyle, separator }\n );\n\n // Track overflow state changes\n const prevOverflowRef = useRef(0);\n const isOverflowEntering = overflowCount > 0 && prevOverflowRef.current === 0;\n const isOverflowExiting = overflowCount === 0 && prevOverflowRef.current > 0;\n\n useEffect(() => {\n prevOverflowRef.current = overflowCount;\n }, [overflowCount]);\n\n // Separator visibility logic - same as monolith\n const shouldShowSeparator = useCallback((tokenId: string, tokenIndex: number): boolean => {\n const isLastToken = tokenIndex >= visibleTokens.length - 1;\n if (isLastToken) return false;\n\n const separatorState = separatorStatesRef.current.get(tokenId);\n\n // Show separator if:\n // 1. No separator state (default to visible)\n // 2. Separator is idle or animating but not completed\n if (!separatorState) return true;\n\n switch (separatorState.animationPhase) {\n case 'idle':\n case 'exit-coordinated':\n case 'flip-coordinated':\n return true; // Still visible during coordinated animation\n case 'completed':\n return false; // Animation finished, hide separator\n default:\n return true;\n }\n }, [visibleTokens.length]);\n\n // Clean up separator states for tokens that no longer exist\n useEffect(() => {\n const visibleTokenIds = new Set(visibleTokens.map(t => t.id));\n const currentStates = separatorStatesRef.current;\n\n // Remove states for tokens that are no longer visible\n for (const tokenId of currentStates.keys()) {\n if (!visibleTokenIds.has(tokenId)) {\n separatorStatesRef.current.delete(tokenId);\n }\n }\n\n // Initialize separator states for new visible tokens that need separators\n visibleTokens.forEach((token, index) => {\n const needsSeparator = index < visibleTokens.length - 1;\n if (needsSeparator && !separatorStatesRef.current.has(token.id)) {\n separatorStatesRef.current.set(token.id, { animationPhase: 'idle' });\n }\n });\n }, [visibleTokens]);\n\n // Animate separator exit with state tracking\n const animateSeparatorExit = useCallback((tokenId: string) => {\n const separatorEl = separatorRefs.current.get(tokenId);\n if (!separatorEl) return;\n\n // Update separator state\n separatorStatesRef.current.set(tokenId, { animationPhase: 'exit-coordinated' });\n separatorEl.dataset.separatorState = 'exiting';\n\n const animation = separatorEl.animate([\n { opacity: 1, transform: 'translateY(0) scale(1)' },\n { opacity: 0, transform: `translateY(${TRANSFORMS.OFFSET_Y_EXIT}px) scale(${TRANSFORMS.SCALE_EXIT})` }\n ], {\n duration: TIMING.EXIT_DURATION,\n easing: EASINGS.EASE_IN_CUBIC,\n fill: 'forwards'\n });\n\n animation.finished.then(() => {\n separatorStatesRef.current.set(tokenId, { animationPhase: 'completed' });\n separatorEl.dataset.separatorState = 'completed';\n }).catch(() => {\n separatorStatesRef.current.delete(tokenId);\n });\n }, []);\n\n const handleItemExit = useCallback((id: string) => {\n exitingIdsRef.current.add(id);\n\n // Find additional separators that need animation (like monolith)\n const additionalSeparators: string[] = [];\n const tokenIndex = visibleTokens.findIndex(t => t.id === id);\n\n // If removing last visible token, the previous token loses its separator\n if (tokenIndex === visibleTokens.length - 1 && tokenIndex > 0) {\n const previousToken = visibleTokens[tokenIndex - 1];\n if (previousToken) {\n additionalSeparators.push(previousToken.id);\n }\n }\n\n // Animate this token's separator and any additional ones\n animateSeparatorExit(id);\n additionalSeparators.forEach(sepId => animateSeparatorExit(sepId));\n\n logEventRef.current({\n type: 'token-remove',\n source: 'AnimatedTokensV2',\n message: `Token exiting: ${id}`,\n data: { id, additionalSeparators }\n });\n }, [visibleTokens, animateSeparatorExit]);\n\n const handleItemEnter = useCallback((id: string) => {\n logEventRef.current({\n type: 'token-add',\n source: 'AnimatedTokensV2',\n message: `Token entering: ${id}`,\n data: { id }\n });\n }, []);\n\n const showPlaceholder = tokens.length === 0 && !!placeholder;\n\n // Build children from list parts (elements + separators)\n const tokenElements = useMemo(() => {\n return listParts.map((part, partIndex) => {\n if (part.type === 'element') {\n const token = visibleTokens[part.index];\n if (!token) return null;\n\n const isExiting = exitingIdsRef.current.has(token.id);\n\n return (\n <span\n key={token.id}\n className={`waapi-token-wrapper ${tokenClassName}`}\n data-reorder-id={token.id}\n >\n <SlidingText\n text={token.text}\n mode={isExiting ? 'none' : textAnimationMode}\n direction={textDirection}\n staggerDelay={textStaggerDelay}\n duration={ANIMATION_DEFAULTS.DURATION_ENTER}\n blur={PRESETS.newToken.blur}\n widthAnimation={!isExiting && enableWidthAnimation}\n initial={isExiting ? false : 'initial'}\n animate=\"animate\"\n />\n </span>\n );\n }\n\n // Separator (literal) - apply shouldShowSeparator logic\n const prevToken = visibleTokens[part.index];\n if (!prevToken) return null;\n\n const tokenIndex = visibleTokens.findIndex(t => t.id === prevToken.id);\n const showSeparator = shouldShowSeparator(prevToken.id, tokenIndex);\n\n if (!showSeparator) return null;\n\n return (\n <span\n key={`sep-${prevToken.id}-${partIndex}`}\n ref={el => {\n if (el) separatorRefs.current.set(prevToken.id, el);\n else separatorRefs.current.delete(prevToken.id);\n }}\n className={`waapi-token-separator ${separatorClassName}`}\n data-separator-state={separatorStatesRef.current.get(prevToken.id)?.animationPhase ?? 'idle'}\n >\n {part.value}\n </span>\n );\n }).filter(Boolean);\n }, [listParts, visibleTokens, tokenClassName, textAnimationMode, textDirection, textStaggerDelay, enableWidthAnimation, separatorClassName, shouldShowSeparator]);\n\n // Get last separator for overflow (use formatted separator or fallback)\n const lastSeparator = useMemo(() => {\n if (separator !== undefined) return separator;\n // Find the last literal in parts for proper locale formatting\n const lastLiteral = [...listParts].reverse().find(p => p.type === 'literal');\n return lastLiteral?.value ?? ', ';\n }, [listParts, separator]);\n\n // Overflow element - now inside Reorder for FLIP coordination\n const overflowElement = useMemo(() => {\n if (overflowCount === 0 && !isOverflowExiting) return null;\n\n return (\n <div\n key=\"overflow-counter\"\n data-reorder-id=\"overflow-counter\"\n className={`waapi-token-overflow ${tokenClassName} ${\n isOverflowEntering ? 'entering' : ''\n } ${isOverflowExiting ? 'exiting' : ''}`}\n >\n {visibleTokens.length > 0 && (\n <span className={`waapi-token-separator ${separatorClassName}`}>\n {lastSeparator}\n </span>\n )}\n <span className={`waapi-token-separator ${separatorClassName}`}>+</span>\n <SlidingNumber\n value={overflowCount}\n duration={ANIMATION_DEFAULTS.DURATION_ENTER}\n fontSize=\"inherit\"\n fontWeight=\"inherit\"\n color=\"inherit\"\n />\n <SlidingText\n text=\" more\"\n mode={textAnimationMode}\n direction={textDirection}\n staggerDelay={textStaggerDelay}\n duration={\n isOverflowExiting\n ? ANIMATION_DEFAULTS.DURATION_EXIT\n : ANIMATION_DEFAULTS.DURATION_ENTER\n }\n blur={PRESETS.newToken.blur}\n initial={isOverflowEntering ? 'initial' : false}\n animate=\"animate\"\n />\n </div>\n );\n }, [overflowCount, isOverflowEntering, isOverflowExiting, tokenClassName, visibleTokens.length, lastSeparator, separatorClassName, textAnimationMode, textDirection, textStaggerDelay]);\n\n return (\n <div role=\"text\" className={`waapi-animated-tokens-container waapi-v2 ${className}`}>\n {showPlaceholder && (\n <SlidingText\n key=\"placeholder\"\n text={placeholder}\n mode={PRESETS.placeholder.mode}\n direction={PRESETS.placeholder.direction}\n blur={PRESETS.placeholder.blur}\n duration={PRESETS.placeholder.duration}\n initial=\"initial\"\n animate=\"animate\"\n className={`waapi-token-placeholder ${placeholderClassName}`}\n />\n )}\n\n <Reorder\n layout=\"horizontal\"\n onItemExit={handleItemExit}\n onItemEnter={handleItemEnter}\n >\n {tokenElements}\n {overflowElement}\n </Reorder>\n </div>\n );\n};\n","import { createContext, useContext } from 'react';\nimport type { MorphContextValue } from './types';\n\n/**\n * Context for sharing Morph state between components\n */\nexport const MorphContext = createContext<MorphContextValue | null>(null);\n\n/**\n * Hook to access Morph context\n * Must be used within a Morph component\n *\n * @throws Error if used outside Morph component\n */\nexport function useMorphContext(): MorphContextValue {\n const context = useContext(MorphContext);\n\n if (!context) {\n throw new Error(\n 'useMorphContext must be used within a <Morph> component. ' +\n 'Wrap your morphable components with <Morph>.'\n );\n }\n\n return context;\n}\n","import { useRef, useCallback, useEffect, useState } from 'react';\nimport { TIMING, EASINGS } from '../../../utils/animationUtils';\nimport type { FLIPClipPathAPI, FLIPClipPathOptions } from '../types';\n\nconst DEFAULT_DURATION = TIMING.FLIP_DURATION;\nconst DEFAULT_EASING = EASINGS.EASE_OUT_CUBIC;\n\n/**\n * Hook for FLIP + clip-path morphing technique\n *\n * This technique combines:\n * 1. FLIP for position/size transitions\n * 2. clip-path for shape morphing\n * 3. Opacity crossfade for smooth visual transition\n *\n * @example\n * ```tsx\n * const { isMorphing, morph, cancel } = useFLIPClipPath({ duration: 300 });\n *\n * const handleMorph = async () => {\n * await morph(fromRef.current, toRef.current);\n * };\n * ```\n */\nexport function useFLIPClipPath(options?: FLIPClipPathOptions): FLIPClipPathAPI {\n const [isMorphing, setIsMorphing] = useState(false);\n const activeAnimationsRef = useRef<Animation[]>([]);\n const optionsRef = useRef(options);\n\n useEffect(() => {\n optionsRef.current = options;\n }, [options]);\n\n const cancel = useCallback(() => {\n activeAnimationsRef.current.forEach(anim => anim.cancel());\n activeAnimationsRef.current = [];\n setIsMorphing(false);\n }, []);\n\n const morph = useCallback(async (\n fromElement: HTMLElement,\n toElement: HTMLElement\n ): Promise<void> => {\n if (isMorphing) {\n cancel();\n }\n\n setIsMorphing(true);\n\n const duration = optionsRef.current?.duration ?? DEFAULT_DURATION;\n const easing = optionsRef.current?.easing ?? DEFAULT_EASING;\n\n const fromRect = fromElement.getBoundingClientRect();\n const toRect = toElement.getBoundingClientRect();\n\n const deltaX = fromRect.left - toRect.left;\n const deltaY = fromRect.top - toRect.top;\n const scaleX = fromRect.width / toRect.width;\n const scaleY = fromRect.height / toRect.height;\n\n const fromBorderRadius = getComputedStyle(fromElement).borderRadius;\n const toBorderRadius = getComputedStyle(toElement).borderRadius;\n\n const fromClipPath = optionsRef.current?.clipPathStart ||\n borderRadiusToInset(fromBorderRadius, fromRect);\n const toClipPath = optionsRef.current?.clipPathEnd ||\n borderRadiusToInset(toBorderRadius, toRect);\n\n const originalFromStyles = {\n position: fromElement.style.position,\n opacity: fromElement.style.opacity,\n pointerEvents: fromElement.style.pointerEvents\n };\n\n const originalToStyles = {\n position: toElement.style.position,\n opacity: toElement.style.opacity,\n transform: toElement.style.transform,\n clipPath: toElement.style.clipPath\n };\n\n fromElement.style.position = 'absolute';\n fromElement.style.pointerEvents = 'none';\n\n toElement.style.position = 'relative';\n toElement.style.transform = `translate(${deltaX}px, ${deltaY}px) scale(${scaleX}, ${scaleY})`;\n toElement.style.clipPath = fromClipPath;\n toElement.style.opacity = '0';\n\n const fromFadeOut = fromElement.animate([\n { opacity: 1 },\n { opacity: 0 }\n ], {\n duration: duration * 0.5,\n easing,\n fill: 'forwards'\n });\n\n const toFadeIn = toElement.animate([\n { opacity: 0 },\n { opacity: 1 }\n ], {\n duration: duration * 0.5,\n delay: duration * 0.25,\n easing,\n fill: 'forwards'\n });\n\n const toTransform = toElement.animate([\n {\n transform: `translate(${deltaX}px, ${deltaY}px) scale(${scaleX}, ${scaleY})`,\n clipPath: fromClipPath\n },\n {\n transform: 'translate(0, 0) scale(1, 1)',\n clipPath: toClipPath\n }\n ], {\n duration,\n easing,\n fill: 'forwards'\n });\n\n activeAnimationsRef.current = [fromFadeOut, toFadeIn, toTransform];\n\n try {\n await Promise.all([\n fromFadeOut.finished,\n toFadeIn.finished,\n toTransform.finished\n ]);\n } catch {\n Object.assign(fromElement.style, originalFromStyles);\n Object.assign(toElement.style, originalToStyles);\n setIsMorphing(false);\n return;\n }\n\n Object.assign(fromElement.style, originalFromStyles);\n toElement.style.transform = '';\n toElement.style.clipPath = '';\n toElement.style.opacity = '1';\n\n activeAnimationsRef.current = [];\n setIsMorphing(false);\n }, [isMorphing, cancel]);\n\n useEffect(() => {\n return () => {\n cancel();\n };\n }, [cancel]);\n\n return {\n isMorphing,\n morph,\n cancel\n };\n}\n\nfunction borderRadiusToInset(borderRadius: string, rect: DOMRect): string {\n const radius = parseFloat(borderRadius) || 0;\n const percentX = (radius / rect.width) * 100;\n const percentY = (radius / rect.height) * 100;\n return `inset(0 round ${percentX}% ${percentY}%)`;\n}\n","import { useRef, useCallback, useState } from 'react';\nimport { TIMING, EASINGS } from '../../../utils/animationUtils';\nimport type { CSSGridMorphAPI, CSSGridMorphOptions } from '../types';\n\nconst DEFAULT_DURATION = TIMING.FLIP_DURATION;\nconst DEFAULT_EASING = EASINGS.MATERIAL_STANDARD;\n\n/**\n * Hook for CSS Grid-based expand/collapse animations\n *\n * Uses the grid-template-rows: 0fr/1fr technique for smooth height animations\n * without needing to know the content height in advance.\n *\n * CSS required on container:\n * ```css\n * .morph-container {\n * display: grid;\n * grid-template-rows: 0fr;\n * transition: grid-template-rows 300ms ease;\n * }\n * .morph-container.expanded {\n * grid-template-rows: 1fr;\n * }\n * .morph-content {\n * overflow: hidden;\n * }\n * ```\n *\n * @example\n * ```tsx\n * const { isExpanded, toggle, containerRef } = useCSSGridMorph();\n *\n * return (\n * <div ref={containerRef} className={isExpanded ? 'expanded' : ''}>\n * <div className=\"morph-content\">\n * Collapsible content\n * </div>\n * </div>\n * );\n * ```\n */\nexport function useCSSGridMorph(options?: CSSGridMorphOptions): CSSGridMorphAPI {\n const [isExpanded, setIsExpanded] = useState(false);\n const containerRef = useRef<HTMLElement | null>(null);\n const optionsRef = useRef(options);\n\n const duration = optionsRef.current?.duration ?? DEFAULT_DURATION;\n const easing = optionsRef.current?.easing ?? DEFAULT_EASING;\n\n const applyTransition = useCallback(() => {\n if (containerRef.current) {\n containerRef.current.style.transition = `grid-template-rows ${duration}ms ${easing}`;\n }\n }, [duration, easing]);\n\n const expand = useCallback(() => {\n applyTransition();\n if (containerRef.current) {\n containerRef.current.style.gridTemplateRows = '1fr';\n }\n setIsExpanded(true);\n }, [applyTransition]);\n\n const collapse = useCallback(() => {\n applyTransition();\n if (containerRef.current) {\n containerRef.current.style.gridTemplateRows = '0fr';\n }\n setIsExpanded(false);\n }, [applyTransition]);\n\n const toggle = useCallback(() => {\n if (isExpanded) {\n collapse();\n } else {\n expand();\n }\n }, [isExpanded, expand, collapse]);\n\n return {\n isExpanded,\n expand,\n collapse,\n toggle,\n containerRef\n };\n}\n","import { useRef, useCallback, useMemo } from 'react';\nimport type { ViewTransitionsAPI, ViewTransitionsOptions } from '../types';\n\n/**\n * Hook for View Transitions API\n *\n * Provides a wrapper around the native View Transitions API\n * with automatic feature detection and fallback handling.\n *\n * Browser support (as of 2025):\n * - Chrome 111+: ✅\n * - Firefox 144+: ✅\n * - Safari latest: ✅\n *\n * @example\n * ```tsx\n * const { isSupported, startTransition } = useViewTransitions({ name: 'hero' });\n *\n * const handleTransition = async () => {\n * await startTransition(() => {\n * setActiveImage(nextImage);\n * });\n * };\n * ```\n */\nexport function useViewTransitions(options?: ViewTransitionsOptions): ViewTransitionsAPI {\n const optionsRef = useRef(options);\n const typesRef = useRef<string[]>(options?.types || []);\n\n const isSupported = useMemo(() => {\n if (typeof document === 'undefined') return false;\n return 'startViewTransition' in document;\n }, []);\n\n const setTypes = useCallback((types: string[]) => {\n typesRef.current = types;\n }, []);\n\n const startTransition = useCallback(async (\n callback: () => void | Promise<void>\n ): Promise<void> => {\n if (!document.startViewTransition) {\n await callback();\n return;\n }\n\n const viewTransitionName = optionsRef.current?.name;\n\n const setupStyles = () => {\n if (viewTransitionName) {\n const styleEl = document.createElement('style');\n styleEl.id = `view-transition-${viewTransitionName}`;\n styleEl.textContent = `\n ::view-transition-old(${viewTransitionName}),\n ::view-transition-new(${viewTransitionName}) {\n animation-duration: 300ms;\n animation-timing-function: ease-out;\n }\n `;\n document.head.appendChild(styleEl);\n return () => {\n styleEl.remove();\n };\n }\n return () => {};\n };\n\n const cleanup = setupStyles();\n\n try {\n const transition = document.startViewTransition(callback);\n await transition.finished;\n } finally {\n cleanup();\n }\n }, []);\n\n return {\n isSupported,\n startTransition,\n setTypes\n };\n}\n","import { useCallback, useRef, useEffect } from 'react';\nimport { useFLIPClipPath } from './techniques/useFLIPClipPath';\nimport { useCSSGridMorph } from './techniques/useCSSGridMorph';\nimport { useViewTransitions } from './techniques/useViewTransitions';\nimport type { UseMorphReturn, UseMorphOptions, MorphTechnique } from './types';\n\n/**\n * Unified hook for morphing animations\n *\n * Provides access to multiple morphing techniques:\n * - FLIP + clip-path (default): Best for element-to-element transitions\n * - CSS Grid: Best for expand/collapse animations\n * - View Transitions: Best for page/route transitions (requires browser support)\n *\n * @example\n * ```tsx\n * const morph = useMorph({ technique: 'flip-clip-path' });\n *\n * // Using unified API:\n * await morph.morph(fromElement, toElement);\n *\n * // Or using individual techniques:\n * morph.cssGrid.toggle();\n * ```\n */\nexport function useMorph(options?: UseMorphOptions): UseMorphReturn {\n const technique: MorphTechnique = options?.technique ?? 'flip-clip-path';\n\n const optionsRef = useRef(options);\n useEffect(() => {\n optionsRef.current = options;\n }, [options]);\n\n const flipClipPath = useFLIPClipPath({\n duration: options?.duration,\n easing: options?.easing\n });\n\n const cssGrid = useCSSGridMorph({\n duration: options?.duration,\n easing: options?.easing\n });\n\n const viewTransitions = useViewTransitions();\n\n const isMorphing = flipClipPath.isMorphing;\n\n const morph = useCallback(async (\n fromElement: HTMLElement,\n toElement: HTMLElement\n ): Promise<void> => {\n optionsRef.current?.onMorphStart?.();\n\n const currentTechnique = optionsRef.current?.technique ?? 'flip-clip-path';\n\n switch (currentTechnique) {\n case 'flip-clip-path':\n await flipClipPath.morph(fromElement, toElement);\n break;\n\n case 'view-transitions':\n if (viewTransitions.isSupported) {\n await viewTransitions.startTransition(() => {\n fromElement.style.opacity = '0';\n toElement.style.opacity = '1';\n });\n } else {\n await flipClipPath.morph(fromElement, toElement);\n }\n break;\n\n case 'css-grid':\n cssGrid.toggle();\n break;\n }\n\n optionsRef.current?.onMorphEnd?.();\n }, [flipClipPath, viewTransitions, cssGrid]);\n\n const cancel = useCallback(() => {\n flipClipPath.cancel();\n }, [flipClipPath]);\n\n return {\n isMorphing,\n technique,\n isViewTransitionsSupported: viewTransitions.isSupported,\n morph,\n cancel,\n flipClipPath,\n cssGrid,\n viewTransitions\n };\n}\n","import { useMemo, type ReactNode } from 'react';\nimport { MorphContext } from './MorphContext';\nimport { useMorph } from './useMorph';\nimport type { MorphProps, MorphContextValue } from './types';\n\n/**\n * Container component for morphable elements\n *\n * Provides morphing capabilities to child components through context.\n *\n * @example\n * ```tsx\n * <Morph technique=\"flip-clip-path\" duration={300}>\n * <MorphSource ref={fromRef}>From Content</MorphSource>\n * <MorphTarget ref={toRef}>To Content</MorphTarget>\n * <button onClick={() => morph(fromRef.current, toRef.current)}>\n * Morph\n * </button>\n * </Morph>\n * ```\n */\nexport function Morph({\n children,\n technique = 'flip-clip-path',\n duration,\n easing,\n className = '',\n onMorphStart,\n onMorphEnd\n}: MorphProps): ReactNode {\n const morph = useMorph({\n technique,\n duration,\n easing,\n onMorphStart,\n onMorphEnd\n });\n\n const contextValue = useMemo<MorphContextValue>(() => ({\n morph\n }), [morph]);\n\n return (\n <MorphContext.Provider value={contextValue}>\n <div className={`morph-container ${className}`}>\n {children}\n </div>\n </MorphContext.Provider>\n );\n}\n\nexport type { MorphProps } from './types';\n","import type React from 'react';\nimport {\n slidingTextBaseStyles,\n CSS_VAR_NAMES,\n cssVar,\n getResponsiveTokenStyles,\n} from './cssTokens';\n\n// Re-export base styles for consumers\nexport const slidingTextStyles = slidingTextBaseStyles;\n\n// Helper para obtener estilos de token según estado y dirección\nexport const getSlidingTextTokenStyle = (\n state: 'enter-from' | 'enter-to' | 'exit-active',\n direction: 'vertical' | 'horizontal'\n): React.CSSProperties => {\n const base = { ...slidingTextBaseStyles.token };\n\n if (state === 'enter-from') {\n Object.assign(base, slidingTextBaseStyles.enterFrom);\n if (direction === 'vertical') {\n Object.assign(base, slidingTextBaseStyles.verticalEnterFrom);\n } else {\n Object.assign(base, slidingTextBaseStyles.horizontalEnterFrom);\n }\n } else if (state === 'enter-to') {\n Object.assign(base, slidingTextBaseStyles.enterTo);\n } else if (state === 'exit-active') {\n Object.assign(base, slidingTextBaseStyles.exitActive);\n if (direction === 'vertical') {\n Object.assign(base, slidingTextBaseStyles.verticalExitActive);\n } else {\n Object.assign(base, slidingTextBaseStyles.horizontalExitActive);\n }\n }\n\n return base;\n};\n\n// Helper para obtener estilos de container con responsive\nexport const getResponsiveSlidingTextStyle = (): React.CSSProperties => {\n const responsiveVars = getResponsiveTokenStyles();\n return {\n ...slidingTextBaseStyles.container,\n ...responsiveVars,\n };\n};\n\n// Export CSS variable names para uso avanzado\nexport { CSS_VAR_NAMES, cssVar };\n","import type React from 'react';\nimport {\n animatedTokensBaseStyles,\n CSS_VAR_NAMES,\n cssVar,\n getResponsiveTokenStyles,\n} from './cssTokens';\n\n// Re-export base styles for consumers\nexport const animatedTokensStyles = animatedTokensBaseStyles;\n\n// Helper para obtener estilos responsivos del container\nexport const getResponsiveAnimatedTokensStyle = (): React.CSSProperties => {\n const responsiveVars = getResponsiveTokenStyles();\n return {\n ...animatedTokensBaseStyles.container,\n ...responsiveVars,\n };\n};\n\n// Export CSS variable names para uso avanzado\nexport { CSS_VAR_NAMES, cssVar };\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["CSS_PROPERTY_DEFINITIONS: PropertyDefinition[]","baseContainerStyle: React.CSSProperties","options: Intl.NumberFormatOptions","result: CharItem[]","seq: number[]","diff: number","DebugProvider: React.FC<{ children: React.ReactNode }>","overlaps: string[]","SlidingText: React.FC<SlidingTextProps>","newSeparatorState: SeparatorState","exitAnimations: Animation[]","flipAnimations: Animation[]","AnimatedTokens: React.FC<AnimatedTokensProps>","additionalSeparators: string[]","visibleTokens","DEFAULT_DURATION","DEFAULT_EASING","animations: Animation[]","flipAnimations: Animation[]","siblingsToAnimate: string[]","exitAnimations: Animation[]","animations: Animation[]","added: string[]","result: ReactNode[]","layoutStyles: Record<ReorderLayout, CSSProperties>","records: ChildRecord[]","parts: ListPart[]","AnimatedTokensV2: React.FC<AnimatedTokensV2Props>","additionalSeparators: string[]","Reorder","DEFAULT_DURATION","DEFAULT_EASING","technique: MorphTechnique"],"sources":["../src/utils/animationUtils.ts","../src/styles/cssTokens.ts","../src/styles/injectStyles.ts","../src/components/SlidingNumber.tsx","../src/contexts/DebugContext.tsx","../src/utils/debugCapture.ts","../src/utils/choreographyTracker.ts","../src/components/SlidingText.tsx","../src/hooks/useWAAPIAnimations.ts","../src/components/AnimatedTokens.tsx","../src/core/useElementRegistry.ts","../src/core/usePositionCapture.ts","../src/core/useFLIPAnimation.ts","../src/core/useAnimationOrchestrator.ts","../src/primitives/reorder/useReorder.ts","../src/primitives/reorder/useReorderPresence.ts","../src/primitives/reorder/Reorder.tsx","../src/hooks/useListFormat.ts","../src/components/AnimatedTokensV2.tsx","../src/primitives/morph/MorphContext.tsx","../src/primitives/morph/techniques/useFLIPClipPath.ts","../src/primitives/morph/techniques/useCSSGridMorph.ts","../src/primitives/morph/techniques/useViewTransitions.ts","../src/primitives/morph/useMorph.ts","../src/primitives/morph/Morph.tsx","../src/styles/slidingText.styles.ts","../src/styles/animatedTokens.styles.ts"],"sourcesContent":["// ============================================\n// ANIMATION CONFIGURATION\n// Sistema configurable para iteración rápida\n// Research-based values from:\n// - Material Design Motion\n// - CSS-Tricks FLIP Technique\n// - Josh W. Comeau Spring Animations\n// - Easings.net\n// ============================================\n\n// ============================================\n// RESPONSIVE & ACCESSIBILITY HELPERS\n// ============================================\n\n/**\n * Obtiene duración de animación responsiva para el dispositivo actual\n * @param baseDuration - Duración base en ms\n * @returns Duración ajustada para dispositivo actual\n */\nexport const getResponsiveDuration = (baseDuration: number): number => {\n if (typeof window === 'undefined') return baseDuration;\n\n // Verificar si el usuario prefiere reducir movimiento\n const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;\n if (prefersReducedMotion) return 0;\n\n // Detectar dispositivo móvil y reducir duración para mejor performance\n const isMobile = window.innerWidth < 768; // md breakpoint\n if (isMobile) {\n return Math.round(baseDuration * 0.6); // 60% de la duración en móviles\n }\n\n return baseDuration;\n};\n\n/**\n * Obtiene stagger delay responsivo\n * @param baseDelay - Delay base en ms\n * @returns Delay ajustado para dispositivo actual\n */\nexport const getResponsiveStagger = (baseDelay: number): number => {\n if (typeof window === 'undefined') return baseDelay;\n\n const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;\n if (prefersReducedMotion) return 0;\n\n const isMobile = window.innerWidth < 768;\n return isMobile ? Math.round(baseDelay * 0.5) : baseDelay;\n};\n\n// TIMING (ms)\nexport const TIMING = {\n // Durations\n ENTER_DURATION: 200,\n EXIT_DURATION: 180, // Faster to not block FLIP\n COLLAPSE_DURATION: 200,\n FLIP_DURATION: 300, // Longer for spring effect\n\n // Stagger between characters\n ENTER_STAGGER: 15,\n EXIT_STAGGER: 0, // No stagger on exit (cleaner)\n\n // Delays\n COLLAPSE_DELAY: 30, // Start collapse after fade begins\n FLIP_DELAY_PERCENT: 0.25, // Start FLIP at 25% of exit\n\n // Thresholds\n MIN_DELTA_PX: 1, // Ignore sub-pixel movements\n} as const;\n\n// TRANSFORMS (px/deg)\nexport const TRANSFORMS = {\n OFFSET_Y_ENTER: 8,\n OFFSET_Y_EXIT: -8,\n OFFSET_X: 16,\n\n // Scale values (más visibles según guía)\n SCALE_ENTER: 0.85, // Para tokens que entran\n SCALE_EXIT: 0.85, // Más visible que 0.95\n\n ROTATE_EXIT: 0, // Optional: -2 for playful feel\n} as const;\n\n// EFFECTS\nexport const EFFECTS = {\n BLUR_ENTER: 4, // px\n BLUR_EXIT: 2, // Less blur on exit (faster)\n} as const;\n\n// EASINGS - Research-based (Material Design + CSS-Tricks + Josh W. Comeau)\nexport const EASINGS = {\n // Material Design (estándar industry)\n MATERIAL_DECELERATE: 'cubic-bezier(0, 0, 0.2, 1)', // Para elementos que entran\n MATERIAL_ACCELERATE: 'cubic-bezier(0.4, 0, 1, 1)', // Para elementos que salen\n MATERIAL_STANDARD: 'cubic-bezier(0.4, 0, 0.2, 1)', // Para movimientos estándar\n\n // Física realista (CSS-Tricks recommendation)\n EASE_REORDER: 'cubic-bezier(0.215, 0.61, 0.355, 1)', // Perfecto para FLIP/reordenamiento\n\n // Standard legacy (compatibilidad)\n EASE_OUT_CUBIC: 'cubic-bezier(0.33, 1, 0.68, 1)',\n EASE_IN_CUBIC: 'cubic-bezier(0.32, 0, 0.67, 0)',\n EASE_IN_OUT: 'cubic-bezier(0.42, 0, 0.58, 1)',\n EASE_OUT_EXPO: 'cubic-bezier(0.16, 1, 0.3, 1)',\n\n // FLIP + Spring optimizados\n EASE_FLIP: 'cubic-bezier(0.2, 0, 0.2, 1)', // Para FLIP technique\n\n // Spring via linear() - Josh W. Comeau (mantenemos para casos especiales)\n SPRING_GENTLE: `linear(0, 0.009, 0.035 2.1%, 0.141 4.4%, 0.723 12.9%, 0.938 16.7%, 1.017 19.4%, 1.067, 1.099 24.3%, 1.108 26%, 1.100, 1.078 30.1%, 1.049 32.5%, 0.994 37.3%, 0.981 40.2%, 0.974 43.4%, 0.975 50.2%, 0.997 62.5%, 1.001 74.7%, 1)`,\n\n SPRING_SNAPPY: `linear(0, 0.006, 0.024 2%, 0.096 4.2%, 0.397 9.3%, 0.861 15.8%, 1.002 18.7%, 1.093 21.4%, 1.143 24%, 1.156, 1.149 28.3%, 1.115 31.5%, 1.022 40%, 0.988 47.1%, 0.984 55.1%, 0.998 72.3%, 1.001 85.4%, 1)`,\n} as const;\n\n/**\n * Configuración de animación responsiva con accessibility\n */\nexport const RESPONSIVE_CONFIGS = {\n tokenEnter: {\n get duration() { return getResponsiveDuration(TIMING.ENTER_DURATION); },\n get stagger() { return getResponsiveStagger(TIMING.ENTER_STAGGER); },\n easing: EASINGS.MATERIAL_DECELERATE, // Material Design - entra rápido y se asienta\n blur: EFFECTS.BLUR_ENTER,\n offsetY: TRANSFORMS.OFFSET_Y_ENTER,\n scale: TRANSFORMS.SCALE_ENTER, // Nuevo scale más visible\n },\n tokenExit: {\n get duration() { return getResponsiveDuration(TIMING.EXIT_DURATION); },\n get stagger() { return getResponsiveStagger(TIMING.EXIT_STAGGER); },\n easing: EASINGS.MATERIAL_ACCELERATE, // Material Design - sale rápido\n blur: EFFECTS.BLUR_EXIT,\n offsetY: TRANSFORMS.OFFSET_Y_EXIT,\n scale: TRANSFORMS.SCALE_EXIT,\n },\n collapse: {\n get duration() { return getResponsiveDuration(TIMING.COLLAPSE_DURATION); },\n delay: TIMING.COLLAPSE_DELAY,\n easing: EASINGS.MATERIAL_STANDARD, // Material Design - estándar\n },\n flip: {\n get duration() { return getResponsiveDuration(TIMING.FLIP_DURATION); },\n delayPercent: TIMING.FLIP_DELAY_PERCENT,\n easing: EASINGS.EASE_REORDER, // Física realista para reordenamiento\n },\n} as const;\n\n// LEGACY DEFAULTS (for backward compatibility)\nexport const ANIMATION_DEFAULTS = {\n // Timing\n DURATION_ENTER: TIMING.ENTER_DURATION,\n DURATION_EXIT: TIMING.EXIT_DURATION,\n DURATION_FLIP: TIMING.FLIP_DURATION,\n STAGGER_DELAY: TIMING.ENTER_STAGGER,\n\n // Transform offsets\n OFFSET_VERTICAL: TRANSFORMS.OFFSET_Y_ENTER,\n OFFSET_HORIZONTAL: TRANSFORMS.OFFSET_X,\n\n // Blur\n BLUR_AMOUNT: EFFECTS.BLUR_ENTER,\n\n // Easing (updated to research-based values)\n EASING_ENTER: EASINGS.EASE_OUT_CUBIC,\n EASING_EXIT: EASINGS.EASE_IN_CUBIC,\n EASING_FLIP: EASINGS.SPRING_GENTLE,\n\n // Spring (para linear())\n SPRING_EASING: EASINGS.SPRING_GENTLE,\n};\n\n// ANIMATION CONFIGS (composite presets for specific use cases)\nexport const ANIMATION_CONFIGS = {\n tokenEnter: {\n duration: TIMING.ENTER_DURATION,\n stagger: TIMING.ENTER_STAGGER,\n easing: EASINGS.MATERIAL_DECELERATE, // Material Design\n blur: EFFECTS.BLUR_ENTER,\n offsetY: TRANSFORMS.OFFSET_Y_ENTER,\n scale: TRANSFORMS.SCALE_ENTER, // Scale más visible\n },\n tokenExit: {\n duration: TIMING.EXIT_DURATION,\n stagger: TIMING.EXIT_STAGGER,\n easing: EASINGS.MATERIAL_ACCELERATE, // Material Design - salida rápida\n blur: EFFECTS.BLUR_EXIT,\n offsetY: TRANSFORMS.OFFSET_Y_EXIT,\n scale: TRANSFORMS.SCALE_EXIT,\n },\n collapse: {\n duration: TIMING.COLLAPSE_DURATION,\n delay: TIMING.COLLAPSE_DELAY,\n easing: EASINGS.MATERIAL_STANDARD, // Material Design\n },\n flip: {\n duration: TIMING.FLIP_DURATION,\n delayPercent: TIMING.FLIP_DELAY_PERCENT,\n easing: EASINGS.EASE_REORDER, // Física realista para FLIP\n },\n} as const;\n\nexport const PRESETS = {\n newToken: {\n mode: 'character' as const,\n direction: 'vertical' as const,\n staggerDelay: 15,\n blur: true,\n widthAnimation: true,\n duration: 200,\n initial: 'initial' as const,\n },\n\n existingToken: {\n mode: 'none' as const,\n blur: false,\n widthAnimation: false,\n initial: false as const,\n },\n\n placeholder: {\n mode: 'word' as const,\n direction: 'vertical' as const,\n blur: true,\n widthAnimation: false,\n duration: 150,\n },\n\n separator: {\n duration: 100,\n widthAnimation: true,\n },\n};\n","import type React from 'react';\nimport { TIMING, TRANSFORMS, EFFECTS, EASINGS } from '../utils/animationUtils';\n\n// ============================================\n// CSS TOKENS - Single Source of Truth\n// Genera CSS variables y estilos desde animationUtils.ts\n// ============================================\n\n// ============================================\n// @property DEFINITIONS (Modern CSS 2024)\n// These define typed, animatable CSS custom properties\n// ============================================\n\nexport interface PropertyDefinition {\n name: string;\n syntax: '<time>' | '<length>' | '<number>' | '<integer>' | '<color>' | '<percentage>';\n inherits: boolean;\n initialValue: string;\n}\n\nexport const CSS_PROPERTY_DEFINITIONS: PropertyDefinition[] = [\n // Timing\n { name: '--waapi-duration-exit', syntax: '<time>', inherits: true, initialValue: `${TIMING.EXIT_DURATION}ms` },\n { name: '--waapi-duration-enter', syntax: '<time>', inherits: true, initialValue: `${TIMING.ENTER_DURATION}ms` },\n { name: '--waapi-duration-flip', syntax: '<time>', inherits: true, initialValue: `${TIMING.FLIP_DURATION}ms` },\n { name: '--waapi-duration-collapse', syntax: '<time>', inherits: true, initialValue: `${TIMING.COLLAPSE_DURATION}ms` },\n { name: '--waapi-stagger-enter', syntax: '<time>', inherits: true, initialValue: `${TIMING.ENTER_STAGGER}ms` },\n { name: '--waapi-stagger-exit', syntax: '<time>', inherits: true, initialValue: `${TIMING.EXIT_STAGGER}ms` },\n\n // Transforms\n { name: '--waapi-offset-y-enter', syntax: '<length>', inherits: true, initialValue: `${TRANSFORMS.OFFSET_Y_ENTER}px` },\n { name: '--waapi-offset-y-exit', syntax: '<length>', inherits: true, initialValue: `${TRANSFORMS.OFFSET_Y_EXIT}px` },\n { name: '--waapi-offset-x', syntax: '<length>', inherits: true, initialValue: `${TRANSFORMS.OFFSET_X}px` },\n { name: '--waapi-scale-exit', syntax: '<number>', inherits: true, initialValue: `${TRANSFORMS.SCALE_EXIT}` },\n { name: '--waapi-scale-enter', syntax: '<number>', inherits: true, initialValue: `${TRANSFORMS.SCALE_ENTER}` },\n\n // Effects\n { name: '--waapi-blur-enter', syntax: '<length>', inherits: true, initialValue: `${EFFECTS.BLUR_ENTER}px` },\n { name: '--waapi-blur-exit', syntax: '<length>', inherits: true, initialValue: `${EFFECTS.BLUR_EXIT}px` },\n\n // Stagger index (for progressive enhancement)\n { name: '--waapi-stagger-index', syntax: '<integer>', inherits: false, initialValue: '0' },\n];\n\n/**\n * Generates @property CSS rules from definitions\n * These enable typed, animatable CSS custom properties\n */\nexport const generatePropertyRules = (): string => {\n return CSS_PROPERTY_DEFINITIONS.map(def => `@property ${def.name} {\n syntax: '${def.syntax}';\n inherits: ${def.inherits};\n initial-value: ${def.initialValue};\n}`).join('\\n\\n');\n};\n\n// CSS Variable names (para type safety)\nexport const CSS_VAR_NAMES = {\n // Timing\n durationEnter: '--waapi-duration-enter',\n durationExit: '--waapi-duration-exit',\n durationCollapse: '--waapi-duration-collapse',\n durationFlip: '--waapi-duration-flip',\n staggerEnter: '--waapi-stagger-enter',\n staggerExit: '--waapi-stagger-exit',\n\n // Transforms\n offsetYEnter: '--waapi-offset-y-enter',\n offsetYExit: '--waapi-offset-y-exit',\n offsetX: '--waapi-offset-x',\n scaleExit: '--waapi-scale-exit',\n\n // Effects\n blurEnter: '--waapi-blur-enter',\n blurExit: '--waapi-blur-exit',\n\n // Easings\n easeEnter: '--waapi-ease-enter',\n easeExit: '--waapi-ease-exit',\n easeCollapse: '--waapi-ease-collapse',\n easeFlip: '--waapi-ease-flip',\n\n // Separator specific\n separatorDuration: '--waapi-separator-duration',\n separatorDelay: '--waapi-separator-delay',\n separatorEasing: '--waapi-separator-easing',\n} as const;\n\n// Tipo para las CSS variables\nexport type CSSVarName = typeof CSS_VAR_NAMES[keyof typeof CSS_VAR_NAMES];\n\n// ============================================\n// CSS VARIABLE VALUES (derivados de animationUtils)\n// ============================================\n\nexport const CSS_VAR_VALUES = {\n // Timing (convertido a CSS time)\n [CSS_VAR_NAMES.durationEnter]: `${TIMING.ENTER_DURATION}ms`,\n [CSS_VAR_NAMES.durationExit]: `${TIMING.EXIT_DURATION}ms`,\n [CSS_VAR_NAMES.durationCollapse]: `${TIMING.COLLAPSE_DURATION}ms`,\n [CSS_VAR_NAMES.durationFlip]: `${TIMING.FLIP_DURATION}ms`,\n [CSS_VAR_NAMES.staggerEnter]: `${TIMING.ENTER_STAGGER}ms`,\n [CSS_VAR_NAMES.staggerExit]: `${TIMING.EXIT_STAGGER}ms`,\n\n // Transforms (convertido a CSS units)\n [CSS_VAR_NAMES.offsetYEnter]: `${TRANSFORMS.OFFSET_Y_ENTER}px`,\n [CSS_VAR_NAMES.offsetYExit]: `${TRANSFORMS.OFFSET_Y_EXIT}px`,\n [CSS_VAR_NAMES.offsetX]: `${TRANSFORMS.OFFSET_X}px`,\n [CSS_VAR_NAMES.scaleExit]: `${TRANSFORMS.SCALE_EXIT}`,\n\n // Effects\n [CSS_VAR_NAMES.blurEnter]: `${EFFECTS.BLUR_ENTER}px`,\n [CSS_VAR_NAMES.blurExit]: `${EFFECTS.BLUR_EXIT}px`,\n\n // Easings (ya son strings válidos)\n [CSS_VAR_NAMES.easeEnter]: EASINGS.EASE_OUT_CUBIC,\n [CSS_VAR_NAMES.easeExit]: EASINGS.EASE_IN_CUBIC,\n [CSS_VAR_NAMES.easeCollapse]: EASINGS.EASE_IN_OUT,\n [CSS_VAR_NAMES.easeFlip]: EASINGS.EASE_FLIP,\n\n // Separator\n [CSS_VAR_NAMES.separatorDuration]: `${TIMING.FLIP_DURATION}ms`,\n [CSS_VAR_NAMES.separatorDelay]: `${Math.round(TIMING.EXIT_DURATION * TIMING.FLIP_DELAY_PERCENT)}ms`,\n [CSS_VAR_NAMES.separatorEasing]: EASINGS.EASE_FLIP,\n} as const;\n\n// ============================================\n// HELPER: Referencia a CSS variable\n// ============================================\n\nexport const cssVar = (name: CSSVarName): string => `var(${name})`;\n\n// ============================================\n// STYLE OBJECTS (usando CSS variables)\n// ============================================\n\nexport const baseContainerStyle: React.CSSProperties = {\n // Inyectar todas las CSS variables en el container\n ...CSS_VAR_VALUES as unknown as React.CSSProperties,\n};\n\n// ============================================\n// ANIMATED TOKENS STYLES\n// ============================================\n\nexport const animatedTokensBaseStyles = {\n container: {\n ...baseContainerStyle,\n display: 'flex',\n flexWrap: 'wrap',\n alignItems: 'center',\n gap: 0,\n } as React.CSSProperties,\n\n placeholder: {\n color: 'var(--muted-foreground)',\n fontStyle: 'italic',\n } as React.CSSProperties,\n\n tokenWrapper: {\n display: 'inline-flex',\n alignItems: 'center',\n } as React.CSSProperties,\n\n tokenWrapperExitCompleted: {\n position: 'absolute',\n opacity: 0,\n pointerEvents: 'none',\n } as React.CSSProperties,\n\n separator: {\n display: 'inline',\n whiteSpace: 'pre',\n marginLeft: '0.25em',\n willChange: 'transform, opacity',\n // NO CSS transitions - WAAPI handles all animations\n // This prevents conflicts between CSS and WAAPI\n } as React.CSSProperties,\n\n separatorExitCoordinated: {\n opacity: 0,\n transform: `translateY(-8px) scale(${TRANSFORMS.SCALE_EXIT})`,\n } as React.CSSProperties,\n\n separatorExitCompleted: {\n opacity: 0,\n position: 'absolute',\n pointerEvents: 'none',\n width: 0,\n overflow: 'hidden',\n } as React.CSSProperties,\n\n overflow: {\n display: 'inline-flex',\n alignItems: 'center',\n whiteSpace: 'nowrap',\n } as React.CSSProperties,\n} as const;\n\n// ============================================\n// SLIDING TEXT STYLES\n// ============================================\n\nexport const slidingTextBaseStyles = {\n container: {\n ...baseContainerStyle,\n display: 'inline-flex',\n overflow: 'hidden',\n verticalAlign: 'bottom',\n willChange: 'width',\n } as React.CSSProperties,\n\n content: {\n display: 'inline-flex',\n whiteSpace: 'pre',\n } as React.CSSProperties,\n\n token: {\n display: 'inline-block',\n willChange: 'transform, opacity, filter',\n backfaceVisibility: 'hidden',\n fontWeight: 500,\n } as React.CSSProperties,\n\n enterFrom: {\n opacity: 0,\n filter: `blur(${cssVar(CSS_VAR_NAMES.blurEnter)})`,\n } as React.CSSProperties,\n\n enterTo: {\n opacity: 1,\n transform: 'translate(0, 0)',\n filter: 'blur(0)',\n } as React.CSSProperties,\n\n exitActive: {\n opacity: 0,\n filter: `blur(${cssVar(CSS_VAR_NAMES.blurExit)})`,\n } as React.CSSProperties,\n\n // Direction variants\n verticalEnterFrom: {\n transform: `translateY(${cssVar(CSS_VAR_NAMES.offsetYEnter)})`,\n } as React.CSSProperties,\n\n verticalExitActive: {\n transform: `translateY(${cssVar(CSS_VAR_NAMES.offsetYExit)})`,\n } as React.CSSProperties,\n\n horizontalEnterFrom: {\n transform: `translateX(${cssVar(CSS_VAR_NAMES.offsetX)})`,\n } as React.CSSProperties,\n\n horizontalExitActive: {\n transform: `translateX(calc(${cssVar(CSS_VAR_NAMES.offsetX)} * -1))`,\n } as React.CSSProperties,\n} as const;\n\n// ============================================\n// RESPONSIVE OVERRIDES (para media queries en JS)\n// ============================================\n\nexport const responsiveOverrides = {\n mobile: {\n [CSS_VAR_NAMES.durationEnter]: '120ms',\n [CSS_VAR_NAMES.durationExit]: '100ms',\n [CSS_VAR_NAMES.durationCollapse]: '120ms',\n [CSS_VAR_NAMES.durationFlip]: '180ms',\n [CSS_VAR_NAMES.staggerEnter]: '8ms',\n [CSS_VAR_NAMES.blurEnter]: '2px',\n [CSS_VAR_NAMES.blurExit]: '1px',\n [CSS_VAR_NAMES.offsetYEnter]: '4px',\n [CSS_VAR_NAMES.offsetYExit]: '-4px',\n } as React.CSSProperties,\n\n smallMobile: {\n [CSS_VAR_NAMES.durationEnter]: '80ms',\n [CSS_VAR_NAMES.durationExit]: '60ms',\n [CSS_VAR_NAMES.durationFlip]: '120ms',\n [CSS_VAR_NAMES.staggerEnter]: '4ms',\n [CSS_VAR_NAMES.blurEnter]: '1px',\n [CSS_VAR_NAMES.blurExit]: '0px',\n [CSS_VAR_NAMES.offsetYEnter]: '2px',\n [CSS_VAR_NAMES.offsetYExit]: '-2px',\n } as React.CSSProperties,\n\n reducedMotion: {\n [CSS_VAR_NAMES.durationEnter]: '0ms',\n [CSS_VAR_NAMES.durationExit]: '0ms',\n [CSS_VAR_NAMES.durationCollapse]: '0ms',\n [CSS_VAR_NAMES.durationFlip]: '0ms',\n [CSS_VAR_NAMES.staggerEnter]: '0ms',\n [CSS_VAR_NAMES.staggerExit]: '0ms',\n [CSS_VAR_NAMES.blurEnter]: '0px',\n [CSS_VAR_NAMES.blurExit]: '0px',\n [CSS_VAR_NAMES.offsetYEnter]: '0px',\n [CSS_VAR_NAMES.offsetYExit]: '0px',\n [CSS_VAR_NAMES.scaleExit]: '1',\n } as React.CSSProperties,\n\n highContrast: {\n [CSS_VAR_NAMES.blurEnter]: '0px',\n [CSS_VAR_NAMES.blurExit]: '0px',\n } as React.CSSProperties,\n} as const;\n\n// ============================================\n// HELPER: Obtener estilos responsivos\n// ============================================\n\nexport const getResponsiveTokenStyles = (): React.CSSProperties => {\n if (typeof window === 'undefined') return CSS_VAR_VALUES as unknown as React.CSSProperties;\n\n const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;\n if (prefersReducedMotion) {\n return { ...CSS_VAR_VALUES, ...responsiveOverrides.reducedMotion } as unknown as React.CSSProperties;\n }\n\n const prefersHighContrast = window.matchMedia('(prefers-contrast: high)').matches;\n if (prefersHighContrast) {\n return { ...CSS_VAR_VALUES, ...responsiveOverrides.highContrast } as unknown as React.CSSProperties;\n }\n\n const width = window.innerWidth;\n if (width <= 480) {\n return { ...CSS_VAR_VALUES, ...responsiveOverrides.smallMobile } as unknown as React.CSSProperties;\n }\n if (width <= 768) {\n return { ...CSS_VAR_VALUES, ...responsiveOverrides.mobile } as unknown as React.CSSProperties;\n }\n\n return CSS_VAR_VALUES as unknown as React.CSSProperties;\n};\n\n// ============================================\n// CSS STRING GENERATOR (para inyección)\n// ============================================\n\nexport const generateCSSVariables = (): string => {\n const vars = Object.entries(CSS_VAR_VALUES)\n .map(([key, value]) => ` ${key}: ${value};`)\n .join('\\n');\n\n return `:root {\\n${vars}\\n}`;\n};\n\nexport const generateResponsiveCSS = (): string => {\n const propertyRules = generatePropertyRules();\n const baseVars = generateCSSVariables();\n\n const mobileVars = Object.entries(responsiveOverrides.mobile)\n .map(([key, value]) => ` ${key}: ${value};`)\n .join('\\n');\n\n const smallMobileVars = Object.entries(responsiveOverrides.smallMobile)\n .map(([key, value]) => ` ${key}: ${value};`)\n .join('\\n');\n\n const reducedMotionVars = Object.entries(responsiveOverrides.reducedMotion)\n .map(([key, value]) => ` ${key}: ${value};`)\n .join('\\n');\n\n const highContrastVars = Object.entries(responsiveOverrides.highContrast)\n .map(([key, value]) => ` ${key}: ${value};`)\n .join('\\n');\n\n return `\n/* ============================================\n @property DEFINITIONS (Modern CSS 2024)\n Enables typed, animatable CSS custom properties\n Baseline: July 2024 - All major browsers\n ============================================ */\n${propertyRules}\n\n/* ============================================\n CSS VARIABLES (derived from animationUtils.ts SSOT)\n ============================================ */\n${baseVars}\n\n@media (max-width: 768px) {\n :root {\n${mobileVars}\n }\n}\n\n@media (max-width: 480px) {\n :root {\n${smallMobileVars}\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n :root {\n${reducedMotionVars}\n }\n}\n\n@media (prefers-contrast: high) {\n :root {\n${highContrastVars}\n }\n}\n`.trim();\n};\n","import { generateResponsiveCSS } from './cssTokens';\n\nconst STYLE_ID = 'waapi-animation-primitives-vars';\n\nlet injected = false;\n\n/**\n * Inyecta las CSS variables en el documento.\n * Se llama automáticamente al importar el package.\n * Es idempotente - solo inyecta una vez.\n */\nexport const injectCSSVariables = (): void => {\n if (typeof document === 'undefined') return;\n if (injected) return;\n if (document.getElementById(STYLE_ID)) {\n injected = true;\n return;\n }\n\n const style = document.createElement('style');\n style.id = STYLE_ID;\n style.textContent = generateResponsiveCSS();\n document.head.appendChild(style);\n injected = true;\n};\n\n/**\n * Remueve las CSS variables inyectadas.\n * Útil para cleanup en tests o hot reload.\n */\nexport const removeCSSVariables = (): void => {\n if (typeof document === 'undefined') return;\n\n const style = document.getElementById(STYLE_ID);\n if (style) {\n style.remove();\n injected = false;\n }\n};\n\n/**\n * Fuerza la re-inyección de CSS variables.\n * Útil cuando cambian los valores dinámicamente.\n */\nexport const reinjectCSSVariables = (): void => {\n removeCSSVariables();\n injectCSSVariables();\n};\n\n// Auto-inject on import (solo en browser)\nif (typeof document !== 'undefined') {\n // Defer injection to avoid blocking\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', injectCSSVariables);\n } else {\n injectCSSVariables();\n }\n}\n","\"use client\"\n\nimport { useEffect, useRef, useMemo, useLayoutEffect } from \"react\"\n\nexport interface FormatOptions {\n style?: \"decimal\" | \"currency\" | \"percent\"\n currency?: string\n locale?: string\n minimumFractionDigits?: number\n maximumFractionDigits?: number\n useGrouping?: boolean\n}\n\nexport interface SlidingNumberProps {\n value: number\n duration?: number\n fontSize?: string\n fontWeight?: string\n color?: string\n digitHeight?: number\n stagger?: number\n motionBlur?: boolean\n format?: FormatOptions\n trend?: -1 | 0 | 1\n animationConfig?: {\n overshoot?: number\n mass?: number\n stiffness?: number\n }\n}\n\ninterface CharItem {\n char: string\n key: string\n isDigit: boolean\n position: number\n}\n\nfunction parseNumberToChars(value: number, format?: FormatOptions): CharItem[] {\n const locale = format?.locale || \"en-US\"\n const options: Intl.NumberFormatOptions = {\n style: format?.style || \"decimal\",\n minimumFractionDigits: format?.minimumFractionDigits,\n maximumFractionDigits: format?.maximumFractionDigits,\n useGrouping: format?.useGrouping ?? true,\n }\n\n if (format?.style === \"currency\" && format?.currency) {\n options.currency = format.currency\n }\n\n const formatter = new Intl.NumberFormat(locale, options)\n const parts = formatter.formatToParts(value)\n\n const result: CharItem[] = []\n\n let integerDigitCount = 0\n parts.forEach((part) => {\n if (part.type === \"integer\") {\n integerDigitCount += part.value.length\n }\n })\n\n let integerIndex = integerDigitCount\n let fractionIndex = 0\n let groupCount = 0\n\n parts.forEach((part, partIdx) => {\n if (part.type === \"integer\") {\n for (const char of part.value) {\n result.push({\n char,\n key: `int-${integerIndex}`,\n isDigit: true,\n position: integerIndex,\n })\n integerIndex--\n }\n } else if (part.type === \"fraction\") {\n for (const char of part.value) {\n fractionIndex++\n result.push({\n char,\n key: `frac-${fractionIndex}`,\n isDigit: true,\n position: -fractionIndex,\n })\n }\n } else if (part.type === \"decimal\") {\n result.push({ char: part.value, key: \"decimal\", isDigit: false, position: 0 })\n } else if (part.type === \"group\") {\n groupCount++\n result.push({ char: part.value, key: `group-${groupCount}`, isDigit: false, position: 0 })\n } else if (part.type === \"currency\") {\n result.push({ char: part.value, key: `currency-${partIdx}`, isDigit: false, position: 0 })\n } else if (part.type === \"percentSign\") {\n result.push({ char: part.value, key: \"percent\", isDigit: false, position: 0 })\n } else {\n result.push({ char: part.value, key: `symbol-${partIdx}`, isDigit: false, position: 0 })\n }\n })\n\n return result\n}\n\nexport function SlidingNumber({\n value,\n duration = 700,\n fontSize = \"3rem\",\n fontWeight = \"700\",\n color = \"#000\",\n digitHeight = 60,\n stagger = 30,\n motionBlur = true,\n format,\n trend = 0,\n animationConfig,\n}: SlidingNumberProps) {\n const computedEasing = useMemo(() => {\n if (animationConfig) {\n const { overshoot = 1, stiffness = 1, mass = 1 } = animationConfig\n const p1 = 0.34 * stiffness\n const p2 = 1 + 0.56 * overshoot\n const p3 = 0.64 / mass\n return `cubic-bezier(${Math.min(p1, 1).toFixed(2)}, ${Math.min(p2, 2).toFixed(2)}, ${Math.min(p3, 1).toFixed(2)}, 1)`\n }\n return \"cubic-bezier(0.34, 1.56, 0.64, 1)\"\n }, [animationConfig])\n\n const currentChars = useMemo(() => parseNumberToChars(value, format), [value, format])\n const prevKeysRef = useRef<Set<string>>(new Set())\n const isFirstRenderRef = useRef(true)\n\n const enteringKeys = useMemo(() => {\n if (isFirstRenderRef.current) return new Set<string>()\n const entering = new Set<string>()\n currentChars.forEach((c) => {\n if (!prevKeysRef.current.has(c.key)) {\n entering.add(c.key)\n }\n })\n return entering\n }, [currentChars])\n\n useEffect(() => {\n isFirstRenderRef.current = false\n prevKeysRef.current = new Set(currentChars.map((c) => c.key))\n }, [currentChars])\n\n const getDelay = (position: number) => {\n const absPos = Math.abs(position)\n return (absPos - 1) * stagger\n }\n\n return (\n <div\n style={{\n display: \"inline-flex\",\n alignItems: \"center\",\n fontSize,\n fontWeight,\n color,\n fontVariantNumeric: \"tabular-nums\",\n fontFamily: \"system-ui, -apple-system, sans-serif\",\n overflow: \"hidden\",\n }}\n >\n {currentChars.map((item) => {\n const isEntering = enteringKeys.has(item.key)\n\n if (item.isDigit) {\n return (\n <Digit\n key={item.key}\n digit={Number.parseInt(item.char)}\n duration={duration}\n digitHeight={digitHeight}\n delay={getDelay(item.position)}\n motionBlur={motionBlur}\n easing={computedEasing}\n isEntering={isEntering}\n trend={trend}\n />\n )\n }\n\n return (\n <Symbol key={item.key} char={item.char} isEntering={isEntering} duration={duration} easing={computedEasing} />\n )\n })}\n </div>\n )\n}\n\nfunction Symbol({\n char,\n isEntering,\n duration,\n easing,\n}: { char: string; isEntering: boolean; duration: number; easing: string }) {\n const ref = useRef<HTMLSpanElement>(null)\n const hasAnimatedRef = useRef(false)\n\n useLayoutEffect(() => {\n if (!ref.current || !isEntering || hasAnimatedRef.current) return\n hasAnimatedRef.current = true\n\n ref.current.animate(\n [\n { opacity: 0, transform: \"scale(0.8)\" },\n { opacity: 1, transform: \"scale(1)\" },\n ],\n {\n duration: duration * 0.3,\n easing,\n fill: \"forwards\",\n },\n )\n }, [isEntering, duration, easing])\n\n return (\n <span\n ref={ref}\n style={{\n display: \"inline-block\",\n whiteSpace: \"pre\",\n opacity: isEntering ? 0 : 1,\n }}\n >\n {char}\n </span>\n )\n}\n\ninterface DigitProps {\n digit: number\n duration: number\n digitHeight: number\n delay: number\n motionBlur: boolean\n easing: string\n isEntering: boolean\n trend: -1 | 0 | 1\n}\n\nfunction Digit({ digit, duration, digitHeight, delay, motionBlur, easing, isEntering, trend }: DigitProps) {\n const columnRef = useRef<HTMLDivElement>(null)\n const containerRef = useRef<HTMLDivElement>(null)\n const animationRef = useRef<Animation | null>(null)\n const filterId = useRef(`blur-${Math.random().toString(36).slice(2, 9)}`).current\n\n const currentOffsetRef = useRef<number | null>(null)\n const targetDigitRef = useRef(digit)\n const hasInitializedRef = useRef(false)\n\n const sequence = useMemo(() => {\n const seq: number[] = []\n for (let cycle = -1; cycle <= 1; cycle++) {\n for (let d = 0; d <= 9; d++) {\n seq.push(d)\n }\n }\n return seq\n }, [])\n\n useLayoutEffect(() => {\n if (!hasInitializedRef.current) {\n hasInitializedRef.current = true\n const offset = -(digit + 10) * digitHeight\n currentOffsetRef.current = offset\n targetDigitRef.current = digit\n\n if (columnRef.current) {\n columnRef.current.style.transform = `translateY(${offset}px)`\n }\n }\n }, [digit, digitHeight])\n\n useLayoutEffect(() => {\n if (!containerRef.current || !isEntering) return\n\n containerRef.current.animate(\n [\n { opacity: 0, transform: \"scale(0.5) translateY(-20px)\" },\n { opacity: 1, transform: \"scale(1) translateY(0)\" },\n ],\n {\n duration: duration * 0.4,\n easing,\n fill: \"forwards\",\n },\n )\n }, [isEntering, duration, easing])\n\n useEffect(() => {\n targetDigitRef.current = digit\n\n if (!hasInitializedRef.current) return\n\n const currentDigit =\n currentOffsetRef.current !== null\n ? (((Math.round(-currentOffsetRef.current / digitHeight) - 10) % 10) + 10) % 10\n : digit\n\n if (digit === currentDigit && currentOffsetRef.current !== null) {\n return\n }\n\n animateToDigit(digit)\n }, [digit, digitHeight])\n\n const animateToDigit = (target: number) => {\n if (!columnRef.current || !containerRef.current) return\n\n if (animationRef.current) {\n const computedStyle = getComputedStyle(columnRef.current)\n const matrix = new DOMMatrix(computedStyle.transform)\n currentOffsetRef.current = matrix.m42\n animationRef.current.cancel()\n animationRef.current = null\n columnRef.current.style.transform = `translateY(${currentOffsetRef.current}px)`\n }\n\n const rawIndex = currentOffsetRef.current !== null ? -currentOffsetRef.current / digitHeight : target + 10\n\n const currentDigitFromOffset = (((Math.round(rawIndex) - 10) % 10) + 10) % 10\n const from = currentDigitFromOffset\n\n if (target === from && currentOffsetRef.current !== null) {\n const normalizedOffset = -(target + 10) * digitHeight\n if (columnRef.current) {\n const currentAnim = animationRef.current as Animation | null\n if (currentAnim) {\n currentAnim.cancel()\n }\n columnRef.current.style.transform = `translateY(${normalizedOffset}px)`\n }\n currentOffsetRef.current = normalizedOffset\n return\n }\n\n let diff: number\n if (trend === 1) {\n diff = target >= from ? target - from : 10 - from + target\n } else if (trend === -1) {\n diff = target <= from ? target - from : target - from - 10\n } else {\n diff = target - from\n if (diff > 5) diff -= 10\n else if (diff < -5) diff += 10\n }\n\n const startOffset = currentOffsetRef.current ?? -(from + 10) * digitHeight\n const endOffset = startOffset - diff * digitHeight\n\n const blurEl = document.getElementById(filterId)?.querySelector(\"feGaussianBlur\")\n if (motionBlur && blurEl) {\n const intensity = Math.min(Math.abs(diff) * 1.2, 6)\n blurEl.setAttribute(\"stdDeviation\", `0,${intensity}`)\n containerRef.current.style.filter = `url(#${filterId})`\n }\n\n const anim = columnRef.current.animate(\n [{ transform: `translateY(${startOffset}px)` }, { transform: `translateY(${endOffset}px)` }],\n {\n duration,\n delay,\n easing,\n fill: \"forwards\",\n },\n )\n\n animationRef.current = anim\n\n anim.onfinish = () => {\n const normalizedOffset = -(target + 10) * digitHeight\n if (columnRef.current) {\n anim.cancel()\n columnRef.current.style.transform = `translateY(${normalizedOffset}px)`\n }\n currentOffsetRef.current = normalizedOffset\n\n if (containerRef.current) {\n containerRef.current.style.filter = \"none\"\n }\n if (blurEl) {\n blurEl.setAttribute(\"stdDeviation\", \"0,0\")\n }\n\n animationRef.current = null\n\n if (targetDigitRef.current !== target) {\n requestAnimationFrame(() => {\n animateToDigit(targetDigitRef.current)\n })\n }\n }\n\n anim.oncancel = () => {\n animationRef.current = null\n }\n }\n\n return (\n <>\n <svg style={{ position: \"absolute\", width: 0, height: 0 }} aria-hidden=\"true\">\n <defs>\n <filter id={filterId}>\n <feGaussianBlur in=\"SourceGraphic\" stdDeviation=\"0,0\" />\n </filter>\n </defs>\n </svg>\n\n <div\n ref={containerRef}\n style={{\n position: \"relative\",\n height: `${digitHeight}px`,\n overflow: \"hidden\",\n width: \"0.65em\",\n textAlign: \"center\",\n opacity: isEntering ? 0 : 1,\n }}\n >\n <div\n ref={columnRef}\n style={{\n position: \"absolute\",\n left: 0,\n right: 0,\n willChange: \"transform\",\n }}\n >\n {sequence.map((d, i) => (\n <div\n key={i}\n style={{\n height: `${digitHeight}px`,\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n }}\n >\n {d}\n </div>\n ))}\n </div>\n </div>\n </>\n )\n}\n","import React, { createContext, useContext, useState, useCallback, useRef, useMemo, useEffect } from 'react';\n\n// ============================================\n// ENHANCED DEBUG EVENT TYPES\n// ============================================\n\n// Timing data for measuring actual vs expected animation duration\nexport interface TimingData {\n startTime: number;\n endTime?: number;\n expectedDuration: number;\n actualDuration?: number;\n deviation?: number;\n deviationPercent?: number;\n}\n\n// Style comparison data\nexport interface StyleData {\n property: string;\n expected: string;\n actual: string;\n matches: boolean;\n}\n\n// Position data with full DOMRect info\nexport interface PositionData {\n element: string;\n x: number;\n y: number;\n width: number;\n height: number;\n delta?: { x: number; y: number };\n}\n\n// Animation metadata\nexport interface AnimationData {\n name: string;\n phase: 'start' | 'running' | 'complete' | 'cancelled';\n progress?: number;\n easing?: string;\n fill?: string;\n}\n\n// All event types\nexport type DebugEventType =\n // Token events\n | 'token-add'\n | 'token-remove'\n | 'token-reorder'\n | 'token-exit-start'\n | 'token-exit-complete'\n | 'token-dom-remove'\n | 'overflow-token-remove'\n // FLIP events\n | 'flip-animation'\n | 'flip-animation-complete'\n | 'flip-all-animations-complete'\n | 'flip-executing-callback'\n | 'flip-measure-start'\n | 'flip-position-measured'\n | 'flip-invalid-rect'\n | 'flip-invalid-delta'\n | 'flip-manual-trigger'\n | 'flip-capture-positions'\n | 'flip-position-captured'\n | 'flip-skipped'\n // WAAPI events\n | 'waapi-exit-start'\n | 'waapi-exit-complete'\n | 'waapi-flip-animation'\n // Separated timing events\n | 'exit-fade-complete'\n | 'orchestration-complete'\n // Enhanced quantitative events\n | 'animation-start-detailed'\n | 'animation-complete-detailed'\n | 'animation-timing'\n | 'style-capture'\n | 'style-mismatch'\n | 'position-capture'\n | 'position-delta'\n | 'choreography-overlap'\n | 'choreography-sequence'\n // Enter animation events\n | 'text-enter-start'\n | 'text-enter-complete'\n // Reorder events\n | 'reorder-item-enter'\n | 'reorder-item-exit'\n | 'reorder-item-exit-complete'\n | 'reorder-flip-start'\n | 'reorder-flip-complete'\n | 'reorder-flip-before-capture'\n | 'reorder-flip-after-capture'\n | 'reorder-flip-after-positions'\n | 'reorder-flip-after-precaptured'\n | 'reorder-flip-delta'\n | 'reorder-flip-reflow-trigger'\n | 'reorder-flip-skipped'\n | 'reorder-flip-filter'\n | 'reorder-stagger-start'\n | 'reorder-ghost-created'\n | 'reorder-ghost-removed'\n | 'reorder-register-element'\n | 'reorder-animation-start'\n | 'reorder-animation-complete'\n | 'reorder-animation-setup'\n | 'reorder-animation-created'\n | 'reorder-animation-init'\n | 'reorder-animation-error'\n | 'reorder-exit-check'\n | 'reorder-exit-rejected'\n | 'reorder-reflow-triggered'\n | 'reorder-transform-applied'\n | 'reorder-display-none'\n | 'reorder-position-absolute'\n | 'reorder-after-immediate'\n | 'reorder-exit-cleanup'\n | 'reorder-await-start'\n | 'reorder-await-complete'\n | 'reorder-enter-start'\n | 'reorder-enter-complete'\n // Separator events\n | 'separator-animation-start'\n | 'separator-animation-state-only'\n | 'separator-animation-skipped'\n | 'separator-flip-start'\n | 'separator-animation-complete'\n | 'separator-state-cleanup'\n // Test events\n | 'test-suite-start'\n | 'test-suite-complete'\n | 'test-suite-error'\n | 'test-suite-stopped'\n | 'test-start'\n | 'test-complete'\n | 'test-error'\n | 'test-progress'\n | 'test-flip-capture'\n | 'test-export'\n | 'test-manual-remove'\n // Legacy events\n | 'triggering-flip-before-absolute'\n | 'state-change'\n | 'render'\n | 'animation-complete-called'\n | 'scheduling-raf'\n | 'raf-executed'\n | 'component-unmounted'\n | 'token-removing-from-layout'\n | 'exit-completed-ids-updated'\n | 'exit-completed-change'\n | 'registering-callback'\n | 'callback-fired';\n\nexport interface DebugEvent {\n timestamp: number;\n perfNow?: number; // High-precision timing via performance.now()\n type: DebugEventType;\n source: string;\n message: string;\n\n // Enhanced quantitative data (optional)\n timing?: TimingData;\n styles?: StyleData[];\n position?: PositionData;\n animation?: AnimationData;\n\n // Generic data field (existing)\n data?: Record<string, unknown>;\n}\n\ninterface DebugContextValue {\n events: DebugEvent[];\n isEnabled: boolean;\n enableDebug: () => void;\n disableDebug: () => void;\n toggleDebug: () => void;\n logEvent: (event: Omit<DebugEvent, 'timestamp' | 'perfNow'>) => void;\n clearEvents: () => void;\n getEventLog: () => string;\n exportToCSV: () => string;\n // P3: Collect mode for tests\n startCollecting: () => void;\n stopCollecting: () => DebugEvent[];\n isCollecting: boolean;\n // Utility: flush pending events immediately\n flushEvents: () => void;\n}\n\nconst DebugContext = createContext<DebugContextValue | null>(null);\n\nexport const useDebug = () => {\n const context = useContext(DebugContext);\n if (!context) {\n throw new Error('useDebug must be used within DebugProvider');\n }\n return context;\n};\n\nexport const DebugProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {\n const [events, setEvents] = useState<DebugEvent[]>([]);\n const [isEnabled, setIsEnabled] = useState(true);\n const [isCollecting, setIsCollecting] = useState(false);\n const isEnabledRef = useRef(isEnabled);\n\n // RAF micro-batching refs\n const bufferRef = useRef<DebugEvent[]>([]);\n const rafIdRef = useRef<number | null>(null);\n\n // Collect mode ref for tests\n const collectedEventsRef = useRef<DebugEvent[]>([]);\n const isCollectingRef = useRef(false);\n\n // Keep refs in sync with state\n isEnabledRef.current = isEnabled;\n isCollectingRef.current = isCollecting;\n\n // Flush buffered events to state (called by RAF or manually)\n const flushBuffer = useCallback(() => {\n if (bufferRef.current.length === 0) return;\n\n const eventsToFlush = [...bufferRef.current];\n bufferRef.current = [];\n rafIdRef.current = null;\n\n setEvents(prev => [...prev, ...eventsToFlush]);\n }, []);\n\n // Cleanup RAF on unmount\n useEffect(() => {\n return () => {\n if (rafIdRef.current !== null) {\n cancelAnimationFrame(rafIdRef.current);\n // Flush remaining events on unmount\n if (bufferRef.current.length > 0) {\n setEvents(prev => [...prev, ...bufferRef.current]);\n bufferRef.current = [];\n }\n }\n };\n }, []);\n\n const logEvent = useCallback((event: Omit<DebugEvent, 'timestamp' | 'perfNow'>) => {\n if (!__DEBUG__) return; // Guard: skip entirely in production\n\n // Use ref to check if enabled to avoid dependency on isEnabled state\n if (!isEnabledRef.current) return;\n\n const fullEvent: DebugEvent = {\n ...event,\n timestamp: Date.now(),\n perfNow: performance.now(), // High-precision timing\n };\n\n // If in collect mode, also save to collected events\n if (isCollectingRef.current) {\n collectedEventsRef.current.push(fullEvent);\n }\n\n // Buffer the event instead of setting state immediately\n bufferRef.current.push(fullEvent);\n\n // Schedule flush on next animation frame (if not already scheduled)\n if (rafIdRef.current === null) {\n rafIdRef.current = requestAnimationFrame(flushBuffer);\n }\n\n // Console logging with color coding (immediate, outside of React state)\n const colors: Record<string, string> = {\n // Token events\n 'token-add': '#22c55e',\n 'token-remove': '#ef4444',\n 'token-reorder': '#f59e0b',\n 'token-exit-start': '#f97316',\n 'token-exit-complete': '#8b5cf6',\n 'token-dom-remove': '#dc2626',\n // FLIP events\n 'flip-animation': '#3b82f6',\n 'flip-animation-complete': '#06b6d4',\n 'flip-all-animations-complete': '#10b981',\n 'flip-executing-callback': '#14b8a6',\n 'flip-measure-start': '#0ea5e9',\n 'flip-position-measured': '#3b82f6',\n 'flip-invalid-rect': '#f59e0b',\n 'flip-invalid-delta': '#ef4444',\n 'flip-manual-trigger': '#06b6d4',\n 'flip-capture-positions': '#8b5cf6',\n 'flip-position-captured': '#a78bfa',\n // WAAPI events\n 'waapi-exit-start': '#f97316',\n 'waapi-exit-complete': '#22c55e',\n 'waapi-flip-animation': '#3b82f6',\n // Separated timing events\n 'exit-fade-complete': '#f97316',\n 'orchestration-complete': '#10b981',\n // Enhanced quantitative events\n 'animation-start-detailed': '#f97316',\n 'animation-complete-detailed': '#22c55e',\n 'animation-timing': '#06b6d4',\n 'style-capture': '#8b5cf6',\n 'style-mismatch': '#ef4444',\n 'position-capture': '#3b82f6',\n 'position-delta': '#0ea5e9',\n 'choreography-overlap': '#f59e0b',\n 'choreography-sequence': '#14b8a6',\n // Enter animation events\n 'text-enter-start': '#22c55e',\n 'text-enter-complete': '#10b981',\n // Reorder events\n 'reorder-item-enter': '#22c55e',\n 'reorder-item-exit': '#ef4444',\n 'reorder-item-exit-complete': '#f97316',\n 'reorder-flip-start': '#3b82f6',\n 'reorder-flip-complete': '#06b6d4',\n 'reorder-flip-before-capture': '#8b5cf6',\n 'reorder-flip-after-capture': '#a78bfa',\n 'reorder-flip-delta': '#c4b5fd',\n 'reorder-flip-reflow-trigger': '#f59e0b',\n 'reorder-flip-skipped': '#ef4444',\n 'reorder-flip-filter': '#a855f7',\n 'reorder-stagger-start': '#a855f7',\n 'reorder-ghost-created': '#f59e0b',\n 'reorder-ghost-removed': '#dc2626',\n 'reorder-register-element': '#6b7280',\n 'reorder-animation-start': '#f97316',\n 'reorder-animation-complete': '#10b981',\n 'reorder-animation-setup': '#f59e0b',\n 'reorder-animation-created': '#3b82f6',\n 'reorder-animation-init': '#3b82f6',\n 'reorder-animation-error': '#ef4444',\n 'reorder-exit-check': '#6b7280',\n 'reorder-exit-rejected': '#f59e0b',\n 'reorder-exit-cleanup': '#22c55e',\n 'reorder-reflow-triggered': '#06b6d4',\n 'reorder-transform-applied': '#8b5cf6',\n 'reorder-display-none': '#f97316',\n 'reorder-position-absolute': '#fb923c',\n 'reorder-after-immediate': '#a78bfa',\n 'reorder-flip-after-positions': '#c4b5fd',\n 'reorder-flip-after-precaptured': '#c4b5fd',\n 'reorder-await-start': '#8b5cf6',\n 'reorder-await-complete': '#a78bfa',\n 'reorder-enter-start': '#22c55e',\n 'reorder-enter-complete': '#10b981',\n // Test events\n 'test-suite-start': '#8b5cf6',\n 'test-suite-complete': '#10b981',\n 'test-suite-error': '#ef4444',\n 'test-suite-stopped': '#f59e0b',\n 'test-start': '#3b82f6',\n 'test-complete': '#22c55e',\n 'test-error': '#ef4444',\n 'test-progress': '#06b6d4',\n 'test-flip-capture': '#a78bfa',\n 'test-export': '#14b8a6',\n 'test-manual-remove': '#f97316',\n // Legacy events\n 'triggering-flip-before-absolute': '#f97316',\n 'state-change': '#06b6d4',\n 'render': '#6b7280',\n 'animation-complete-called': '#8b5cf6',\n 'scheduling-raf': '#a855f7',\n 'raf-executed': '#c084fc',\n 'component-unmounted': '#ef4444',\n 'token-removing-from-layout': '#f97316',\n 'exit-completed-ids-updated': '#fb923c',\n 'exit-completed-change': '#f59e0b',\n 'registering-callback': '#34d399',\n 'callback-fired': '#10b981',\n };\n\n console.log(\n `%c[${event.type}] %c${event.source}%c: ${event.message}`,\n `color: ${colors[event.type] || '#6b7280'}; font-weight: bold`,\n 'color: #9ca3af',\n 'color: inherit',\n event.data || ''\n );\n }, [flushBuffer]); // Depends on flushBuffer for RAF scheduling\n\n const clearEvents = useCallback(() => {\n if (!__DEBUG__) return;\n setEvents([]);\n }, []);\n\n const enableDebug = useCallback(() => {\n if (!__DEBUG__) return;\n setIsEnabled(true);\n }, []);\n\n const disableDebug = useCallback(() => {\n if (!__DEBUG__) return;\n setIsEnabled(false);\n }, []);\n\n const toggleDebug = useCallback(() => {\n if (!__DEBUG__) return;\n setIsEnabled(prev => !prev);\n }, []);\n\n // P3: Collect mode for tests - start collecting events\n const startCollecting = useCallback(() => {\n if (!__DEBUG__) return;\n collectedEventsRef.current = [];\n setIsCollecting(true);\n }, []);\n\n // P3: Collect mode for tests - stop and return collected events\n const stopCollecting = useCallback((): DebugEvent[] => {\n if (!__DEBUG__) return [];\n setIsCollecting(false);\n const collected = [...collectedEventsRef.current];\n collectedEventsRef.current = [];\n return collected;\n }, []);\n\n // Utility: flush pending buffered events immediately\n const flushEvents = useCallback(() => {\n if (!__DEBUG__) return;\n if (rafIdRef.current !== null) {\n cancelAnimationFrame(rafIdRef.current);\n rafIdRef.current = null;\n }\n flushBuffer();\n }, [flushBuffer]);\n\n const exportToCSV = useCallback(() => {\n if (!__DEBUG__) return '';\n\n // CSV Header - includes all quantitative data + perfNow\n const headers = [\n 'Index', 'Timestamp', 'PerfNow', 'Time', 'Type', 'Source', 'Message',\n 'Expected_Duration', 'Actual_Duration', 'Deviation', 'Deviation_Percent',\n 'Animation_Name', 'Animation_Phase', 'Animation_Easing',\n 'Element', 'Pos_X', 'Pos_Y', 'Width', 'Height', 'Delta_X', 'Delta_Y',\n 'Style_Checks', 'Style_Matches', 'Style_Mismatches',\n 'Data'\n ];\n\n // Sort events by timestamp before processing\n const sortedEvents = [...events].sort((a, b) => a.timestamp - b.timestamp);\n\n // CSV Rows - includes all captured data\n const rows = sortedEvents.map((e, i) => {\n const time = new Date(e.timestamp).toLocaleTimeString('en-US', {\n hour12: false,\n hour: '2-digit',\n minute: '2-digit',\n second: '2-digit'\n });\n\n // Flatten data object into key=value pairs\n const dataStr = e.data\n ? Object.entries(e.data)\n .map(([key, val]) => {\n if (Array.isArray(val)) {\n return `${key}=[${val.join(',')}]`;\n } else if (typeof val === 'object' && val !== null) {\n return `${key}=${JSON.stringify(val)}`;\n }\n return `${key}=${val}`;\n })\n .join(';')\n : '';\n\n // Style analysis\n const styleMatches = e.styles?.filter(s => s.matches).length ?? '';\n const styleMismatches = e.styles?.filter(s => !s.matches).length ?? '';\n\n return [\n i + 1,\n e.timestamp,\n e.perfNow?.toFixed(3) ?? '',\n time,\n e.type,\n e.source,\n e.message,\n e.timing?.expectedDuration ?? '',\n e.timing?.actualDuration?.toFixed(2) ?? '',\n e.timing?.deviation?.toFixed(2) ?? '',\n e.timing?.deviationPercent?.toFixed(2) ?? '',\n e.animation?.name ?? '',\n e.animation?.phase ?? '',\n e.animation?.easing ?? '',\n e.position?.element ?? '',\n e.position?.x?.toFixed(2) ?? '',\n e.position?.y?.toFixed(2) ?? '',\n e.position?.width?.toFixed(2) ?? '',\n e.position?.height?.toFixed(2) ?? '',\n e.position?.delta?.x?.toFixed(2) ?? '',\n e.position?.delta?.y?.toFixed(2) ?? '',\n e.styles?.length ?? '',\n styleMatches,\n styleMismatches,\n dataStr\n ];\n });\n\n // Convert to CSV format\n const csvContent = [\n headers.join(','),\n ...rows.map(row =>\n row.map(cell => {\n const cellStr = String(cell);\n if (cellStr.includes(',') || cellStr.includes('\"') || cellStr.includes('\\n')) {\n return `\"${cellStr.replace(/\"/g, '\"\"')}\"`;\n }\n return cellStr;\n }).join(',')\n )\n ].join('\\n');\n\n return csvContent;\n }, [events]);\n\n const getEventLog = useCallback(() => {\n if (!__DEBUG__) return '';\n\n return events.map((e, i) => {\n const time = new Date(e.timestamp).toLocaleTimeString();\n const parts: string[] = [\n `[${i + 1}] ${time} [${e.type}] ${e.source}: ${e.message}`\n ];\n\n if (e.timing) {\n const t = e.timing;\n parts.push(` Timing: ${t.expectedDuration}ms expected → ${t.actualDuration?.toFixed(1) ?? '?'}ms actual (${t.deviation && t.deviation >= 0 ? '+' : ''}${t.deviation?.toFixed(1) ?? '?'}ms)`);\n }\n\n if (e.animation) {\n parts.push(` Animation: ${e.animation.name} [${e.animation.phase}] easing=${e.animation.easing || 'default'}`);\n }\n\n if (e.position) {\n const p = e.position;\n let posStr = ` Position: ${p.element} @ (${p.x.toFixed(0)}, ${p.y.toFixed(0)}) ${p.width.toFixed(0)}x${p.height.toFixed(0)}`;\n if (p.delta) {\n posStr += ` Δ(${p.delta.x.toFixed(1)}, ${p.delta.y.toFixed(1)})`;\n }\n parts.push(posStr);\n }\n\n if (e.styles && e.styles.length > 0) {\n const matches = e.styles.filter(s => s.matches).length;\n const mismatches = e.styles.filter(s => !s.matches).length;\n parts.push(` Styles: ${e.styles.length} checks (${matches} match, ${mismatches} mismatch)`);\n e.styles.filter(s => !s.matches).forEach(s => {\n parts.push(` ✗ ${s.property}: expected \"${s.expected}\" got \"${s.actual}\"`);\n });\n }\n\n if (e.data && Object.keys(e.data).length > 0) {\n parts.push(` Data: ${JSON.stringify(e.data, null, 2).split('\\n').join('\\n ')}`);\n }\n\n return parts.join('\\n');\n }).join('\\n\\n');\n }, [events]);\n\n const contextValue = useMemo(() => ({\n events: __DEBUG__ ? events : [],\n isEnabled: __DEBUG__ ? isEnabled : false,\n isCollecting: __DEBUG__ ? isCollecting : false,\n enableDebug,\n disableDebug,\n toggleDebug,\n logEvent,\n clearEvents,\n getEventLog,\n exportToCSV,\n startCollecting,\n stopCollecting,\n flushEvents,\n }), [events, isEnabled, isCollecting, enableDebug, disableDebug, toggleDebug, logEvent, clearEvents, getEventLog, exportToCSV, startCollecting, stopCollecting, flushEvents]);\n\n return (\n <DebugContext.Provider value={contextValue}>\n {children}\n </DebugContext.Provider>\n );\n};\n","// ============================================\n// DEBUG CAPTURE UTILITIES\n// Functions for capturing quantitative animation data\n// ============================================\n\n// Types\nexport interface StyleCapture {\n opacity: string;\n transform: string;\n filter: string;\n width: string;\n height: string;\n marginRight: string;\n marginLeft: string;\n position: string;\n visibility: string;\n pointerEvents: string;\n}\n\nexport interface PositionCapture {\n x: number;\n y: number;\n width: number;\n height: number;\n top: number;\n left: number;\n right: number;\n bottom: number;\n}\n\nexport interface StyleComparison {\n property: string;\n expected: string;\n actual: string;\n matches: boolean;\n}\n\nexport interface TimingResult {\n startTime: number;\n endTime: number;\n expectedDuration: number;\n actualDuration: number;\n deviation: number;\n deviationPercent: number;\n}\n\nexport interface AnimationTimer {\n start: number;\n expectedDuration: number;\n end: () => TimingResult;\n}\n\n// ============================================\n// STYLE CAPTURE\n// ============================================\n\n/**\n * Capture relevant computed styles from an element\n * These are the styles we animate and need to verify\n */\nexport function captureComputedStyles(el: HTMLElement): StyleCapture {\n const computed = getComputedStyle(el);\n return {\n opacity: computed.opacity,\n transform: computed.transform,\n filter: computed.filter,\n width: computed.width,\n height: computed.height,\n marginRight: computed.marginRight,\n marginLeft: computed.marginLeft,\n position: computed.position,\n visibility: computed.visibility,\n pointerEvents: computed.pointerEvents,\n };\n}\n\n/**\n * Capture styles as a simple key-value object for logging\n */\nexport function captureStylesForLog(el: HTMLElement): Record<string, string> {\n const styles = captureComputedStyles(el);\n return {\n opacity: styles.opacity,\n transform: styles.transform === 'none' ? 'none' : styles.transform,\n filter: styles.filter === 'none' ? 'none' : styles.filter,\n width: styles.width,\n marginRight: styles.marginRight,\n position: styles.position,\n };\n}\n\n// ============================================\n// POSITION CAPTURE\n// ============================================\n\n/**\n * Capture complete position information from DOMRect\n */\nexport function capturePosition(el: HTMLElement): PositionCapture {\n const rect = el.getBoundingClientRect();\n return {\n x: Math.round(rect.x * 100) / 100,\n y: Math.round(rect.y * 100) / 100,\n width: Math.round(rect.width * 100) / 100,\n height: Math.round(rect.height * 100) / 100,\n top: Math.round(rect.top * 100) / 100,\n left: Math.round(rect.left * 100) / 100,\n right: Math.round(rect.right * 100) / 100,\n bottom: Math.round(rect.bottom * 100) / 100,\n };\n}\n\n/**\n * Capture position as simple object for logging\n */\nexport function capturePositionForLog(el: HTMLElement): Record<string, number> {\n const pos = capturePosition(el);\n return {\n x: pos.x,\n y: pos.y,\n w: pos.width,\n h: pos.height,\n };\n}\n\n/**\n * Calculate delta between two positions\n */\nexport function calculatePositionDelta(\n before: PositionCapture,\n after: PositionCapture\n): { deltaX: number; deltaY: number; deltaWidth: number; deltaHeight: number } {\n return {\n deltaX: Math.round((before.left - after.left) * 100) / 100,\n deltaY: Math.round((before.top - after.top) * 100) / 100,\n deltaWidth: Math.round((before.width - after.width) * 100) / 100,\n deltaHeight: Math.round((before.height - after.height) * 100) / 100,\n };\n}\n\n// ============================================\n// STYLE COMPARISON\n// ============================================\n\n/**\n * Compare expected styles vs actual computed styles\n * Used to verify animations applied correctly\n */\nexport function compareStyles(\n expected: Record<string, string>,\n el: HTMLElement\n): StyleComparison[] {\n const actual = captureComputedStyles(el);\n\n return Object.entries(expected).map(([prop, expectedVal]) => {\n const actualVal = actual[prop as keyof StyleCapture] || '';\n\n // Normalize values for comparison\n const normalizedExpected = normalizeStyleValue(prop, expectedVal);\n const normalizedActual = normalizeStyleValue(prop, actualVal);\n\n return {\n property: prop,\n expected: expectedVal,\n actual: actualVal,\n matches: normalizedExpected === normalizedActual,\n };\n });\n}\n\n/**\n * Normalize style values for comparison\n * Handles browser quirks and formatting differences\n */\nfunction normalizeStyleValue(property: string, value: string): string {\n if (!value) return '';\n\n // Normalize opacity (some browsers return \"1\" vs \"1.0\")\n if (property === 'opacity') {\n return parseFloat(value).toString();\n }\n\n // Normalize transform (handle matrix vs translate)\n if (property === 'transform') {\n if (value === 'none') return 'none';\n // Extract translation values if present\n const match = value.match(/matrix\\(([^)]+)\\)/);\n if (match && match[1]) {\n const values = match[1].split(',').map(v => parseFloat(v.trim()));\n // matrix(a, b, c, d, tx, ty) - tx is values[4], ty is values[5]\n const tx = values[4];\n const ty = values[5];\n if (tx !== undefined && ty !== undefined) {\n return `translate(${Math.round(tx)}px, ${Math.round(ty)}px)`;\n }\n }\n return value;\n }\n\n // Normalize width/height (round to nearest pixel)\n if (property === 'width' || property === 'height' || property.includes('margin')) {\n const num = parseFloat(value);\n if (!isNaN(num)) {\n return `${Math.round(num)}px`;\n }\n }\n\n // Normalize filter blur\n if (property === 'filter') {\n if (value === 'none') return 'none';\n const blurMatch = value.match(/blur\\(([^)]+)\\)/);\n if (blurMatch && blurMatch[1]) {\n const blurValue = parseFloat(blurMatch[1]);\n return `blur(${Math.round(blurValue)}px)`;\n }\n }\n\n return value;\n}\n\n// ============================================\n// TIMING\n// ============================================\n\n/**\n * Create a timer for measuring actual animation duration\n * Uses performance.now() for high precision\n */\nexport function createAnimationTimer(expectedDuration: number): AnimationTimer {\n const startTime = performance.now();\n\n return {\n start: startTime,\n expectedDuration,\n end: () => {\n const endTime = performance.now();\n const actualDuration = endTime - startTime;\n const deviation = actualDuration - expectedDuration;\n\n return {\n startTime: Math.round(startTime * 100) / 100,\n endTime: Math.round(endTime * 100) / 100,\n expectedDuration,\n actualDuration: Math.round(actualDuration * 100) / 100,\n deviation: Math.round(deviation * 100) / 100,\n deviationPercent: Math.round((deviation / expectedDuration) * 10000) / 100,\n };\n },\n };\n}\n\n/**\n * Format timing result for display\n */\nexport function formatTimingResult(timing: TimingResult): string {\n const sign = timing.deviation >= 0 ? '+' : '';\n const status = Math.abs(timing.deviation) < 10 ? '✅' : '⚠️';\n return `Expected: ${timing.expectedDuration}ms | Actual: ${timing.actualDuration}ms | Deviation: ${sign}${timing.deviation}ms ${status}`;\n}\n\n// ============================================\n// ANIMATION INFO HELPERS\n// ============================================\n\n/**\n * Extract animation info from WAAPI Animation object\n */\nexport function captureAnimationInfo(animation: Animation): Record<string, unknown> {\n const timing = animation.effect?.getTiming?.();\n return {\n id: animation.id,\n playState: animation.playState,\n currentTime: animation.currentTime,\n playbackRate: animation.playbackRate,\n pending: animation.pending,\n duration: timing?.duration,\n easing: timing?.easing,\n fill: timing?.fill,\n };\n}\n\n/**\n * Capture all active animations on an element\n */\nexport function captureElementAnimations(el: HTMLElement): Record<string, unknown>[] {\n const animations = el.getAnimations();\n return animations.map(captureAnimationInfo);\n}\n","// ============================================\n// CHOREOGRAPHY TRACKER\n// Track animation timeline and detect overlaps\n// ============================================\n\nexport interface AnimationInfo {\n id: string;\n type: 'exit-fade' | 'exit-collapse' | 'flip' | 'enter' | 'width-collapse' | 'separator-exit';\n elementId: string;\n startTime: number;\n expectedDuration: number;\n endTime?: number;\n actualDuration?: number;\n}\n\nexport interface TimelineEvent {\n time: number;\n event: 'animation-start' | 'animation-end' | 'overlap-detected';\n animationId: string;\n type?: string;\n overlappingWith?: string[];\n expectedDuration?: number;\n actualDuration?: number;\n deviation?: number;\n}\n\nexport interface OverlapInfo {\n animation1: string;\n animation2: string;\n overlapStart: number;\n overlapDuration: number;\n}\n\nexport interface ChoreographySummary {\n totalAnimations: number;\n totalDuration: number;\n overlaps: OverlapInfo[];\n timeline: TimelineEvent[];\n activeAnimations: AnimationInfo[];\n}\n\n/**\n * ChoreographyTracker - Singleton for tracking animation choreography\n * Detects overlaps, generates timeline, measures actual vs expected timing\n */\nclass ChoreographyTrackerClass {\n private activeAnimations: Map<string, AnimationInfo> = new Map();\n private timeline: TimelineEvent[] = [];\n private completedAnimations: AnimationInfo[] = [];\n private sessionStartTime: number = 0;\n\n /**\n * Start a new tracking session (call when debug panel is cleared)\n */\n startSession(): void {\n if (!__DEBUG__) return;\n\n this.activeAnimations.clear();\n this.timeline = [];\n this.completedAnimations = [];\n this.sessionStartTime = performance.now();\n }\n\n /**\n * Get relative time since session start\n */\n private getRelativeTime(): number {\n return Math.round((performance.now() - this.sessionStartTime) * 100) / 100;\n }\n\n /**\n * Start tracking an animation\n */\n startAnimation(\n id: string,\n type: AnimationInfo['type'],\n elementId: string,\n expectedDuration: number\n ): void {\n if (!__DEBUG__) return;\n\n const startTime = this.getRelativeTime();\n\n const info: AnimationInfo = {\n id,\n type,\n elementId,\n startTime,\n expectedDuration,\n };\n\n this.activeAnimations.set(id, info);\n\n // Detect overlaps with currently running animations\n const overlaps = this.detectOverlaps(id);\n\n // Log start event\n this.timeline.push({\n time: startTime,\n event: 'animation-start',\n animationId: id,\n type,\n expectedDuration,\n overlappingWith: overlaps.length > 0 ? overlaps : undefined,\n });\n\n // Log overlap event if detected\n if (overlaps.length > 0) {\n this.timeline.push({\n time: startTime,\n event: 'overlap-detected',\n animationId: id,\n overlappingWith: overlaps,\n });\n }\n }\n\n /**\n * End tracking an animation\n */\n endAnimation(id: string): AnimationInfo | undefined {\n if (!__DEBUG__) return undefined;\n\n const info = this.activeAnimations.get(id);\n if (!info) return undefined;\n\n const endTime = this.getRelativeTime();\n const actualDuration = endTime - info.startTime;\n\n info.endTime = endTime;\n info.actualDuration = Math.round(actualDuration * 100) / 100;\n\n this.timeline.push({\n time: endTime,\n event: 'animation-end',\n animationId: id,\n type: info.type,\n expectedDuration: info.expectedDuration,\n actualDuration: info.actualDuration,\n deviation: Math.round((info.actualDuration - info.expectedDuration) * 100) / 100,\n });\n\n this.completedAnimations.push(info);\n this.activeAnimations.delete(id);\n\n return info;\n }\n\n /**\n * Detect which animations are overlapping with the given animation\n */\n private detectOverlaps(newAnimationId: string): string[] {\n const overlaps: string[] = [];\n\n this.activeAnimations.forEach((info, id) => {\n if (id !== newAnimationId) {\n overlaps.push(`${info.type}:${info.elementId}`);\n }\n });\n\n return overlaps;\n }\n\n /**\n * Get all overlap pairs with duration\n */\n getOverlaps(): OverlapInfo[] {\n if (!__DEBUG__) return [];\n\n const overlaps: OverlapInfo[] = [];\n const allAnimations = [...this.completedAnimations];\n\n // Also include active animations with estimated end time\n this.activeAnimations.forEach(info => {\n allAnimations.push({\n ...info,\n endTime: info.startTime + info.expectedDuration,\n actualDuration: info.expectedDuration,\n });\n });\n\n // Find overlapping pairs\n for (let i = 0; i < allAnimations.length; i++) {\n for (let j = i + 1; j < allAnimations.length; j++) {\n const a = allAnimations[i];\n const b = allAnimations[j];\n\n // Skip if either is undefined (TypeScript safety)\n if (!a || !b) continue;\n\n const aEnd = a.endTime || (a.startTime + a.expectedDuration);\n const bEnd = b.endTime || (b.startTime + b.expectedDuration);\n\n // Check if they overlap\n if (a.startTime < bEnd && aEnd > b.startTime) {\n const overlapStart = Math.max(a.startTime, b.startTime);\n const overlapEnd = Math.min(aEnd, bEnd);\n const overlapDuration = overlapEnd - overlapStart;\n\n if (overlapDuration > 0) {\n overlaps.push({\n animation1: `${a.type}:${a.elementId}`,\n animation2: `${b.type}:${b.elementId}`,\n overlapStart: Math.round(overlapStart * 100) / 100,\n overlapDuration: Math.round(overlapDuration * 100) / 100,\n });\n }\n }\n }\n }\n\n return overlaps;\n }\n\n /**\n * Get current timeline\n */\n getTimeline(): TimelineEvent[] {\n if (!__DEBUG__) return [];\n return [...this.timeline];\n }\n\n /**\n * Get active animation count\n */\n getActiveCount(): number {\n if (!__DEBUG__) return 0;\n return this.activeAnimations.size;\n }\n\n /**\n * Get active animations\n */\n getActiveAnimations(): AnimationInfo[] {\n if (!__DEBUG__) return [];\n return Array.from(this.activeAnimations.values());\n }\n\n /**\n * Get completed animations\n */\n getCompletedAnimations(): AnimationInfo[] {\n if (!__DEBUG__) return [];\n return [...this.completedAnimations];\n }\n\n /**\n * Get full choreography summary\n */\n getSummary(): ChoreographySummary {\n if (!__DEBUG__) {\n return {\n totalAnimations: 0,\n totalDuration: 0,\n overlaps: [],\n timeline: [],\n activeAnimations: [],\n };\n }\n\n const allAnimations = [...this.completedAnimations, ...this.getActiveAnimations()];\n\n let totalDuration = 0;\n if (allAnimations.length > 0) {\n const minStart = Math.min(...allAnimations.map(a => a.startTime));\n const maxEnd = Math.max(\n ...allAnimations.map(a => a.endTime || (a.startTime + a.expectedDuration))\n );\n totalDuration = maxEnd - minStart;\n }\n\n return {\n totalAnimations: allAnimations.length,\n totalDuration: Math.round(totalDuration * 100) / 100,\n overlaps: this.getOverlaps(),\n timeline: this.getTimeline(),\n activeAnimations: this.getActiveAnimations(),\n };\n }\n\n /**\n * Get timeline for visualization (normalized to 0-100%)\n */\n getTimelineForVisualization(): Array<{\n id: string;\n type: string;\n elementId: string;\n startPercent: number;\n widthPercent: number;\n duration: number;\n isActive: boolean;\n }> {\n if (!__DEBUG__) return [];\n\n const summary = this.getSummary();\n if (summary.totalDuration === 0) return [];\n\n const allAnimations = [...this.completedAnimations, ...this.getActiveAnimations()];\n const minStart = Math.min(...allAnimations.map(a => a.startTime));\n\n return allAnimations.map(anim => {\n const duration = anim.actualDuration || anim.expectedDuration;\n const startPercent = ((anim.startTime - minStart) / summary.totalDuration) * 100;\n const widthPercent = (duration / summary.totalDuration) * 100;\n\n return {\n id: anim.id,\n type: anim.type,\n elementId: anim.elementId,\n startPercent: Math.round(startPercent * 100) / 100,\n widthPercent: Math.round(widthPercent * 100) / 100,\n duration: Math.round(duration * 100) / 100,\n isActive: this.activeAnimations.has(anim.id),\n };\n });\n }\n}\n\n// Export singleton instance\nexport const choreographyTracker = new ChoreographyTrackerClass();\n\n// Export class for testing\nexport { ChoreographyTrackerClass };\n","import React, { useRef, useState, useLayoutEffect, useEffect } from 'react';\nimport { ANIMATION_DEFAULTS } from '../utils/animationUtils';\nimport { useDebug } from '../contexts/DebugContext';\nimport { capturePosition, createAnimationTimer } from '../utils/debugCapture';\nimport { choreographyTracker } from '../utils/choreographyTracker';\n\nexport interface SlidingTextProps {\n text: string;\n mode?: 'word' | 'character' | 'none';\n direction?: 'vertical' | 'horizontal';\n staggerDelay?: number;\n duration?: number;\n easing?: string;\n blur?: boolean;\n widthAnimation?: boolean;\n initial?: 'initial' | false;\n animate?: 'animate';\n exit?: 'exit';\n onAnimationComplete?: () => void;\n className?: string;\n style?: React.CSSProperties;\n}\n\nexport const SlidingText: React.FC<SlidingTextProps> = ({\n text,\n mode = 'word',\n direction = 'vertical',\n staggerDelay = ANIMATION_DEFAULTS.STAGGER_DELAY,\n duration = ANIMATION_DEFAULTS.DURATION_ENTER,\n easing = ANIMATION_DEFAULTS.EASING_ENTER,\n blur = true,\n widthAnimation = false,\n initial = 'initial',\n exit,\n className = '',\n style,\n}) => {\n const containerRef = useRef<HTMLDivElement>(null);\n const contentRef = useRef<HTMLDivElement>(null);\n const { logEvent } = useDebug();\n const enterTimerRef = useRef<ReturnType<typeof createAnimationTimer> | null>(null);\n const choreographyIdRef = useRef<string | null>(null);\n\n \n // Track if we've triggered the enter animation\n const hasTriggeredEnterRef = useRef(false);\n const [showAnimate, setShowAnimate] = useState(initial !== 'initial');\n\n // Trigger enter animation after mount\n useEffect(() => {\n if (initial === 'initial' && !hasTriggeredEnterRef.current) {\n hasTriggeredEnterRef.current = true;\n requestAnimationFrame(() => {\n setShowAnimate(true);\n });\n }\n }, [initial]);\n\n // Compute the actual visual state\n const visualState = exit === 'exit' ? 'exit' : (showAnimate ? 'animate' : 'initial');\n\n // Split text based on mode (moved up for logging)\n const elements = mode === 'character' ? text.split('') : [text];\n const totalEnterDuration = duration + (elements.length - 1) * staggerDelay;\n\n // Log enter animation start\n useEffect(() => {\n if (visualState === 'animate' && containerRef.current) {\n const container = containerRef.current;\n\n // Start timer for enter animation\n enterTimerRef.current = createAnimationTimer(totalEnterDuration);\n choreographyIdRef.current = `enter-${text.slice(0, 10)}-${Date.now()}`;\n\n // Start choreography tracking\n choreographyTracker.startAnimation(\n choreographyIdRef.current,\n 'enter',\n text,\n totalEnterDuration\n );\n\n // Capture initial state\n const initialPosition = capturePosition(container);\n\n logEvent({\n type: 'text-enter-start',\n source: 'SlidingText',\n message: `Enter animation starting for: \"${text}\"`,\n timing: {\n startTime: enterTimerRef.current.start,\n expectedDuration: totalEnterDuration\n },\n position: {\n element: text,\n x: initialPosition.x,\n y: initialPosition.y,\n width: initialPosition.width,\n height: initialPosition.height\n },\n animation: {\n name: 'enter',\n phase: 'start',\n easing: easing,\n fill: 'forwards'\n },\n data: {\n text,\n charCount: elements.length,\n mode,\n direction,\n staggerDelay,\n duration,\n totalDuration: totalEnterDuration,\n blur,\n widthAnimation\n }\n });\n\n // Schedule enter complete logging\n const completeTimer = setTimeout(() => {\n if (enterTimerRef.current && choreographyIdRef.current && containerRef.current) {\n const timingResult = enterTimerRef.current.end();\n choreographyTracker.endAnimation(choreographyIdRef.current);\n\n const finalPosition = capturePosition(containerRef.current);\n\n logEvent({\n type: 'text-enter-complete',\n source: 'SlidingText',\n message: `Enter animation complete for: \"${text}\"`,\n timing: {\n startTime: timingResult.startTime,\n endTime: timingResult.endTime,\n expectedDuration: timingResult.expectedDuration,\n actualDuration: timingResult.actualDuration,\n deviation: timingResult.deviation,\n deviationPercent: timingResult.deviationPercent\n },\n position: {\n element: text,\n x: finalPosition.x,\n y: finalPosition.y,\n width: finalPosition.width,\n height: finalPosition.height\n },\n animation: {\n name: 'enter',\n phase: 'complete',\n progress: 1,\n easing: easing,\n fill: 'forwards'\n },\n data: {\n text,\n deviationStatus: Math.abs(timingResult.deviation) < 10 ? 'OK' : 'WARNING'\n }\n });\n }\n }, totalEnterDuration + 50); // +50ms buffer\n\n return () => clearTimeout(completeTimer);\n }\n }, [visualState, text, mode, direction, staggerDelay, duration, easing, blur, widthAnimation, totalEnterDuration, elements.length, logEvent]);\n\n // Width Animation Logic\n useLayoutEffect(() => {\n if (!widthAnimation || !containerRef.current || !contentRef.current) return;\n\n const container = containerRef.current;\n const content = contentRef.current;\n\n if (visualState === 'initial') {\n container.style.width = '0px';\n } else if (visualState === 'animate') {\n const supportsInterpolateSize = CSS.supports('interpolate-size', 'allow-keywords');\n\n if (supportsInterpolateSize) {\n container.style.width = 'auto';\n container.style.transition = `width ${duration}ms ${easing}`;\n } else {\n const targetWidth = content.scrollWidth;\n container.style.width = `${targetWidth}px`;\n container.style.transition = `width ${duration}ms ${easing}`;\n\n const timer = setTimeout(() => {\n container.style.width = 'auto';\n }, duration);\n return () => clearTimeout(timer);\n }\n } else if (visualState === 'exit') {\n const currentWidth = container.getBoundingClientRect().width;\n container.style.width = `${currentWidth}px`;\n container.getBoundingClientRect(); // Force reflow\n container.style.width = '0px';\n container.style.transition = `width ${ANIMATION_DEFAULTS.DURATION_EXIT}ms ${ANIMATION_DEFAULTS.EASING_EXIT}`;\n }\n }, [visualState, widthAnimation, duration, easing, text]);\n\n const getTransitionStyle = (index: number): React.CSSProperties => {\n const delay = index * staggerDelay;\n const isExit = visualState === 'exit';\n const currentDuration = isExit ? ANIMATION_DEFAULTS.DURATION_EXIT : duration;\n const currentEasing = isExit ? ANIMATION_DEFAULTS.EASING_EXIT : easing;\n\n return {\n transition: `\n opacity ${currentDuration}ms ${currentEasing} ${delay}ms,\n transform ${currentDuration}ms ${currentEasing} ${delay}ms,\n filter ${currentDuration}ms ${currentEasing} ${delay}ms\n `,\n '--blur-amount': blur ? `${ANIMATION_DEFAULTS.BLUR_AMOUNT}px` : '0px',\n '--offset': direction === 'vertical' ? `${ANIMATION_DEFAULTS.OFFSET_VERTICAL}px` : `${ANIMATION_DEFAULTS.OFFSET_HORIZONTAL}px`\n } as React.CSSProperties;\n };\n\n return (\n <div\n ref={containerRef}\n className={`waapi-sliding-text-container ${className}`}\n style={style}\n >\n <div ref={contentRef} className={`waapi-sliding-text-content waapi-direction-${direction}`}>\n {elements.map((char, index) => (\n <span\n key={index}\n className={`waapi-sliding-text-token ${\n visualState === 'initial' ? 'enter-from' :\n visualState === 'animate' ? 'enter-to' :\n 'exit-active'\n }`}\n style={getTransitionStyle(index)}\n >\n {char}\n </span>\n ))}\n </div>\n </div>\n );\n};\n","import { useRef, useCallback, useEffect } from 'react';\nimport { TIMING, TRANSFORMS, EFFECTS, EASINGS } from '../utils/animationUtils';\nimport { useDebug } from '../contexts/DebugContext';\nimport {\n captureComputedStyles,\n capturePosition,\n createAnimationTimer,\n compareStyles\n} from '../utils/debugCapture';\nimport { choreographyTracker } from '../utils/choreographyTracker';\n\ninterface SeparatorState {\n tokenId: string;\n isVisible: boolean;\n isAnimating: boolean;\n animationPhase: 'idle' | 'exit-coordinated' | 'flip-coordinated' | 'completed';\n startTime?: number;\n expectedEndTime?: number;\n}\n\ninterface AnimationConfig {\n onComplete: (id: string) => void;\n exitDuration?: number;\n flipDuration?: number;\n exitEasing?: string;\n flipEasing?: string;\n}\n\ninterface WapiState {\n animatingIds: Set<string>;\n positions: Map<string, DOMRect>;\n separatorStates: Map<string, SeparatorState>;\n}\n\nexport const useWAAPIAnimations = (config: AnimationConfig) => {\n const { logEvent } = useDebug();\n\n // Ref pattern para logEvent - evita re-ejecutar callbacks cuando logEvent cambia\n const logEventRef = useRef(logEvent);\n useEffect(() => {\n logEventRef.current = logEvent;\n }, [logEvent]);\n\n // Ref pattern para config - evita re-crear callbacks\n const configRef = useRef(config);\n useEffect(() => {\n configRef.current = config;\n }, [config]);\n\n const elementsRef = useRef<Map<string, HTMLElement>>(new Map());\n const separatorsRef = useRef<Map<string, HTMLElement | null>>(new Map());\n const stateRef = useRef<WapiState>({\n animatingIds: new Set(),\n positions: new Map(),\n separatorStates: new Map()\n });\n const activeAnimationsRef = useRef<Map<string, Animation[]>>(new Map());\n\n const registerElement = useCallback((id: string, el: HTMLElement | null) => {\n if (el) {\n elementsRef.current.set(id, el);\n } else {\n elementsRef.current.delete(id);\n }\n }, []);\n\n const capturePositions = useCallback((excludeIds: Set<string>) => {\n const positions = new Map<string, DOMRect>();\n elementsRef.current.forEach((el, id) => {\n if (!excludeIds.has(id)) {\n const rect = el.getBoundingClientRect();\n if (rect.width > 0 && rect.height > 0) {\n positions.set(id, rect);\n }\n }\n });\n stateRef.current.positions = positions;\n return positions;\n }, []);\n\n const cancelAnimations = useCallback((id: string) => {\n const animations = activeAnimationsRef.current.get(id);\n if (animations) {\n animations.forEach(anim => anim.cancel());\n activeAnimationsRef.current.delete(id);\n }\n }, []);\n\n const registerSeparator = useCallback((tokenId: string, el: HTMLElement | null) => {\n // Initialize separator state if it doesn't exist\n if (!stateRef.current.separatorStates.has(tokenId)) {\n stateRef.current.separatorStates.set(tokenId, {\n tokenId,\n isVisible: true,\n isAnimating: false,\n animationPhase: 'idle'\n });\n }\n\n if (el) {\n separatorsRef.current.set(tokenId, el);\n } else {\n separatorsRef.current.delete(tokenId);\n // Don't delete state on null - it might be pre-registration\n }\n }, []);\n\n const getSeparatorState = useCallback((tokenId: string): SeparatorState | undefined => {\n return stateRef.current.separatorStates.get(tokenId);\n }, []);\n\n const startSeparatorAnimation = useCallback(async (tokenId: string): Promise<void> => {\n const separatorEl = separatorsRef.current.get(tokenId);\n const existingSeparatorState = stateRef.current.separatorStates.get(tokenId);\n\n if (!separatorEl) {\n // Check if we have a separator state (pre-registered) but no DOM element\n if (existingSeparatorState) {\n logEventRef.current({\n type: 'separator-animation-state-only',\n source: 'useWAAPIAnimations.startSeparatorAnimation',\n message: `Separator state exists but no DOM element for token: ${tokenId} - marking as completed`,\n data: { tokenId, reason: 'state-no-element' }\n });\n\n // Mark separator as completed immediately since there's no DOM element to animate\n existingSeparatorState.animationPhase = 'completed';\n existingSeparatorState.isVisible = false;\n return;\n }\n\n logEventRef.current({\n type: 'separator-animation-skipped',\n source: 'useWAAPIAnimations.startSeparatorAnimation',\n message: `No separator element found for token: ${tokenId} - skipping animation`,\n data: { tokenId, reason: 'no-separator-element' }\n });\n return;\n }\n\n const exitDuration = configRef.current.exitDuration || TIMING.EXIT_DURATION;\n const exitEasing = configRef.current.exitEasing || EASINGS.EASE_IN_CUBIC;\n\n const newSeparatorState: SeparatorState = {\n tokenId,\n isVisible: true,\n isAnimating: true,\n animationPhase: 'exit-coordinated',\n startTime: performance.now(),\n expectedEndTime: performance.now() + exitDuration\n };\n\n stateRef.current.separatorStates.set(tokenId, newSeparatorState);\n\n const choreographyId = `separator-${tokenId}-${Date.now()}`;\n choreographyTracker.startAnimation(choreographyId, 'separator-exit', tokenId, exitDuration);\n\n logEventRef.current({\n type: 'separator-animation-start',\n source: 'useWAAPIAnimations.startSeparatorAnimation',\n message: `Separator animation starting for token: ${tokenId}`,\n timing: {\n startTime: newSeparatorState.startTime || Date.now(),\n expectedDuration: exitDuration\n },\n data: { tokenId, duration: exitDuration }\n });\n\n separatorEl.classList.add('exit-coordinated');\n\n const separatorAnim = separatorEl.animate([\n { opacity: 1, transform: 'translateY(0) scale(1)' },\n { opacity: 0, transform: `translateY(${TRANSFORMS.OFFSET_Y_EXIT}px) scale(${TRANSFORMS.SCALE_EXIT})` }\n ], {\n duration: exitDuration,\n easing: exitEasing,\n fill: 'forwards'\n });\n\n try {\n await separatorAnim.finished;\n } catch {\n return;\n }\n\n newSeparatorState.animationPhase = 'completed';\n newSeparatorState.isVisible = false;\n separatorEl.classList.add('exit-completed');\n\n choreographyTracker.endAnimation(choreographyId);\n\n logEventRef.current({\n type: 'separator-animation-complete',\n source: 'useWAAPIAnimations.startSeparatorAnimation',\n message: `Separator animation complete for token: ${tokenId}`,\n data: { tokenId, duration: exitDuration }\n });\n }, []);\n\n // CLEANUP: Remove separator states when tokens change\n const cleanupSeparatorStates = useCallback((visibleTokenIds: Set<string>) => {\n const currentSeparatorStates = Array.from(stateRef.current.separatorStates.keys());\n\n currentSeparatorStates.forEach(tokenId => {\n // Remove separator state if token doesn't exist or doesn't need separator\n if (!visibleTokenIds.has(tokenId)) {\n stateRef.current.separatorStates.delete(tokenId);\n logEventRef.current({\n type: 'separator-state-cleanup',\n source: 'useWAAPIAnimations.cleanupSeparatorStates',\n message: `Removed separator state for non-existent token: ${tokenId}`,\n data: { tokenId }\n });\n }\n });\n }, []);\n\n const startExit = useCallback(async (id: string, additionalSeparatorIds?: string[]) => {\n const el = elementsRef.current.get(id);\n if (!el || stateRef.current.animatingIds.has(id)) return;\n\n stateRef.current.animatingIds.add(id);\n\n // 1. Capture positions of REMAINING tokens BEFORE any DOM changes\n const excludeIds = new Set([id]);\n capturePositions(excludeIds);\n\n // 1.5. Start separator animation for this token and any additional ones (avoiding duplicates)\n const allSeparatorIds = new Set([id, ...(additionalSeparatorIds || [])]);\n allSeparatorIds.forEach(sepId => {\n startSeparatorAnimation(sepId);\n });\n\n // Use research-based timing values\n const exitDuration = configRef.current.exitDuration || TIMING.EXIT_DURATION;\n const exitEasing = configRef.current.exitEasing || EASINGS.EASE_IN_CUBIC;\n const flipDuration = configRef.current.flipDuration || TIMING.FLIP_DURATION;\n\n // Use research-based transform/effect values (declared early for logging)\n const exitOffsetY = TRANSFORMS.OFFSET_Y_EXIT;\n const exitScale = TRANSFORMS.SCALE_EXIT;\n const exitBlur = EFFECTS.BLUR_EXIT;\n const exitStagger = TIMING.EXIT_STAGGER;\n\n // Get wrapper dimensions for width collapse\n const wrapperRect = el.getBoundingClientRect();\n const computedStyle = getComputedStyle(el);\n const marginRight = parseFloat(computedStyle.marginRight) || 0;\n\n // Create separate timers for exit fade and total orchestration\n const exitFadeTimer = createAnimationTimer(exitDuration);\n const orchestrationTimer = createAnimationTimer(exitDuration + flipDuration);\n\n // Capture initial styles and position for quantitative analysis\n const initialStyles = captureComputedStyles(el);\n const initialPosition = capturePosition(el);\n\n // Start choreography tracking\n const choreographyId = `exit-${id}-${Date.now()}`;\n choreographyTracker.startAnimation(choreographyId, 'exit-fade', id, exitDuration);\n\n // Also track width collapse as separate animation\n const widthCollapseId = `width-${id}-${Date.now()}`;\n choreographyTracker.startAnimation(widthCollapseId, 'width-collapse', id, exitDuration);\n\n logEventRef.current({\n type: 'animation-start-detailed',\n source: 'useWAAPIAnimations.startExit',\n message: `Exit animation starting for: ${id}`,\n timing: {\n startTime: exitFadeTimer.start,\n expectedDuration: exitDuration\n },\n styles: Object.entries(initialStyles).map(([prop, val]) => ({\n property: prop,\n expected: val,\n actual: val,\n matches: true\n })),\n position: {\n element: id,\n x: initialPosition.x,\n y: initialPosition.y,\n width: initialPosition.width,\n height: initialPosition.height\n },\n animation: {\n name: 'exit',\n phase: 'start',\n easing: exitEasing,\n fill: 'forwards'\n },\n data: {\n id,\n capturedPositions: stateRef.current.positions.size,\n wrapperWidth: wrapperRect.width,\n marginRight,\n expectedFinalStyles: {\n opacity: '0',\n transform: `translateY(${exitOffsetY}px) scale(${exitScale})`,\n filter: `blur(${exitBlur}px)`,\n width: '0px',\n marginRight: '0px'\n }\n }\n });\n\n const exitAnimations: Animation[] = [];\n const flipAnimations: Animation[] = [];\n let flipStarted = false;\n\n // 2. Run exit animations SIMULTANEOUSLY:\n // - Character fade out (staggered)\n // - Width collapse\n // - Margin collapse\n\n const tokens = el.querySelectorAll('.sliding-text-token');\n\n if (tokens.length > 0) {\n // Animate each character - no stagger on exit for cleaner feel\n tokens.forEach((token, index) => {\n const delay = index * exitStagger;\n const anim = (token as HTMLElement).animate([\n { opacity: 1, transform: 'translateY(0) scale(1)', filter: 'blur(0px)' },\n { opacity: 0, transform: `translateY(${exitOffsetY}px) scale(${exitScale})`, filter: `blur(${exitBlur}px)` }\n ], {\n duration: exitDuration,\n easing: exitEasing,\n delay,\n fill: 'forwards'\n });\n exitAnimations.push(anim);\n });\n } else {\n // Fallback: animate the whole element with scale\n const anim = el.animate([\n { opacity: 1, transform: 'translateY(0) scale(1)' },\n { opacity: 0, transform: `translateY(${exitOffsetY}px) scale(${exitScale})` }\n ], {\n duration: exitDuration,\n easing: exitEasing,\n fill: 'forwards'\n });\n exitAnimations.push(anim);\n }\n\n // Width + margin collapse animation (simultaneous with fade)\n el.style.overflow = 'hidden';\n const widthAnim = el.animate([\n { width: `${wrapperRect.width}px`, marginRight: `${marginRight}px` },\n { width: '0px', marginRight: '0px' }\n ], {\n duration: exitDuration,\n easing: exitEasing,\n fill: 'forwards'\n });\n exitAnimations.push(widthAnim);\n\n activeAnimationsRef.current.set(id, exitAnimations);\n\n // 3. Start FLIP animations at 25% of exit (research-based overlap)\n const flipDelay = exitDuration * TIMING.FLIP_DELAY_PERCENT;\n\n const startFlipAnimations = () => {\n if (flipStarted) return;\n flipStarted = true;\n\n // Remove from layout flow\n el.style.position = 'absolute';\n el.style.opacity = '0';\n el.style.pointerEvents = 'none';\n\n // Check if there are any remaining elements to FLIP\n const remainingElements = Array.from(elementsRef.current.entries()).filter(([elemId]) => elemId !== id);\n\n if (remainingElements.length === 0) {\n // No elements to FLIP - skip FLIP animations\n logEventRef.current({\n type: 'flip-skipped',\n source: 'useWAAPIAnimations.startExit',\n message: `No remaining elements to FLIP - skipping animation`,\n data: { id }\n });\n return;\n }\n\n elementsRef.current.forEach((remainingEl, remainingId) => {\n if (remainingId === id) return;\n\n const prevRect = stateRef.current.positions.get(remainingId);\n if (!prevRect) return;\n\n const newRect = remainingEl.getBoundingClientRect();\n const deltaX = prevRect.left - newRect.left;\n const deltaY = prevRect.top - newRect.top;\n\n // Ignore sub-pixel movements\n if (Math.abs(deltaX) < 1 && Math.abs(deltaY) < 1) return;\n\n // Track FLIP in choreography\n const flipChoreographyId = `flip-${remainingId}-${Date.now()}`;\n choreographyTracker.startAnimation(flipChoreographyId, 'flip', remainingId, flipDuration);\n\n logEventRef.current({\n type: 'flip-animation',\n source: 'useWAAPIAnimations.startExit',\n message: `FLIP animation for: ${remainingId}`,\n timing: {\n startTime: performance.now(),\n expectedDuration: flipDuration\n },\n position: {\n element: remainingId,\n x: newRect.left,\n y: newRect.top,\n width: newRect.width,\n height: newRect.height,\n delta: { x: deltaX, y: deltaY }\n },\n animation: {\n name: 'flip',\n phase: 'start',\n easing: EASINGS.SPRING_GENTLE,\n fill: 'none'\n },\n data: {\n id: remainingId,\n deltaX: Math.round(deltaX * 100) / 100,\n deltaY: Math.round(deltaY * 100) / 100,\n prevPosition: {\n x: prevRect.left,\n y: prevRect.top,\n width: prevRect.width,\n height: prevRect.height\n },\n newPosition: {\n x: newRect.left,\n y: newRect.top,\n width: newRect.width,\n height: newRect.height\n }\n }\n });\n\n // Use spring easing for natural FLIP feel\n const flipAnim = remainingEl.animate([\n { transform: `translate3d(${deltaX}px, ${deltaY}px, 0)` },\n { transform: 'translate3d(0, 0, 0)' }\n ], {\n duration: flipDuration,\n easing: EASINGS.SPRING_GENTLE // Spring for natural overshoot\n });\n\n // Track FLIP completion\n flipAnim.onfinish = () => {\n choreographyTracker.endAnimation(flipChoreographyId);\n logEventRef.current({\n type: 'flip-animation-complete',\n source: 'useWAAPIAnimations.startExit',\n message: `FLIP complete for: ${remainingId}`,\n animation: {\n name: 'flip',\n phase: 'complete',\n progress: 1\n },\n data: { id: remainingId }\n });\n };\n\n flipAnimations.push(flipAnim);\n });\n };\n\n // Schedule FLIP to start during exit animation (overlap)\n const flipTimer = setTimeout(startFlipAnimations, flipDelay);\n\n // 4. Separate timing: exit-fade completes first, width-collapse coordinates with FLIP\n try {\n // Separate fade animations from width animation\n const fadeAnimations = exitAnimations.filter(anim => anim !== widthAnim);\n const widthAnimationPromise = widthAnim.finished;\n\n // Wait for fade animations to complete (exactly 180ms)\n await Promise.all(fadeAnimations.map(a => a.finished));\n\n // Log exit-fade completion (exactly 180ms)\n const exitFadeTiming = exitFadeTimer.end();\n logEventRef.current({\n type: 'exit-fade-complete',\n source: 'useWAAPIAnimations.startExit',\n message: `Exit fade complete for: ${id}`,\n timing: {\n startTime: exitFadeTiming.startTime,\n endTime: exitFadeTiming.endTime,\n expectedDuration: exitFadeTiming.expectedDuration,\n actualDuration: exitFadeTiming.actualDuration,\n deviation: exitFadeTiming.deviation,\n deviationPercent: exitFadeTiming.deviationPercent\n },\n animation: {\n name: 'exit-fade',\n phase: 'complete',\n progress: 1,\n easing: exitEasing\n },\n data: { id }\n });\n\n // Ensure FLIP started if exit finished early\n startFlipAnimations();\n\n // Wait for both FLIP and width-collapse to complete (can extend beyond 180ms)\n await Promise.all([\n ...flipAnimations.map(a => a.finished.catch(() => {})),\n widthAnimationPromise\n ]);\n } catch {\n // Animation was cancelled\n clearTimeout(flipTimer);\n stateRef.current.animatingIds.delete(id);\n return;\n }\n\n // End choreography tracking\n choreographyTracker.endAnimation(choreographyId);\n choreographyTracker.endAnimation(widthCollapseId);\n\n // Capture orchestration timing result (total time including FLIP)\n const orchestrationTiming = orchestrationTimer.end();\n\n // Capture final position for verification\n const finalPosition = capturePosition(el);\n\n // Compare expected vs actual final styles\n const expectedFinalStyles = {\n opacity: '0',\n width: '0px',\n marginRight: '0px'\n };\n const styleComparison = compareStyles(expectedFinalStyles, el);\n\n logEventRef.current({\n type: 'orchestration-complete',\n source: 'useWAAPIAnimations.startExit',\n message: `Orchestration complete for: ${id}`,\n timing: {\n startTime: orchestrationTiming.startTime,\n endTime: orchestrationTiming.endTime,\n expectedDuration: orchestrationTiming.expectedDuration,\n actualDuration: orchestrationTiming.actualDuration,\n deviation: orchestrationTiming.deviation,\n deviationPercent: orchestrationTiming.deviationPercent\n },\n styles: styleComparison,\n position: {\n element: id,\n x: finalPosition.x,\n y: finalPosition.y,\n width: finalPosition.width,\n height: finalPosition.height\n },\n animation: {\n name: 'orchestration',\n phase: 'complete',\n progress: 1,\n easing: exitEasing,\n fill: 'forwards'\n },\n data: {\n id,\n choreographySummary: choreographyTracker.getSummary(),\n deviationStatus: Math.abs(orchestrationTiming.deviation) < 100 ? 'OK' : 'WARNING',\n styleMatchCount: styleComparison.filter(s => s.matches).length,\n styleMismatchCount: styleComparison.filter(s => !s.matches).length\n }\n });\n\n // Log timing analysis event for orchestration\n logEventRef.current({\n type: 'animation-timing',\n source: 'useWAAPIAnimations.startExit',\n message: `Orchestration timing for: ${id}`,\n timing: {\n startTime: orchestrationTiming.startTime,\n endTime: orchestrationTiming.endTime,\n expectedDuration: orchestrationTiming.expectedDuration,\n actualDuration: orchestrationTiming.actualDuration,\n deviation: orchestrationTiming.deviation,\n deviationPercent: orchestrationTiming.deviationPercent\n },\n animation: {\n name: 'orchestration',\n phase: 'complete'\n },\n data: {\n id,\n status: Math.abs(orchestrationTiming.deviation) < 100 ? '✅ ON TIME' : '⚠️ SLOW'\n }\n });\n\n // 5. Cleanup and notify completion\n stateRef.current.animatingIds.delete(id);\n activeAnimationsRef.current.delete(id);\n elementsRef.current.delete(id);\n\n configRef.current.onComplete(id);\n }, [capturePositions]); // Removed config - using configRef\n\n const isAnimating = useCallback((id?: string) => {\n if (id) return stateRef.current.animatingIds.has(id);\n return stateRef.current.animatingIds.size > 0;\n }, []);\n\n // Cleanup on unmount\n useEffect(() => {\n return () => {\n activeAnimationsRef.current.forEach((animations) => {\n animations.forEach(anim => anim.cancel());\n });\n activeAnimationsRef.current.clear();\n };\n }, []);\n\n return {\n registerElement,\n startExit,\n isAnimating,\n cancelAnimations,\n // Separator management functions\n registerSeparator,\n getSeparatorState,\n startSeparatorAnimation,\n cleanupSeparatorStates\n };\n};\n","import React, { useEffect, useState, useRef, useCallback } from 'react';\nimport { SlidingText } from './SlidingText';\nimport { SlidingNumber } from './SlidingNumber';\nimport { useWAAPIAnimations } from '../hooks/useWAAPIAnimations';\nimport { PRESETS, ANIMATION_DEFAULTS } from '../utils/animationUtils';\nimport { useDebug } from '../contexts/DebugContext';\n\nexport interface Token {\n id: string;\n text: string;\n}\n\nexport interface AnimatedTokensProps {\n tokens: Token[];\n placeholder?: string;\n maxVisible?: number;\n textAnimationMode?: 'character' | 'word';\n textDirection?: 'vertical' | 'horizontal';\n textStaggerDelay?: number;\n separator?: string;\n enableWidthAnimation?: boolean;\n className?: string;\n tokenClassName?: string;\n placeholderClassName?: string;\n separatorClassName?: string;\n}\n\nexport const AnimatedTokens: React.FC<AnimatedTokensProps> = ({\n tokens,\n placeholder = 'No tokens',\n maxVisible,\n textAnimationMode = 'character',\n textDirection = 'vertical',\n textStaggerDelay = 15,\n separator = ', ',\n enableWidthAnimation = false,\n className = '',\n tokenClassName = '',\n placeholderClassName = '',\n separatorClassName = '',\n}) => {\n const { logEvent } = useDebug();\n const isMountedRef = useRef(true);\n\n // Ref pattern to avoid dependency issues\n const logEventRef = useRef(logEvent);\n useEffect(() => {\n logEventRef.current = logEvent;\n }, [logEvent]);\n\n useEffect(() => {\n isMountedRef.current = true;\n logEventRef.current({\n type: 'render',\n source: 'AnimatedTokens.mount',\n message: 'AnimatedTokens mounted',\n data: { tokenCount: tokens.length }\n });\n return () => {\n logEventRef.current({\n type: 'component-unmounted',\n source: 'AnimatedTokens.unmount',\n message: 'AnimatedTokens UNMOUNTING',\n data: { timestamp: Date.now() }\n });\n isMountedRef.current = false;\n };\n }, []); // Empty deps - only run on mount/unmount\n\n // Single state: displayTokens (includes tokens being animated out)\n const [displayTokens, setDisplayTokens] = useState<Token[]>(tokens);\n\n // Track which tokens are currently animating (via ref, not state)\n const animatingIdsRef = useRef<Set<string>>(new Set());\n\n // Stable callback for onComplete\n const handleAnimationComplete = useCallback((id: string) => {\n if (!isMountedRef.current) return;\n\n logEventRef.current({\n type: 'token-dom-remove',\n source: 'AnimatedTokens.onComplete',\n message: `Token removed from DOM: ${id}`,\n data: { id }\n });\n\n animatingIdsRef.current.delete(id);\n setDisplayTokens(prev => prev.filter(t => t.id !== id));\n }, []);\n\n // WAAPI-centric hook - all animation logic handled internally\n const {\n registerElement,\n startExit,\n isAnimating,\n // Separator management functions\n registerSeparator,\n getSeparatorState,\n cleanupSeparatorStates\n } = useWAAPIAnimations({\n onComplete: handleAnimationComplete\n });\n\n // Store hook functions in refs to avoid dependency issues\n const startExitRef = useRef(startExit);\n const getSeparatorStateRef = useRef(getSeparatorState);\n const cleanupSeparatorStatesRef = useRef(cleanupSeparatorStates);\n useEffect(() => {\n startExitRef.current = startExit;\n getSeparatorStateRef.current = getSeparatorState;\n cleanupSeparatorStatesRef.current = cleanupSeparatorStates;\n }, [startExit, getSeparatorState, cleanupSeparatorStates]);\n\n // Calculate visible tokens and overflow\n const activeTokens = displayTokens.filter(t => !animatingIdsRef.current.has(t.id) || !isAnimating(t.id));\n const visibleTokens = maxVisible ? displayTokens.slice(0, maxVisible) : displayTokens;\n const overflowCount = maxVisible ? Math.max(0, activeTokens.length - maxVisible) : 0;\n\n // Enhanced separator logic function\n const shouldShowSeparator = (tokenId: string, tokenIndex: number): boolean => {\n const isLastToken = tokenIndex >= visibleTokens.length - 1;\n if (isLastToken) return false;\n\n const separatorState = getSeparatorStateRef.current?.(tokenId);\n\n \n // Show separator if:\n // 1. No separator state (default to visible)\n // 2. Separator is idle or animating but not completed\n if (!separatorState) return true;\n\n switch (separatorState.animationPhase) {\n case 'idle':\n case 'exit-coordinated':\n case 'flip-coordinated':\n return true; // Still visible during coordinated animation\n case 'completed':\n return false; // Animation finished, hide separator\n default:\n return true;\n }\n };\n\n // Detect removals and additions - ONE useEffect, no RAF nesting\n useEffect(() => {\n const currentIds = new Set(tokens.map(t => t.id));\n const displayIds = new Set(displayTokens.map(t => t.id));\n\n // Clean up separator states for tokens that no longer exist\n const visibleTokenIds = new Set(visibleTokens.map(t => t.id));\n cleanupSeparatorStatesRef.current?.(visibleTokenIds);\n\n // Detect removals\n const removed = displayTokens.filter(t =>\n !currentIds.has(t.id) && !animatingIdsRef.current.has(t.id)\n );\n\n // Detect additions\n const added = tokens.filter(t => !displayIds.has(t.id));\n\n // Skip if no changes\n if (removed.length === 0 && added.length === 0) return;\n\n logEventRef.current({\n type: 'render',\n source: 'AnimatedTokens.useEffect',\n message: 'Token sync',\n data: {\n currentTokens: tokens.map(t => t.id),\n displayTokens: displayTokens.map(t => t.id),\n removed: removed.map(t => t.id),\n added: added.map(t => t.id),\n animating: Array.from(animatingIdsRef.current)\n }\n });\n\n // Handle removals - check if in overflow (no animation needed)\n removed.forEach(token => {\n const indexInDisplay = displayTokens.findIndex(dt => dt.id === token.id);\n const isInOverflow = maxVisible !== undefined && indexInDisplay >= maxVisible;\n\n if (isInOverflow) {\n logEventRef.current({\n type: 'overflow-token-remove',\n source: 'AnimatedTokens.useEffect',\n message: 'Removing overflow token without animation',\n data: { id: token.id }\n });\n setDisplayTokens(prev => prev.filter(t => t.id !== token.id));\n } else {\n // Find additional separators that need animation\n const additionalSeparators: string[] = [];\n const visibleTokens = displayTokens.slice(0, maxVisible);\n const tokenIndexInVisible = visibleTokens.findIndex(t => t.id === token.id);\n\n // Only animate separator of this token if it's NOT the last visible token\n // (last tokens don't have separators)\n let shouldAnimateOwnSeparator = tokenIndexInVisible < visibleTokens.length - 1;\n\n // If removing last visible token, the previous token loses its separator\n if (tokenIndexInVisible === visibleTokens.length - 1 && tokenIndexInVisible > 0) {\n const previousToken = visibleTokens[tokenIndexInVisible - 1];\n if (previousToken) {\n additionalSeparators.push(previousToken.id);\n }\n }\n\n \n logEventRef.current({\n type: 'token-remove',\n source: 'AnimatedTokens.useEffect',\n message: `Token marked for exit: ${token.text}`,\n data: {\n id: token.id,\n text: token.text,\n additionalSeparators,\n shouldAnimateOwnSeparator,\n wasLastVisible: tokenIndexInVisible === visibleTokens.length - 1\n }\n });\n animatingIdsRef.current.add(token.id);\n\n // Pass separators to animate: additional ones + potentially own separator\n const separatorsToAnimate = shouldAnimateOwnSeparator\n ? [token.id, ...additionalSeparators]\n : additionalSeparators;\n\n startExitRef.current(token.id, separatorsToAnimate);\n }\n });\n\n // Handle additions\n if (added.length > 0) {\n added.forEach(t => {\n logEventRef.current({\n type: 'token-add',\n source: 'AnimatedTokens.useEffect',\n message: `New token detected: ${t.text}`,\n data: { id: t.id, text: t.text }\n });\n });\n\n // Pre-register separators for newly added tokens to ensure they have state when rendered\n added.forEach(t => {\n const tokenIndex = tokens.findIndex(token => token.id === t.id);\n const needsSeparator = maxVisible\n ? tokenIndex < Math.min(maxVisible, tokens.length) - 1\n : tokenIndex < tokens.length - 1;\n\n if (needsSeparator) {\n // Pre-register separator state to ensure it's available for shouldShowSeparator\n registerSeparator(t.id, null);\n }\n });\n\n // Merge added tokens while preserving order from tokens prop\n const exitingTokens = displayTokens.filter(t => !currentIds.has(t.id));\n let result = [...tokens];\n\n // Insert exiting tokens at their original positions\n exitingTokens.forEach(et => {\n const oldIdx = displayTokens.findIndex(t => t.id === et.id);\n if (oldIdx !== -1 && oldIdx <= result.length) {\n result.splice(oldIdx, 0, et);\n }\n });\n\n if (JSON.stringify(result.map(t => t.id)) !== JSON.stringify(displayTokens.map(t => t.id))) {\n setDisplayTokens(result);\n }\n }\n }, [tokens, maxVisible]); // Removed startExit and logEvent - using refs\n\n // Overflow state tracking for enter/exit animations\n const prevOverflowCount = useRef(0);\n const isOverflowEntering = overflowCount > 0 && prevOverflowCount.current === 0;\n const isOverflowExiting = overflowCount === 0 && prevOverflowCount.current > 0;\n\n useEffect(() => {\n prevOverflowCount.current = overflowCount;\n }, [overflowCount]);\n\n const showPlaceholder = displayTokens.length === 0 && !isAnimating() && !!placeholder;\n\n return (\n <div className={`waapi-animated-tokens-container ${className}`}>\n {showPlaceholder && (\n <SlidingText\n key=\"placeholder\"\n text={placeholder}\n mode={PRESETS.placeholder.mode}\n direction={PRESETS.placeholder.direction}\n blur={PRESETS.placeholder.blur}\n duration={PRESETS.placeholder.duration}\n initial=\"initial\"\n animate=\"animate\"\n className={`waapi-token-placeholder ${placeholderClassName}`}\n />\n )}\n\n {visibleTokens.map((token, index) => {\n const isExiting = animatingIdsRef.current.has(token.id);\n\n // Enhanced separator logic: show separator if not last token AND separator is not completed\n const showSeparator = shouldShowSeparator(token.id, index);\n\n return (\n <div\n key={token.id}\n className={`waapi-token-wrapper ${tokenClassName} ${isExiting ? 'exit-active' : ''}`}\n ref={el => registerElement(token.id, el as HTMLElement)}\n >\n <SlidingText\n text={token.text}\n mode={isExiting ? 'none' : textAnimationMode}\n direction={textDirection}\n staggerDelay={textStaggerDelay}\n duration={ANIMATION_DEFAULTS.DURATION_ENTER}\n blur={PRESETS.newToken.blur}\n widthAnimation={!isExiting && enableWidthAnimation}\n initial={isExiting ? false : 'initial'}\n animate=\"animate\"\n />\n {showSeparator && (\n <span\n className={`waapi-token-separator ${separatorClassName}`}\n ref={el => registerSeparator(token.id, el)}\n >\n {separator}\n </span>\n )}\n </div>\n );\n })}\n\n {(overflowCount > 0 || isOverflowExiting) && (\n <div\n className={`waapi-token-overflow ${tokenClassName} ${isOverflowEntering ? 'entering' : ''} ${isOverflowExiting ? 'exiting' : ''}`}\n ref={el => registerElement('overflow-counter', el as HTMLElement)}\n >\n {visibleTokens.length > 0 && (\n <span className={`waapi-token-separator ${separatorClassName}`}>\n {separator}\n </span>\n )}\n <span className={`waapi-token-separator ${separatorClassName}`}>+</span>\n <SlidingNumber\n value={overflowCount}\n duration={ANIMATION_DEFAULTS.DURATION_ENTER}\n fontSize=\"inherit\"\n fontWeight=\"inherit\"\n color=\"inherit\"\n />\n <SlidingText\n text=\" more\"\n mode={textAnimationMode}\n direction={textDirection}\n staggerDelay={textStaggerDelay}\n duration={isOverflowExiting ? ANIMATION_DEFAULTS.DURATION_EXIT : ANIMATION_DEFAULTS.DURATION_ENTER}\n blur={PRESETS.newToken.blur}\n initial={isOverflowEntering ? 'initial' : false}\n animate=\"animate\"\n />\n </div>\n )}\n </div>\n );\n};\n","import { useRef, useCallback, useEffect } from 'react';\nimport type { ElementRegistryAPI, ElementRegistryCallbacks } from './types';\n\n/**\n * Hook for tracking DOM elements by ID\n * Extracted from useWAAPIAnimations element registration logic\n *\n * @example\n * ```tsx\n * const registry = useElementRegistry({\n * onRegister: (id) => console.log(`Registered: ${id}`),\n * onUnregister: (id) => console.log(`Unregistered: ${id}`)\n * });\n *\n * // In render:\n * <div ref={el => registry.register('item-1', el)} />\n * ```\n */\nexport function useElementRegistry(\n callbacks?: ElementRegistryCallbacks\n): ElementRegistryAPI {\n const elementsRef = useRef<Map<string, HTMLElement>>(new Map());\n\n const callbacksRef = useRef(callbacks);\n useEffect(() => {\n callbacksRef.current = callbacks;\n }, [callbacks]);\n\n const register = useCallback((id: string, el: HTMLElement | null) => {\n if (el) {\n elementsRef.current.set(id, el);\n callbacksRef.current?.onRegister?.(id, el);\n } else {\n if (elementsRef.current.has(id)) {\n elementsRef.current.delete(id);\n callbacksRef.current?.onUnregister?.(id);\n }\n }\n }, []);\n\n const unregister = useCallback((id: string) => {\n if (elementsRef.current.has(id)) {\n elementsRef.current.delete(id);\n callbacksRef.current?.onUnregister?.(id);\n }\n }, []);\n\n const get = useCallback((id: string): HTMLElement | undefined => {\n return elementsRef.current.get(id);\n }, []);\n\n const getAll = useCallback((): Map<string, HTMLElement> => {\n return new Map(elementsRef.current);\n }, []);\n\n const has = useCallback((id: string): boolean => {\n return elementsRef.current.has(id);\n }, []);\n\n const clear = useCallback(() => {\n const ids = Array.from(elementsRef.current.keys());\n elementsRef.current.clear();\n ids.forEach(id => callbacksRef.current?.onUnregister?.(id));\n }, []);\n\n return {\n register,\n unregister,\n get,\n getAll,\n has,\n clear,\n get size() {\n return elementsRef.current.size;\n }\n };\n}\n","import { useRef, useCallback } from 'react';\nimport { TIMING } from '../utils/animationUtils';\nimport type { PositionCaptureAPI, FLIPDelta, PositionCaptureOptions } from './types';\n\nconst DEFAULT_MIN_DELTA_PX = TIMING.MIN_DELTA_PX;\n\n/**\n * Hook for capturing element positions and calculating FLIP deltas\n * Extracted from useWAAPIAnimations position capture logic\n *\n * @example\n * ```tsx\n * const registry = useElementRegistry();\n * const positions = usePositionCapture(registry.getAll);\n *\n * // Before layout change:\n * const before = positions.capture();\n *\n * // After layout change:\n * const after = positions.capture();\n * const deltas = positions.calculateDeltas(before, after);\n * ```\n */\nexport function usePositionCapture(\n getElements: () => Map<string, HTMLElement>,\n options?: PositionCaptureOptions\n): PositionCaptureAPI {\n const lastCaptureRef = useRef<Map<string, DOMRect>>(new Map());\n const minDeltaPx = options?.minDeltaPx ?? DEFAULT_MIN_DELTA_PX;\n\n const capture = useCallback((excludeIds?: Set<string>): Map<string, DOMRect> => {\n const positions = new Map<string, DOMRect>();\n const elements = getElements();\n\n elements.forEach((el, id) => {\n if (excludeIds?.has(id)) return;\n\n const rect = el.getBoundingClientRect();\n if (rect.width > 0 && rect.height > 0) {\n positions.set(id, rect);\n }\n });\n\n lastCaptureRef.current = positions;\n return positions;\n }, [getElements]);\n\n const getPosition = useCallback((id: string): DOMRect | undefined => {\n return lastCaptureRef.current.get(id);\n }, []);\n\n const calculateDeltas = useCallback((\n before: Map<string, DOMRect>,\n after: Map<string, DOMRect>\n ): Map<string, FLIPDelta> => {\n const deltas = new Map<string, FLIPDelta>();\n\n after.forEach((newRect, id) => {\n const prevRect = before.get(id);\n if (!prevRect) return;\n\n const deltaX = prevRect.left - newRect.left;\n const deltaY = prevRect.top - newRect.top;\n const deltaWidth = prevRect.width - newRect.width;\n const deltaHeight = prevRect.height - newRect.height;\n\n const isSignificant =\n Math.abs(deltaX) >= minDeltaPx ||\n Math.abs(deltaY) >= minDeltaPx ||\n Math.abs(deltaWidth) >= minDeltaPx ||\n Math.abs(deltaHeight) >= minDeltaPx;\n\n deltas.set(id, {\n id,\n deltaX,\n deltaY,\n deltaWidth,\n deltaHeight,\n isSignificant\n });\n });\n\n return deltas;\n }, [minDeltaPx]);\n\n const getLastCapture = useCallback((): Map<string, DOMRect> => {\n return new Map(lastCaptureRef.current);\n }, []);\n\n const clear = useCallback(() => {\n lastCaptureRef.current.clear();\n }, []);\n\n return {\n capture,\n getPosition,\n calculateDeltas,\n getLastCapture,\n clear\n };\n}\n","import { useRef, useCallback, useEffect } from 'react';\nimport { TIMING, EASINGS } from '../utils/animationUtils';\nimport type { FLIPAnimationAPI, FLIPAnimationOptions, FLIPDelta } from './types';\n\nconst DEFAULT_DURATION = TIMING.FLIP_DURATION;\nconst DEFAULT_EASING = EASINGS.SPRING_GENTLE;\n\n/**\n * Hook for executing FLIP (First-Last-Invert-Play) animations\n * Extracted from useWAAPIAnimations FLIP logic\n *\n * Uses spring easing for natural motion with slight overshoot\n *\n * @example\n * ```tsx\n * const flip = useFLIPAnimation();\n *\n * // After calculating deltas:\n * await flip.animateAll(elements, deltas, {\n * duration: 300,\n * onComplete: (id) => console.log(`${id} completed`)\n * });\n * ```\n */\nexport function useFLIPAnimation(): FLIPAnimationAPI {\n const activeAnimationsRef = useRef<Map<string, Animation>>(new Map());\n const animatingIdsRef = useRef<Set<string>>(new Set());\n\n const animate = useCallback((\n element: HTMLElement,\n delta: FLIPDelta,\n options?: FLIPAnimationOptions\n ): Animation => {\n const duration = options?.duration ?? DEFAULT_DURATION;\n const easing = options?.easing ?? DEFAULT_EASING;\n\n if (activeAnimationsRef.current.has(delta.id)) {\n activeAnimationsRef.current.get(delta.id)?.cancel();\n }\n\n animatingIdsRef.current.add(delta.id);\n options?.onStart?.(delta.id);\n\n const animation = element.animate([\n { transform: `translate3d(${delta.deltaX}px, ${delta.deltaY}px, 0)` },\n { transform: 'translate3d(0, 0, 0)' }\n ], {\n duration,\n easing\n });\n\n activeAnimationsRef.current.set(delta.id, animation);\n\n animation.onfinish = () => {\n animatingIdsRef.current.delete(delta.id);\n activeAnimationsRef.current.delete(delta.id);\n options?.onComplete?.(delta.id);\n };\n\n animation.oncancel = () => {\n animatingIdsRef.current.delete(delta.id);\n activeAnimationsRef.current.delete(delta.id);\n };\n\n return animation;\n }, []);\n\n const animateAll = useCallback(async (\n elements: Map<string, HTMLElement>,\n deltas: Map<string, FLIPDelta>,\n options?: FLIPAnimationOptions\n ): Promise<void> => {\n const animations: Animation[] = [];\n\n deltas.forEach((delta, id) => {\n if (!delta.isSignificant) return;\n\n const element = elements.get(id);\n if (!element) return;\n\n const anim = animate(element, delta, options);\n animations.push(anim);\n });\n\n if (animations.length === 0) return;\n\n await Promise.all(\n animations.map(anim =>\n anim.finished.catch(() => {})\n )\n );\n }, [animate]);\n\n const cancel = useCallback((id: string) => {\n const animation = activeAnimationsRef.current.get(id);\n if (animation) {\n animation.cancel();\n activeAnimationsRef.current.delete(id);\n animatingIdsRef.current.delete(id);\n }\n }, []);\n\n const cancelAll = useCallback(() => {\n activeAnimationsRef.current.forEach(animation => animation.cancel());\n activeAnimationsRef.current.clear();\n animatingIdsRef.current.clear();\n }, []);\n\n const isAnimating = useCallback((id?: string): boolean => {\n if (id) return animatingIdsRef.current.has(id);\n return animatingIdsRef.current.size > 0;\n }, []);\n\n useEffect(() => {\n return () => {\n cancelAll();\n };\n }, [cancelAll]);\n\n // CRITICAL: Memoize return object to prevent cascading re-renders\n // that would cancel animations via useEffect cleanup chains\n const api = useRef<FLIPAnimationAPI>({\n animate,\n animateAll,\n cancel,\n cancelAll,\n isAnimating\n });\n\n // Update refs to latest callbacks (they're stable due to useCallback)\n api.current.animate = animate;\n api.current.animateAll = animateAll;\n api.current.cancel = cancel;\n api.current.cancelAll = cancelAll;\n api.current.isAnimating = isAnimating;\n\n return api.current;\n}\n","import { useRef, useCallback, useEffect } from 'react';\nimport { TIMING, TRANSFORMS, EFFECTS, EASINGS } from '../utils/animationUtils';\nimport { useElementRegistry } from './useElementRegistry';\nimport { usePositionCapture } from './usePositionCapture';\nimport { useFLIPAnimation } from './useFLIPAnimation';\nimport { useDebug } from '../contexts/DebugContext';\nimport type {\n AnimationOrchestratorAPI,\n AnimationOrchestratorConfig,\n ExitOptions,\n EnterOptions,\n OrchestratorState\n} from './types';\n\n/**\n * Main animation orchestrator hook\n * Composes useElementRegistry, usePositionCapture, and useFLIPAnimation\n *\n * Handles the full exit animation sequence:\n * 1. Capture positions of remaining elements\n * 2. Run exit animation (fade + width collapse)\n * 3. Start FLIP animations at 25% of exit (overlap)\n * 4. Cleanup and notify completion\n *\n * @example\n * ```tsx\n * const orchestrator = useAnimationOrchestrator({\n * onExitComplete: (id) => removeFromList(id)\n * });\n *\n * // Register elements:\n * <div ref={el => orchestrator.registerElement('item-1', el)} />\n *\n * // Trigger exit:\n * await orchestrator.startExit('item-1');\n * ```\n */\nexport function useAnimationOrchestrator(\n config?: AnimationOrchestratorConfig\n): AnimationOrchestratorAPI {\n const configRef = useRef(config);\n useEffect(() => {\n configRef.current = config;\n }, [config]);\n\n // Debug context - ref pattern to avoid callback recreation\n const { logEvent } = useDebug();\n const logEventRef = useRef(logEvent);\n useEffect(() => {\n logEventRef.current = logEvent;\n }, [logEvent]);\n\n const stateRef = useRef<OrchestratorState>({\n animatingIds: new Set(),\n positions: new Map(),\n activeAnimations: new Map()\n });\n\n const registry = useElementRegistry();\n const positions = usePositionCapture(registry.getAll, {\n minDeltaPx: config?.minDeltaPx ?? TIMING.MIN_DELTA_PX\n });\n const flip = useFLIPAnimation();\n\n const exitDuration = config?.exitDuration ?? TIMING.EXIT_DURATION;\n const flipDuration = config?.flipDuration ?? TIMING.FLIP_DURATION;\n const exitEasing = config?.exitEasing ?? EASINGS.EASE_IN_CUBIC;\n\n const cancelAnimation = useCallback((id: string) => {\n const animations = stateRef.current.activeAnimations.get(id);\n if (animations) {\n animations.forEach(anim => anim.cancel());\n stateRef.current.activeAnimations.delete(id);\n }\n stateRef.current.animatingIds.delete(id);\n flip.cancel(id);\n }, [flip]);\n\n const cancelAllAnimations = useCallback(() => {\n stateRef.current.activeAnimations.forEach(animations => {\n animations.forEach(anim => anim.cancel());\n });\n stateRef.current.activeAnimations.clear();\n stateRef.current.animatingIds.clear();\n flip.cancelAll();\n }, [flip]);\n\n // Get behavior configuration with defaults\n const flipBehavior = config?.flipBehavior ?? 'all';\n const exitPositionStrategy = config?.exitPositionStrategy ?? 'absolute-fixed';\n\n /**\n * NEW FLIP IMPLEMENTATION - Based on research from:\n * - Paul Lewis: FLIP Your Animations (aerotwist.com)\n * - react-flip-move (github.com/joshwcomeau/react-flip-move)\n * - Framer Motion popLayout mode\n *\n * Key changes from old implementation:\n * 1. NO display:none - use position:absolute directly\n * 2. Double RAF for reliable reflow waiting\n * 3. Correct position calculation using getBoundingClientRect\n * 4. Sequential FLIP: capture AFTER only after reflow settles\n */\n const startExit = useCallback(async (id: string, options?: ExitOptions): Promise<void> => {\n const el = registry.get(id);\n\n if (__DEBUG__) {\n logEventRef.current({\n type: 'reorder-exit-check',\n source: 'useAnimationOrchestrator.startExit',\n message: `Exit check for ${id}: element=${!!el}, alreadyAnimating=${stateRef.current.animatingIds.has(id)}`,\n data: {\n id,\n elementFound: !!el,\n alreadyAnimating: stateRef.current.animatingIds.has(id),\n registrySize: registry.size,\n animatingIds: Array.from(stateRef.current.animatingIds),\n flipBehavior,\n exitPositionStrategy\n }\n });\n }\n\n if (!el || stateRef.current.animatingIds.has(id)) {\n if (__DEBUG__ && stateRef.current.animatingIds.has(id)) {\n logEventRef.current({\n type: 'reorder-exit-rejected',\n source: 'useAnimationOrchestrator.startExit',\n message: `Exit rejected for ${id} - already animating`,\n data: {\n id,\n reason: 'already-animating',\n currentAnimatingIds: Array.from(stateRef.current.animatingIds)\n }\n });\n }\n return;\n }\n\n stateRef.current.animatingIds.add(id);\n\n const duration = options?.duration ?? exitDuration;\n const easing = options?.easing ?? exitEasing;\n\n // ========================================\n // STEP 1: FIRST - Capture ALL positions BEFORE any DOM/CSS changes\n // CRITICAL: Must capture BEFORE setting data attributes!\n // CSS rule [data-reorder-state=\"exiting\"][data-exit-position=\"absolute\"]\n // would trigger position:absolute and cause reflow before we measure\n // ========================================\n const allElements = registry.getAll();\n const beforePositions = new Map<string, DOMRect>();\n\n // Capture position of exiting element FIRST\n const exitingRect = el.getBoundingClientRect();\n beforePositions.set(id, exitingRect);\n\n // Capture positions of all siblings\n allElements.forEach((element, elemId) => {\n if (elemId !== id) {\n const rect = element.getBoundingClientRect();\n if (rect.width > 0 && rect.height > 0) {\n beforePositions.set(elemId, rect);\n }\n }\n });\n\n const parent = el.parentElement;\n\n // NOW set data attributes (this triggers CSS position:absolute)\n el.dataset.reorderState = 'exiting';\n if (exitPositionStrategy === 'absolute-fixed') {\n el.dataset.exitPosition = 'absolute';\n }\n\n if (__DEBUG__) {\n logEventRef.current({\n type: 'reorder-flip-before-capture',\n source: 'useAnimationOrchestrator.startExit',\n message: `FIRST: All positions captured including exiting element`,\n data: {\n exitingId: id,\n exitingPosition: { x: exitingRect.left, y: exitingRect.top },\n siblingCount: beforePositions.size - 1,\n positions: Object.fromEntries([...beforePositions].map(([k, v]) => [k, { x: v.left, y: v.top }]))\n }\n });\n }\n\n // ========================================\n // STEP 2: Apply position:absolute to exiting element\n // Calculate position relative to parent BEFORE it re-centers\n // ========================================\n const parentRectBefore = parent?.getBoundingClientRect() || { left: 0, top: 0 };\n const absoluteLeft = exitingRect.left - parentRectBefore.left;\n const absoluteTop = exitingRect.top - parentRectBefore.top;\n\n if (exitPositionStrategy === 'absolute-fixed') {\n el.style.position = 'absolute';\n el.style.left = `${absoluteLeft}px`;\n el.style.top = `${absoluteTop}px`;\n el.style.width = `${exitingRect.width}px`;\n el.style.height = `${exitingRect.height}px`;\n el.style.margin = '0';\n }\n\n // Capture parent position AFTER setting absolute (parent may re-center)\n const parentRectAfter = parent?.getBoundingClientRect() || { left: 0, top: 0 };\n const parentDeltaX = parentRectAfter.left - parentRectBefore.left;\n const parentDeltaY = parentRectAfter.top - parentRectBefore.top;\n\n // Compensate for parent movement with transform\n if (Math.abs(parentDeltaX) > 0.5 || Math.abs(parentDeltaY) > 0.5) {\n el.style.transform = `translate(${-parentDeltaX}px, ${-parentDeltaY}px)`;\n }\n\n if (__DEBUG__) {\n logEventRef.current({\n type: 'reorder-position-absolute',\n source: 'useAnimationOrchestrator.startExit',\n message: `Position absolute with parent compensation`,\n data: {\n id,\n position: { left: absoluteLeft, top: absoluteTop },\n parentDelta: { x: parentDeltaX, y: parentDeltaY },\n width: exitingRect.width,\n height: exitingRect.height\n }\n });\n }\n\n // ========================================\n // STEP 3: SYNCHRONOUS reflow - NO RAF!\n // getBoundingClientRect forces synchronous layout recalculation\n // This MUST happen before any paint to avoid visual jumping\n // ========================================\n // Force synchronous reflow by reading layout\n // This ensures siblings have moved BEFORE we apply transforms\n // and BEFORE browser paints - user never sees the jump\n\n // ========================================\n // STEP 4: LAST - Capture AFTER positions IMMEDIATELY (same frame!)\n // ========================================\n const afterPositions = new Map<string, DOMRect>();\n allElements.forEach((element, elemId) => {\n if (elemId !== id) {\n // getBoundingClientRect forces synchronous layout\n const rect = element.getBoundingClientRect();\n if (rect.width > 0 && rect.height > 0) {\n afterPositions.set(elemId, rect);\n }\n }\n });\n\n if (__DEBUG__) {\n logEventRef.current({\n type: 'reorder-flip-after-positions',\n source: 'useAnimationOrchestrator.startExit',\n message: `LAST: After positions captured (synchronous)`,\n data: {\n captureSource: 'synchronous-reflow',\n capturedIds: [...afterPositions.keys()],\n positions: Object.fromEntries([...afterPositions].map(([k, v]) => [k, { x: v.left, y: v.top }])),\n perfNow: performance.now()\n }\n });\n }\n\n // ========================================\n // STEP 5: INVERT + PLAY - Calculate deltas and animate siblings\n // ========================================\n const flipAnimations: Animation[] = [];\n\n if (flipBehavior !== 'none') {\n // Determine which siblings to animate based on flipBehavior\n let siblingsToAnimate: string[] = [];\n\n if (flipBehavior === 'all') {\n siblingsToAnimate = [...afterPositions.keys()];\n } else if (flipBehavior === 'siblings-after') {\n // Get elements sorted by ACTUAL DOM order using compareDocumentPosition\n const elementsWithIds = Array.from(allElements.entries());\n elementsWithIds.sort((a, b) => {\n const posA = a[1];\n const posB = b[1];\n const position = posA.compareDocumentPosition(posB);\n return position & Node.DOCUMENT_POSITION_FOLLOWING ? -1 : 1;\n });\n\n const sortedKeys = elementsWithIds.map(([k]) => k);\n const exitingIndex = sortedKeys.indexOf(id);\n\n if (exitingIndex !== -1) {\n siblingsToAnimate = sortedKeys.slice(exitingIndex + 1).filter(k => afterPositions.has(k));\n }\n\n if (__DEBUG__) {\n logEventRef.current({\n type: 'reorder-flip-filter',\n source: 'useAnimationOrchestrator.startExit',\n message: `Filtered to siblings-after (DOM order)`,\n data: {\n exitingIndex,\n sortedKeys,\n siblingsToAnimate,\n filteredCount: siblingsToAnimate.length\n }\n });\n }\n }\n\n // Apply FLIP to each sibling - only those with significant deltas will animate\n siblingsToAnimate.forEach(siblingId => {\n const before = beforePositions.get(siblingId);\n const after = afterPositions.get(siblingId);\n\n if (!before || !after) return;\n\n // Calculate delta: FIRST - LAST (how much to \"invert\" to get back to first)\n const deltaX = before.left - after.left;\n const deltaY = before.top - after.top;\n\n // Only animate if movement is significant\n const isSignificant = Math.abs(deltaX) > 1 || Math.abs(deltaY) > 1;\n\n if (__DEBUG__) {\n logEventRef.current({\n type: 'reorder-flip-delta',\n source: 'useAnimationOrchestrator.startExit',\n message: `Delta for ${siblingId}: (${deltaX.toFixed(1)}, ${deltaY.toFixed(1)}) significant=${isSignificant}`,\n position: { element: siblingId, x: after.left, y: after.top, width: after.width, height: after.height, delta: { x: deltaX, y: deltaY } },\n data: {\n id: siblingId,\n deltaX,\n deltaY,\n isSignificant\n }\n });\n }\n\n if (isSignificant) {\n const siblingEl = allElements.get(siblingId);\n if (siblingEl) {\n siblingEl.dataset.reorderState = 'flipping';\n\n // CRITICAL: Apply initial transform SYNCHRONOUSLY via CSS\n // This ensures the element appears in \"before\" position on first paint\n // element.animate() does NOT apply initial keyframe synchronously!\n siblingEl.style.transform = `translate(${deltaX}px, ${deltaY}px)`;\n\n // Now animate FROM current transform TO none\n const anim = siblingEl.animate([\n { transform: `translate(${deltaX}px, ${deltaY}px)` },\n { transform: 'none' }\n ], {\n duration: flipDuration,\n easing: EASINGS.MATERIAL_DECELERATE,\n fill: 'forwards'\n });\n\n flipAnimations.push(anim);\n\n // Reset state and clear inline transform when animation completes\n anim.finished.then(() => {\n siblingEl.style.transform = '';\n siblingEl.dataset.reorderState = 'idle';\n }).catch(() => {\n siblingEl.style.transform = '';\n siblingEl.dataset.reorderState = 'idle';\n });\n\n if (__DEBUG__) {\n logEventRef.current({\n type: 'reorder-transform-applied',\n source: 'useAnimationOrchestrator.startExit',\n message: `FLIP animation started for ${siblingId}`,\n data: {\n element: siblingId,\n transform: `translate(${deltaX}px, ${deltaY}px)`,\n deltaX,\n deltaY,\n perfNow: performance.now()\n }\n });\n }\n }\n }\n });\n }\n\n // ========================================\n // STEP 6: Animate exiting element (fade out)\n // ========================================\n const exitAnimations: Animation[] = [];\n const tokens = el.querySelectorAll('.sliding-text-token');\n\n if (__DEBUG__) {\n logEventRef.current({\n type: 'reorder-animation-setup',\n source: 'useAnimationOrchestrator.startExit',\n message: `Exit animation setup for ${id}`,\n data: {\n id,\n tokensCount: tokens.length,\n duration,\n easing,\n flipAnimationsCount: flipAnimations.length\n }\n });\n }\n\n if (tokens.length > 0) {\n tokens.forEach((token, index) => {\n const delay = index * TIMING.EXIT_STAGGER;\n const anim = (token as HTMLElement).animate([\n { opacity: 1, transform: 'translateY(0) scale(1)', filter: 'blur(0px)' },\n {\n opacity: 0,\n transform: `translateY(${TRANSFORMS.OFFSET_Y_EXIT}px) scale(${TRANSFORMS.SCALE_EXIT})`,\n filter: `blur(${EFFECTS.BLUR_EXIT}px)`\n }\n ], {\n duration,\n easing,\n delay,\n fill: 'forwards'\n });\n exitAnimations.push(anim);\n });\n } else {\n const anim = el.animate([\n { opacity: 1, transform: 'scale(1)' },\n { opacity: 0, transform: `scale(${TRANSFORMS.SCALE_EXIT})` }\n ], {\n duration,\n easing,\n fill: 'forwards'\n });\n exitAnimations.push(anim);\n }\n\n stateRef.current.activeAnimations.set(id, [...exitAnimations, ...flipAnimations]);\n\n // ========================================\n // STEP 7: Wait for all animations to complete\n // ========================================\n try {\n if (__DEBUG__) {\n logEventRef.current({\n type: 'reorder-await-start',\n source: 'useAnimationOrchestrator.startExit',\n message: `Waiting for exit + FLIP animations`,\n data: {\n id,\n exitAnimationsCount: exitAnimations.length,\n flipAnimationsCount: flipAnimations.length\n }\n });\n }\n\n // Wait for all animations (exit + FLIP) to complete\n await Promise.all([\n ...exitAnimations.map(a => a.finished),\n ...flipAnimations.map(a => a.finished)\n ]);\n\n if (__DEBUG__) {\n logEventRef.current({\n type: 'reorder-await-complete',\n source: 'useAnimationOrchestrator.startExit',\n message: `All animations completed for ${id}`,\n data: {\n id,\n elementStillConnected: el.isConnected\n }\n });\n }\n\n } catch (error) {\n if (__DEBUG__) {\n logEventRef.current({\n type: 'reorder-animation-error',\n source: 'useAnimationOrchestrator.startExit',\n message: `Animation error/cancelled for ${id}`,\n data: {\n id,\n error: String(error),\n elementStillConnected: el.isConnected\n }\n });\n }\n stateRef.current.animatingIds.delete(id);\n stateRef.current.activeAnimations.delete(id);\n return;\n }\n\n // ========================================\n // STEP 8: Cleanup\n // ========================================\n stateRef.current.animatingIds.delete(id);\n stateRef.current.activeAnimations.delete(id);\n\n el.dataset.reorderState = 'completed';\n\n // Clean up inline styles\n if (exitPositionStrategy === 'absolute-fixed') {\n el.style.removeProperty('position');\n el.style.removeProperty('left');\n el.style.removeProperty('top');\n el.style.removeProperty('width');\n el.style.removeProperty('height');\n el.style.removeProperty('margin');\n }\n\n delete el.dataset.exitPosition;\n registry.unregister(id);\n\n if (__DEBUG__) {\n logEventRef.current({\n type: 'reorder-exit-cleanup',\n source: 'useAnimationOrchestrator.startExit',\n message: `Exit complete for ${id}`,\n data: {\n id,\n finalState: el.dataset.reorderState\n }\n });\n }\n\n options?.onComplete?.();\n configRef.current?.onExitComplete?.(id);\n }, [registry, exitDuration, exitEasing, flipDuration, flipBehavior, exitPositionStrategy]);\n\n /**\n * Start enter animation for an element\n *\n * Architecture: CSS + WAAPI\n * - Assumes element already has data-reorder-state=\"entering\" (set by Reorder.tsx)\n * - CSS [data-reorder-state=\"entering\"] { opacity: 0; transform: translateY(-8px) scale(0.95); }\n * - WAAPI animates FROM that state TO visible\n * - Sets data-reorder-state=\"idle\" on completion (CSS handles final state)\n */\n const startEnter = useCallback(async (id: string, options?: EnterOptions): Promise<void> => {\n const el = registry.get(id);\n if (!el) {\n if (__DEBUG__) {\n logEventRef.current({\n type: 'reorder-animation-error',\n source: 'useAnimationOrchestrator.startEnter',\n message: `Element not found for ${id}`,\n data: { id, registrySize: registry.size }\n });\n }\n return;\n }\n\n // Element should already have data-reorder-state=\"entering\" from Reorder.tsx\n // CSS hides it. If not set, set it now (fallback)\n if (!el.dataset.reorderState) {\n el.dataset.reorderState = 'entering';\n }\n\n const enterDuration = configRef.current?.enterDuration ?? TIMING.ENTER_DURATION;\n const enterEasing = configRef.current?.enterEasing ?? EASINGS.MATERIAL_DECELERATE;\n\n const duration = options?.duration ?? enterDuration;\n const easing = options?.easing ?? enterEasing;\n const stagger = options?.stagger ?? TIMING.ENTER_STAGGER;\n\n if (__DEBUG__) {\n logEventRef.current({\n type: 'reorder-enter-start',\n source: 'useAnimationOrchestrator.startEnter',\n message: `Enter animation for ${id}`,\n data: {\n id,\n duration,\n easing,\n stagger,\n currentState: el.dataset.reorderState\n }\n });\n }\n\n const tokens = el.querySelectorAll('.sliding-text-token');\n const animations: Animation[] = [];\n\n // Animation keyframes match CSS entering state → visible\n // CSS: opacity: 0, transform: translateY(-8px) scale(0.95)\n const fromKeyframe = {\n opacity: 0,\n transform: 'translateY(-8px) scale(0.95)'\n };\n const toKeyframe = {\n opacity: 1,\n transform: 'none'\n };\n\n if (tokens.length > 0) {\n tokens.forEach((token, index) => {\n const delay = index * stagger;\n (token as HTMLElement).dataset.reorderIndex = String(index);\n\n const anim = (token as HTMLElement).animate([\n { ...fromKeyframe, filter: `blur(${EFFECTS.BLUR_ENTER}px)` },\n { ...toKeyframe, filter: 'blur(0px)' }\n ], {\n duration,\n easing,\n delay,\n fill: 'forwards'\n });\n animations.push(anim);\n });\n } else {\n const anim = el.animate([fromKeyframe, toKeyframe], {\n duration,\n easing,\n fill: 'forwards'\n });\n animations.push(anim);\n }\n\n try {\n await Promise.all(animations.map(a => a.finished));\n } catch {\n // Animation cancelled\n el.dataset.reorderState = 'idle';\n return;\n }\n\n // Animation complete - CSS idle state takes over\n el.dataset.reorderState = 'idle';\n\n if (__DEBUG__) {\n logEventRef.current({\n type: 'reorder-enter-complete',\n source: 'useAnimationOrchestrator.startEnter',\n message: `Enter complete for ${id}`,\n data: { id, finalState: 'idle' }\n });\n }\n\n options?.onComplete?.();\n configRef.current?.onEnterComplete?.(id);\n }, [registry]);\n\n const isAnimating = useCallback((id?: string): boolean => {\n if (id) return stateRef.current.animatingIds.has(id);\n return stateRef.current.animatingIds.size > 0;\n }, []);\n\n const capturePositions = useCallback((excludeIds?: Set<string>) => {\n return positions.capture(excludeIds);\n }, [positions]);\n\n useEffect(() => {\n return () => {\n cancelAllAnimations();\n };\n }, [cancelAllAnimations]);\n\n return {\n registry,\n positions,\n flip,\n registerElement: registry.register,\n startExit,\n startEnter,\n isAnimating,\n cancelAnimation,\n cancelAllAnimations,\n capturePositions\n };\n}\n","import { useRef, useEffect } from 'react';\nimport { useAnimationOrchestrator } from '../../core/useAnimationOrchestrator';\nimport { useDebug } from '../../contexts/DebugContext';\nimport type { UseReorderReturn, UseReorderConfig } from './types';\n\n/**\n * Hook for managing reorderable lists with FLIP animations.\n *\n * Architecture: Thin wrapper around useAnimationOrchestrator\n * - Delegates all animation logic to orchestrator\n * - Provides stable API surface for Reorder component\n *\n * @example\n * ```tsx\n * const reorder = useReorder({\n * onComplete: (id) => removeFromList(id)\n * });\n *\n * // Register elements:\n * <div ref={el => reorder.registerElement('item-1', el)}>\n * Item 1\n * </div>\n *\n * // Trigger exit:\n * await reorder.startItemExit('item-1');\n * ```\n */\nexport function useReorder(config?: UseReorderConfig): UseReorderReturn {\n const { logEvent } = useDebug();\n const logEventRef = useRef(logEvent);\n useEffect(() => {\n logEventRef.current = logEvent;\n }, [logEvent]);\n\n const orchestrator = useAnimationOrchestrator({\n enterDuration: config?.enterDuration,\n exitDuration: config?.exitDuration,\n flipDuration: config?.flipDuration,\n enterEasing: config?.enterEasing,\n exitEasing: config?.exitEasing,\n flipEasing: config?.flipEasing,\n flipBehavior: config?.flipBehavior,\n onExitComplete: config?.onComplete\n });\n\n useEffect(() => {\n if (__DEBUG__) {\n logEventRef.current({\n type: 'render',\n source: 'useReorder.mount',\n message: 'useReorder hook mounted',\n data: {\n config: {\n enterDuration: config?.enterDuration,\n exitDuration: config?.exitDuration,\n flipDuration: config?.flipDuration\n }\n }\n });\n }\n\n return () => {\n if (__DEBUG__) {\n logEventRef.current({\n type: 'component-unmounted',\n source: 'useReorder.unmount',\n message: 'useReorder hook unmounting',\n data: {\n registrySize: orchestrator.registry.size\n }\n });\n }\n };\n }, []);\n\n // Return orchestrator methods with stable API names\n return {\n ...orchestrator,\n registerElement: orchestrator.registerElement,\n startItemExit: orchestrator.startExit,\n startItemEnter: orchestrator.startEnter\n };\n}\n","import {\n useRef,\n useCallback,\n useMemo,\n useLayoutEffect,\n useEffect,\n Children,\n isValidElement,\n cloneElement,\n type ReactNode,\n type ReactElement\n} from 'react';\nimport { useReorder } from './useReorder';\nimport { useDebug } from '../../contexts/DebugContext';\nimport type {\n UseReorderPresenceConfig,\n UseReorderPresenceReturn\n} from './types';\n\n/**\n * Hook for managing presence animations in reorderable lists\n *\n * IMPORTANT: This hook follows the AnimatedTokens pattern where:\n * - Elements stay in DOM until animation completes\n * - Consumer must keep items in their state until onAnimationComplete fires\n * - No \"ghost\" cloning - animations run on actual DOM elements\n *\n * @example\n * ```tsx\n * const [items, setItems] = useState(INITIAL_ITEMS);\n *\n * const { presentChildren, triggerExit } = useReorderPresence(\n * items.map(item => <div key={item.id}>{item.name}</div>),\n * {\n * onAnimationComplete: (id) => {\n * // Only remove from state AFTER animation completes\n * setItems(prev => prev.filter(item => item.id !== id));\n * }\n * }\n * );\n *\n * const handleDelete = (id: string) => {\n * // DON'T remove from state - just trigger animation\n * triggerExit(id);\n * };\n * ```\n */\nexport function useReorderPresence(\n children: ReactNode,\n config: UseReorderPresenceConfig = {}\n): UseReorderPresenceReturn {\n const {\n autoAnimate = true,\n stagger,\n enterDuration,\n exitDuration,\n flipDuration,\n enterEasing,\n exitEasing,\n flipEasing\n } = config;\n\n // Debug context - uses ref pattern to avoid recreating callbacks\n const { logEvent } = useDebug();\n const logEventRef = useRef(logEvent);\n useEffect(() => {\n logEventRef.current = logEvent;\n }, [logEvent]);\n\n // Config ref pattern\n const configRef = useRef(config);\n useEffect(() => {\n configRef.current = config;\n }, [config]);\n\n // Track animating IDs via REF (not state!) - like AnimatedTokens\n const exitingIdsRef = useRef<Set<string>>(new Set());\n const enteringIdsRef = useRef<Set<string>>(new Set());\n\n // Track previous children keys for change detection\n const prevKeysRef = useRef<Set<string>>(new Set());\n\n // Helper to calculate stagger delay\n const getStaggerDelay = useCallback((index: number, type: 'enter' | 'exit'): number => {\n if (!stagger) return 0;\n if (typeof stagger === 'number') return stagger * index;\n return (stagger[type] ?? 0) * index;\n }, [stagger]);\n\n // Stable completion handler - calls consumer's onAnimationComplete\n const handleAnimationComplete = useCallback((id: string) => {\n if (__DEBUG__) {\n logEventRef.current({\n type: 'reorder-animation-complete',\n source: 'useReorderPresence.handleAnimationComplete',\n message: `Animation complete, notifying consumer: ${id}`,\n data: { id, remainingExiting: exitingIdsRef.current.size - 1 }\n });\n }\n\n exitingIdsRef.current.delete(id);\n configRef.current.onAnimationComplete?.(id);\n }, []);\n\n // Create reorder instance with completion handler\n const reorder = useReorder({\n enterDuration,\n exitDuration,\n flipDuration,\n enterEasing,\n exitEasing,\n flipEasing,\n onComplete: handleAnimationComplete\n });\n\n // Extract current children keys\n const currentKeys = useMemo(() => {\n const keys = new Set<string>();\n Children.forEach(children, child => {\n if (isValidElement(child) && child.key != null) {\n keys.add(String(child.key));\n }\n });\n return keys;\n }, [children]);\n\n // Detect and process changes - following AnimatedTokens pattern\n useLayoutEffect(() => {\n const prevKeys = prevKeysRef.current;\n\n // Detect additions (in current but not in prev)\n const added: string[] = [];\n currentKeys.forEach(key => {\n if (!prevKeys.has(key) && !enteringIdsRef.current.has(key)) {\n added.push(key);\n }\n });\n\n // Note: We DON'T detect removals here anymore!\n // Removals are triggered manually via triggerExit()\n // The element stays in children until consumer removes it in onAnimationComplete\n\n // Log detected changes\n if (__DEBUG__ && added.length > 0) {\n logEventRef.current({\n type: 'token-reorder',\n source: 'useReorderPresence.detectChanges',\n message: `Detected ${added.length} additions`,\n data: {\n added,\n currentCount: currentKeys.size,\n previousCount: prevKeys.size,\n autoAnimate,\n stagger\n }\n });\n }\n\n // Process additions - track entering, run WAAPI animation with stagger\n if (added.length > 0) {\n added.forEach((key, index) => {\n const delay = getStaggerDelay(index, 'enter');\n\n const processEnter = () => {\n enteringIdsRef.current.add(key);\n configRef.current.onItemEnter?.(key);\n\n if (__DEBUG__) {\n logEventRef.current({\n type: 'reorder-item-enter',\n source: 'useReorderPresence.processAdditions',\n message: `Processing enter for: ${key}`,\n timing: {\n startTime: performance.now(),\n expectedDuration: configRef.current.enterDuration ?? 300\n },\n data: { key, index, enteringCount: enteringIdsRef.current.size }\n });\n }\n\n // Start WAAPI enter animation (deferred to allow DOM to mount)\n requestAnimationFrame(() => {\n reorder.startItemEnter(key).then(() => {\n enteringIdsRef.current.delete(key);\n\n if (__DEBUG__) {\n logEventRef.current({\n type: 'reorder-animation-complete',\n source: 'useReorderPresence.processAdditions',\n message: `Enter animation finished: ${key}`,\n data: { key, remainingEntering: enteringIdsRef.current.size }\n });\n }\n });\n });\n };\n\n if (delay > 0) {\n setTimeout(processEnter, delay);\n } else {\n processEnter();\n }\n });\n }\n\n // Update prev keys for next comparison\n prevKeysRef.current = new Set(currentKeys);\n }, [currentKeys, autoAnimate, reorder, getStaggerDelay]);\n\n // Manual exit trigger - consumer calls this BEFORE removing from state\n const triggerExit = useCallback((id: string) => {\n // Already exiting? Skip\n if (exitingIdsRef.current.has(id)) {\n if (__DEBUG__) {\n logEventRef.current({\n type: 'reorder-item-exit',\n source: 'useReorderPresence.triggerExit',\n message: `Exit already in progress: ${id}`,\n data: { id, reason: 'already-exiting' }\n });\n }\n return;\n }\n\n // Check element is registered\n if (!reorder.registry.has(id)) {\n if (__DEBUG__) {\n logEventRef.current({\n type: 'reorder-item-exit',\n source: 'useReorderPresence.triggerExit',\n message: `Element not registered: ${id}`,\n data: { id, reason: 'not-registered' }\n });\n }\n return;\n }\n\n // Mark as exiting\n exitingIdsRef.current.add(id);\n configRef.current.onItemExit?.(id);\n\n if (__DEBUG__) {\n logEventRef.current({\n type: 'reorder-item-exit',\n source: 'useReorderPresence.triggerExit',\n message: `Exit triggered: ${id}`,\n timing: {\n startTime: performance.now(),\n expectedDuration: configRef.current.exitDuration ?? 200\n },\n data: { id, exitingCount: exitingIdsRef.current.size }\n });\n }\n\n // Start exit animation on the EXISTING element (already in DOM)\n reorder.startItemExit(id);\n }, [reorder]);\n\n // State queries\n const isExiting = useCallback((id: string): boolean => {\n return exitingIdsRef.current.has(id);\n }, []);\n\n const isEntering = useCallback((id: string): boolean => {\n return enteringIdsRef.current.has(id);\n }, []);\n\n const getExitingIds = useCallback((): string[] => {\n return Array.from(exitingIdsRef.current);\n }, []);\n\n const getEnteringIds = useCallback((): string[] => {\n return Array.from(enteringIdsRef.current);\n }, []);\n\n // presentChildren - just the children as-is, with data attribute for exiting\n const presentChildren = useMemo(() => {\n const result: ReactNode[] = [];\n\n Children.forEach(children, child => {\n if (!isValidElement(child)) {\n result.push(child);\n return;\n }\n\n const key = child.key != null ? String(child.key) : null;\n const isCurrentlyExiting = key != null && exitingIdsRef.current.has(key);\n\n // Clone to add data attribute for exiting state (CSS can use this)\n if (isCurrentlyExiting) {\n result.push(\n cloneElement(child as ReactElement<{ 'data-reorder-state'?: string }>, {\n 'data-reorder-state': 'exiting'\n })\n );\n } else {\n result.push(child);\n }\n });\n\n return result;\n }, [children]);\n\n // Mount/unmount logging\n useEffect(() => {\n if (__DEBUG__) {\n logEventRef.current({\n type: 'render',\n source: 'useReorderPresence.mount',\n message: 'useReorderPresence hook mounted',\n data: {\n childCount: currentKeys.size,\n autoAnimate,\n stagger,\n config: { enterDuration, exitDuration, flipDuration }\n }\n });\n }\n\n return () => {\n if (__DEBUG__) {\n logEventRef.current({\n type: 'component-unmounted',\n source: 'useReorderPresence.unmount',\n message: 'useReorderPresence hook unmounting',\n data: {\n exitingCount: exitingIdsRef.current.size,\n enteringCount: enteringIdsRef.current.size\n }\n });\n }\n };\n }, []);\n\n return {\n presentChildren,\n triggerExit,\n isExiting,\n isEntering,\n exitingIds: getExitingIds(),\n enteringIds: getEnteringIds(),\n reorder\n };\n}\n","import React, {\n useMemo,\n useState,\n useEffect,\n useRef,\n useCallback,\n useImperativeHandle,\n Children,\n isValidElement,\n cloneElement,\n forwardRef,\n type CSSProperties,\n type ReactNode,\n type ReactElement\n} from 'react';\nimport { useReorder } from './useReorder';\nimport { useDebug } from '../../contexts/DebugContext';\nimport type { ReorderProps, DurationConfig, ReorderLayout } from './types';\n\nfunction getDuration(config: DurationConfig | undefined, type: 'enter' | 'exit'): number | undefined {\n if (config === undefined) return undefined;\n if (typeof config === 'number') return config;\n return config[type];\n}\n\nconst layoutStyles: Record<ReorderLayout, CSSProperties> = {\n auto: { position: 'relative' },\n horizontal: { display: 'flex', flexDirection: 'row', flexWrap: 'wrap', alignItems: 'center', position: 'relative' },\n vertical: { display: 'flex', flexDirection: 'column', position: 'relative' },\n grid: { display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(100px, 1fr))', gap: '8px', position: 'relative' }\n};\n\ninterface ChildRecord {\n key: string;\n element: ReactElement;\n}\n\n/**\n * Agnostic container for reorderable items with FLIP animations.\n *\n * Architecture: CSS + WAAPI (no React state for animation tracking)\n * - CSS handles initial states ([data-reorder-state=\"entering\"] starts hidden)\n * - WAAPI handles all animations\n * - React only manages displayChildren (what's in DOM)\n *\n * @example\n * ```tsx\n * <Reorder\n * layout=\"horizontal\"\n * onItemExit={(id) => handleRemove(id)}\n * >\n * {items.map(item => (\n * <div key={item.id}>{item.name}</div>\n * ))}\n * </Reorder>\n * ```\n */\nconst ReorderRoot = forwardRef<{\n startItemExit: (id: string) => Promise<void>;\n startItemEnter: (id: string) => Promise<void>;\n isAnimating: (id?: string) => boolean;\n}, ReorderProps>(({\n children,\n autoAnimate: _autoAnimate = true,\n stagger,\n duration,\n layout = 'auto',\n className = '',\n flipBehavior,\n exitPositionStrategy,\n onItemExit,\n onItemEnter\n}, ref) => {\n void _autoAnimate;\n const { logEvent } = useDebug();\n const logEventRef = useRef(logEvent);\n useEffect(() => {\n logEventRef.current = logEvent;\n }, [logEvent]);\n\n // Config refs to avoid recreating callbacks\n const onItemExitRef = useRef(onItemExit);\n const onItemEnterRef = useRef(onItemEnter);\n useEffect(() => {\n onItemExitRef.current = onItemExit;\n onItemEnterRef.current = onItemEnter;\n }, [onItemExit, onItemEnter]);\n\n // Completion handler - ONLY removes from displayChildren\n // This is called by orchestrator when exit animation finishes\n const handleAnimationComplete = useCallback((id: string) => {\n if (__DEBUG__) {\n logEventRef.current({\n type: 'reorder-animation-complete',\n source: 'Reorder.handleAnimationComplete',\n message: `Exit complete, removing from DOM: ${id}`,\n data: { id }\n });\n }\n setDisplayChildren(prev => prev.filter(child => child.key !== id));\n }, []);\n\n // Low-level reorder hook\n const reorder = useReorder({\n enterDuration: getDuration(duration, 'enter'),\n exitDuration: getDuration(duration, 'exit'),\n flipBehavior,\n exitPositionStrategy,\n onComplete: handleAnimationComplete\n });\n\n // Expose reorder methods via ref\n useImperativeHandle(ref, () => ({\n startItemExit: reorder.startItemExit,\n startItemEnter: reorder.startItemEnter,\n isAnimating: reorder.isAnimating\n }), [reorder.startItemExit, reorder.startItemEnter, reorder.isAnimating]);\n\n // Store reorder functions in refs for stable callbacks\n const startExitRef = useRef(reorder.startItemExit);\n const startEnterRef = useRef(reorder.startItemEnter);\n useEffect(() => {\n startExitRef.current = reorder.startItemExit;\n startEnterRef.current = reorder.startItemEnter;\n }, [reorder.startItemExit, reorder.startItemEnter]);\n\n // Extract current children as records\n const currentChildren = useMemo<ChildRecord[]>(() => {\n const records: ChildRecord[] = [];\n Children.forEach(children, child => {\n if (isValidElement(child) && child.key != null) {\n records.push({\n key: String(child.key),\n element: child as ReactElement\n });\n }\n });\n return records;\n }, [children]);\n\n // Internal display state - ONLY state we track\n // This is the single source of truth for what's in the DOM\n const [displayChildren, setDisplayChildren] = useState<ChildRecord[]>(currentChildren);\n\n // Track which keys are currently exiting (to avoid re-triggering)\n // This is a ref, not state - no re-renders needed\n const exitingKeysRef = useRef<Set<string>>(new Set());\n\n // Calculate stagger delay\n const getStaggerDelay = useCallback((index: number, type: 'enter' | 'exit'): number => {\n if (!stagger) return 0;\n if (typeof stagger === 'number') return stagger * index;\n return (stagger[type] ?? 0) * index;\n }, [stagger]);\n\n // Detect changes and sync displayChildren\n useEffect(() => {\n const currentKeys = new Set(currentChildren.map(c => c.key));\n const displayKeys = new Set(displayChildren.map(c => c.key));\n\n // Removals: in display but not in current, and not already exiting\n const removed = displayChildren.filter(c =>\n !currentKeys.has(c.key) && !exitingKeysRef.current.has(c.key)\n );\n\n // Additions: in current but not in display\n const added = currentChildren.filter(c => !displayKeys.has(c.key));\n\n // No structural changes - check for content updates\n if (removed.length === 0 && added.length === 0) {\n const hasUpdates = currentChildren.some((c) => {\n const displayChild = displayChildren.find(dc => dc.key === c.key);\n return displayChild && displayChild.element !== c.element;\n });\n\n if (hasUpdates) {\n setDisplayChildren(prev => prev.map(dc => {\n if (exitingKeysRef.current.has(dc.key)) return dc;\n const currentChild = currentChildren.find(c => c.key === dc.key);\n return currentChild ?? dc;\n }));\n }\n return;\n }\n\n if (__DEBUG__) {\n logEventRef.current({\n type: 'token-reorder',\n source: 'Reorder.detectChanges',\n message: `Detected ${removed.length} removals, ${added.length} additions`,\n data: {\n removed: removed.map(c => c.key),\n added: added.map(c => c.key),\n currentCount: currentChildren.length,\n displayCount: displayChildren.length\n }\n });\n }\n\n // Handle removals - start exit animations\n // Don't remove from displayChildren yet - orchestrator.onComplete does that\n removed.forEach((child, index) => {\n const delay = getStaggerDelay(index, 'exit');\n exitingKeysRef.current.add(child.key);\n\n const processExit = () => {\n onItemExitRef.current?.(child.key);\n\n if (__DEBUG__) {\n logEventRef.current({\n type: 'reorder-item-exit',\n source: 'Reorder.processRemovals',\n message: `Exit triggered: ${child.key}`,\n data: { key: child.key }\n });\n }\n\n // Start exit animation - orchestrator sets data-reorder-state=\"exiting\"\n startExitRef.current(child.key).finally(() => {\n exitingKeysRef.current.delete(child.key);\n });\n };\n\n if (delay > 0) {\n setTimeout(processExit, delay);\n } else {\n processExit();\n }\n });\n\n // Handle additions - merge into display\n if (added.length > 0) {\n // Merge: current children + exiting children at their original positions\n const exitingChildren = displayChildren.filter(c => !currentKeys.has(c.key));\n let result = [...currentChildren];\n\n exitingChildren.forEach(ec => {\n const oldIdx = displayChildren.findIndex(c => c.key === ec.key);\n if (oldIdx !== -1 && oldIdx <= result.length) {\n result.splice(oldIdx, 0, ec);\n }\n });\n\n setDisplayChildren(result);\n\n // Enter animations are started in the ref callback when element mounts\n // This ensures the element exists in DOM before animation starts\n added.forEach((child, index) => {\n const delay = getStaggerDelay(index, 'enter');\n\n const processEnter = () => {\n onItemEnterRef.current?.(child.key);\n\n if (__DEBUG__) {\n logEventRef.current({\n type: 'reorder-item-enter',\n source: 'Reorder.processAdditions',\n message: `Enter scheduled: ${child.key}`,\n data: { key: child.key }\n });\n }\n };\n\n if (delay > 0) {\n setTimeout(processEnter, delay);\n } else {\n processEnter();\n }\n });\n }\n }, [currentChildren, displayChildren, getStaggerDelay]);\n\n // Build present children with data attributes and ref registration\n const presentChildren = useMemo<ReactNode[]>(() => {\n const currentKeys = new Set(currentChildren.map(c => c.key));\n\n return displayChildren.map(({ key, element }) => {\n // Determine if this is a new element (not yet animated in)\n const isNewElement = !currentKeys.has(key) ? false : true;\n // Check if currently exiting\n const isExiting = exitingKeysRef.current.has(key);\n\n const elementWithRef = cloneElement(element as ReactElement<any>, {\n ref: (el: HTMLElement | null) => {\n if (el) {\n // Register with reorder system\n reorder.registerElement(key, el);\n\n // Check current state from DOM\n const currentState = el.dataset.reorderState;\n\n // If no state yet and this is a new element, set entering and animate\n if (!currentState && isNewElement && !isExiting) {\n el.dataset.reorderState = 'entering';\n // CSS hides it, WAAPI will animate to visible\n startEnterRef.current(key);\n }\n } else {\n // Unregister on unmount\n reorder.registerElement(key, null);\n }\n\n // Preserve original ref\n const originalRef = (element as any).ref;\n if (typeof originalRef === 'function') {\n originalRef(el);\n } else if (originalRef && typeof originalRef === 'object') {\n (originalRef as React.MutableRefObject<HTMLElement | null>).current = el;\n }\n },\n 'data-reorder-id': key\n });\n\n return elementWithRef;\n });\n }, [displayChildren, currentChildren, reorder.registerElement]);\n\n const layoutClass = layout !== 'auto' ? `reorder--${layout}` : '';\n\n return (\n <div\n className={`waapi-reorder-container reorder ${layoutClass} ${className}`}\n style={layoutStyles[layout]}\n >\n {presentChildren}\n </div>\n );\n});\n\nReorderRoot.displayName = 'Reorder';\n\nexport { ReorderRoot as Reorder };\nexport type { ReorderProps } from './types';\n","import { useMemo } from 'react';\n\nexport type ListFormatType = 'conjunction' | 'disjunction' | 'unit';\nexport type ListFormatStyle = 'long' | 'short' | 'narrow';\n\n// Type declarations for Intl.ListFormat (ES2021+)\ninterface IntlListFormatPart {\n type: 'element' | 'literal';\n value: string;\n}\n\ninterface IntlListFormatOptions {\n type?: ListFormatType;\n style?: ListFormatStyle;\n}\n\ninterface IntlListFormat {\n format(list: string[]): string;\n formatToParts(list: string[]): IntlListFormatPart[];\n}\n\ninterface IntlListFormatConstructor {\n new (locale?: string | string[], options?: IntlListFormatOptions): IntlListFormat;\n}\n\ndeclare global {\n namespace Intl {\n const ListFormat: IntlListFormatConstructor;\n }\n}\n\nexport interface UseListFormatOptions {\n locale?: string;\n type?: ListFormatType;\n style?: ListFormatStyle;\n separator?: string;\n}\n\nexport interface ListPart {\n type: 'element' | 'literal';\n value: string;\n index: number;\n}\n\nconst getDefaultLocale = (): string => {\n if (typeof navigator !== 'undefined' && navigator.language) {\n return navigator.language;\n }\n return 'en';\n};\n\n/**\n * Hook for locale-aware list formatting using Intl.ListFormat\n *\n * Provides parts that can be rendered individually with animations.\n * Uses browser's Intl.ListFormat API for proper locale handling.\n *\n * @example\n * ```tsx\n * // Automatic locale detection\n * const parts = useListFormat(['Apple', 'Banana', 'Cherry']);\n * // en: [{ type: 'element', value: 'Apple' }, { type: 'literal', value: ', ' }, ...]\n *\n * // Spanish\n * const parts = useListFormat(['Apple', 'Banana', 'Cherry'], { locale: 'es' });\n * // es: [..., { type: 'literal', value: ' y ' }, { type: 'element', value: 'Cherry' }]\n *\n * // Disjunction (or)\n * const parts = useListFormat(['A', 'B', 'C'], { type: 'disjunction' });\n * // en: \"A, B, or C\"\n *\n * // Manual override\n * const parts = useListFormat(['A', 'B', 'C'], { separator: ' | ' });\n * // \"A | B | C\"\n * ```\n */\nexport function useListFormat(\n items: string[],\n options: UseListFormatOptions = {}\n): ListPart[] {\n const {\n locale,\n type = 'conjunction',\n style = 'long',\n separator\n } = options;\n\n return useMemo(() => {\n if (items.length === 0) return [];\n\n // Manual override mode: use fixed separator\n if (separator !== undefined) {\n const parts: ListPart[] = [];\n items.forEach((item, i) => {\n parts.push({ type: 'element', value: item, index: i });\n if (i < items.length - 1) {\n parts.push({ type: 'literal', value: separator, index: i });\n }\n });\n return parts;\n }\n\n // Use Intl.ListFormat for locale-aware formatting\n const resolvedLocale = locale ?? getDefaultLocale();\n\n try {\n const formatter = new Intl.ListFormat(resolvedLocale, { type, style });\n const formatted = formatter.formatToParts(items);\n\n let elementIndex = 0;\n return formatted.map((part): ListPart => {\n if (part.type === 'element') {\n return {\n type: 'element',\n value: part.value,\n index: elementIndex++\n };\n }\n // 'literal' type - separators\n return {\n type: 'literal',\n value: part.value,\n index: Math.max(0, elementIndex - 1)\n };\n });\n } catch {\n // Fallback if Intl.ListFormat fails (shouldn't happen in modern browsers)\n const parts: ListPart[] = [];\n items.forEach((item, i) => {\n parts.push({ type: 'element', value: item, index: i });\n if (i < items.length - 1) {\n parts.push({ type: 'literal', value: ', ', index: i });\n }\n });\n return parts;\n }\n }, [items, locale, type, style, separator]);\n}\n","import { useMemo, useRef, useEffect, useCallback } from 'react';\nimport { SlidingText } from './SlidingText';\nimport { SlidingNumber } from './SlidingNumber';\nimport { Reorder } from '../primitives/reorder';\nimport { useListFormat, type ListFormatType, type ListFormatStyle } from '../hooks/useListFormat';\nimport { PRESETS, ANIMATION_DEFAULTS, TIMING, TRANSFORMS, EASINGS } from '../utils/animationUtils';\nimport { useDebug } from '../contexts/DebugContext';\n\ninterface SeparatorState {\n animationPhase: 'idle' | 'exit-coordinated' | 'flip-coordinated' | 'completed';\n}\n\nexport interface Token {\n id: string;\n text: string;\n}\n\nexport interface AnimatedTokensV2Props {\n tokens: Token[];\n placeholder?: string;\n maxVisible?: number;\n textAnimationMode?: 'character' | 'word';\n textDirection?: 'vertical' | 'horizontal';\n textStaggerDelay?: number;\n\n // Locale-aware formatting (Intl.ListFormat)\n locale?: string;\n listType?: ListFormatType;\n listStyle?: ListFormatStyle;\n\n // Manual separator override (ignores locale if set)\n separator?: string;\n\n enableWidthAnimation?: boolean;\n className?: string;\n tokenClassName?: string;\n placeholderClassName?: string;\n separatorClassName?: string;\n}\n\n/**\n * AnimatedTokensV2 - Locale-aware animated token list component\n *\n * Built on the Reorder primitive, provides smooth FLIP animations for token lists\n * with proper locale-aware separators using Intl.ListFormat.\n *\n * ## Features\n *\n * - **Locale-aware separators**: Uses `Intl.ListFormat` for proper localization\n * - **Smooth FLIP animations**: Coordinated enter/exit transitions\n * - **Separator coordination**: Animates separators when tokens are removed\n * - **Overflow handling**: Shows \"+N more\" when tokens exceed `maxVisible`\n * - **Text animation**: Each token's text animates character-by-character\n *\n * ## Locale Support\n *\n * By default, uses the browser's locale. Supports:\n * - `conjunction`: \"A, B, and C\" (default)\n * - `disjunction`: \"A, B, or C\"\n * - `unit`: \"A B C\" (space-separated)\n *\n * ## Examples\n *\n * ### Basic usage\n * ```tsx\n * import { AnimatedTokensV2 } from '@mks2508/waapi-animation-primitives';\n *\n * const tags = [\n * { id: '1', text: 'React' },\n * { id: '2', text: 'TypeScript' },\n * { id: '3', text: 'WAAPI' }\n * ];\n *\n * <AnimatedTokensV2 tokens={tags} />\n * ```\n *\n * ### Locale-aware separators\n * ```tsx\n * // en-US: \"React, TypeScript, and WAAPI\"\n * // es-ES: \"React, TypeScript y WAAPI\"\n * <AnimatedTokensV2 tokens={tags} locale=\"es\" listStyle=\"long\" />\n * ```\n *\n * ### Disjunction (or)\n * ```tsx\n * // \"React, TypeScript, or WAAPI\"\n * <AnimatedTokensV2 tokens={tags} listType=\"disjunction\" />\n * ```\n *\n * ### Manual separator override\n * ```tsx\n * // \"React | TypeScript | WAAPI\"\n * <AnimatedTokensV2 tokens={tags} separator=\" | \" />\n * ```\n *\n * ### With overflow\n * ```tsx\n * const tags = [\n * { id: '1', text: 'React' },\n * { id: '2', text: 'Vue' },\n * { id: '3', text: 'Svelte' },\n * { id: '4', text: 'Angular' },\n * { id: '5', text: 'Solid' }\n * ];\n *\n * // Shows: \"React, Vue, Svelte + 2 more\"\n * <AnimatedTokensV2 tokens={tags} maxVisible={3} />\n * ```\n *\n * @example\n * ```tsx\n * import { AnimatedTokensV2 } from '@mks2508/waapi-animation-primitives';\n *\n * function TagList({ tags }) {\n * return (\n * <AnimatedTokensV2\n * tokens={tags}\n * locale=\"en-US\"\n * listType=\"conjunction\"\n * maxVisible={5}\n * textAnimationMode=\"character\"\n * textDirection=\"vertical\"\n * enableWidthAnimation={true}\n * tokenClassName=\"text-lg font-medium\"\n * />\n * );\n * }\n * ```\n *\n * @alias TextFlow\n */\nexport const AnimatedTokensV2: React.FC<AnimatedTokensV2Props> = ({\n tokens,\n placeholder = 'No tokens',\n maxVisible,\n textAnimationMode = 'character',\n textDirection = 'vertical',\n textStaggerDelay = 15,\n locale,\n listType = 'conjunction',\n listStyle = 'long',\n separator,\n enableWidthAnimation = false,\n className = '',\n tokenClassName = '',\n placeholderClassName = '',\n separatorClassName = '',\n}) => {\n const { logEvent } = useDebug();\n const logEventRef = useRef(logEvent);\n useEffect(() => {\n logEventRef.current = logEvent;\n }, [logEvent]);\n\n // Track exiting token IDs with REF (not state) - prevents unnecessary re-renders\n const exitingIdsRef = useRef<Set<string>>(new Set());\n\n // Track separator states for coordination\n const separatorStatesRef = useRef<Map<string, SeparatorState>>(new Map());\n const separatorRefs = useRef<Map<string, HTMLSpanElement>>(new Map());\n\n // Visible tokens\n const visibleTokens = useMemo(\n () => (maxVisible ? tokens.slice(0, maxVisible) : tokens),\n [tokens, maxVisible]\n );\n\n const overflowCount = useMemo(\n () => (maxVisible ? Math.max(0, tokens.length - maxVisible) : 0),\n [tokens.length, maxVisible]\n );\n\n // Use locale-aware list formatting\n const listParts = useListFormat(\n visibleTokens.map(t => t.text),\n { locale, type: listType, style: listStyle, separator }\n );\n\n // Track overflow state changes\n const prevOverflowRef = useRef(0);\n const isOverflowEntering = overflowCount > 0 && prevOverflowRef.current === 0;\n const isOverflowExiting = overflowCount === 0 && prevOverflowRef.current > 0;\n\n useEffect(() => {\n prevOverflowRef.current = overflowCount;\n }, [overflowCount]);\n\n // Separator visibility logic - same as monolith\n const shouldShowSeparator = useCallback((tokenId: string, tokenIndex: number): boolean => {\n const isLastToken = tokenIndex >= visibleTokens.length - 1;\n if (isLastToken) return false;\n\n const separatorState = separatorStatesRef.current.get(tokenId);\n\n // Show separator if:\n // 1. No separator state (default to visible)\n // 2. Separator is idle or animating but not completed\n if (!separatorState) return true;\n\n switch (separatorState.animationPhase) {\n case 'idle':\n case 'exit-coordinated':\n case 'flip-coordinated':\n return true; // Still visible during coordinated animation\n case 'completed':\n return false; // Animation finished, hide separator\n default:\n return true;\n }\n }, [visibleTokens.length]);\n\n // Clean up separator states for tokens that no longer exist\n useEffect(() => {\n const visibleTokenIds = new Set(visibleTokens.map(t => t.id));\n const currentStates = separatorStatesRef.current;\n\n // Remove states for tokens that are no longer visible\n for (const tokenId of currentStates.keys()) {\n if (!visibleTokenIds.has(tokenId)) {\n separatorStatesRef.current.delete(tokenId);\n }\n }\n\n // Initialize separator states for new visible tokens that need separators\n visibleTokens.forEach((token, index) => {\n const needsSeparator = index < visibleTokens.length - 1;\n if (needsSeparator && !separatorStatesRef.current.has(token.id)) {\n separatorStatesRef.current.set(token.id, { animationPhase: 'idle' });\n }\n });\n }, [visibleTokens]);\n\n // Animate separator exit with state tracking\n const animateSeparatorExit = useCallback((tokenId: string) => {\n const separatorEl = separatorRefs.current.get(tokenId);\n if (!separatorEl) return;\n\n // Update separator state\n separatorStatesRef.current.set(tokenId, { animationPhase: 'exit-coordinated' });\n separatorEl.dataset.separatorState = 'exiting';\n\n const animation = separatorEl.animate([\n { opacity: 1, transform: 'translateY(0) scale(1)' },\n { opacity: 0, transform: `translateY(${TRANSFORMS.OFFSET_Y_EXIT}px) scale(${TRANSFORMS.SCALE_EXIT})` }\n ], {\n duration: TIMING.EXIT_DURATION,\n easing: EASINGS.EASE_IN_CUBIC,\n fill: 'forwards'\n });\n\n animation.finished.then(() => {\n separatorStatesRef.current.set(tokenId, { animationPhase: 'completed' });\n separatorEl.dataset.separatorState = 'completed';\n }).catch(() => {\n separatorStatesRef.current.delete(tokenId);\n });\n }, []);\n\n const handleItemExit = useCallback((id: string) => {\n exitingIdsRef.current.add(id);\n\n // Find additional separators that need animation (like monolith)\n const additionalSeparators: string[] = [];\n const tokenIndex = visibleTokens.findIndex(t => t.id === id);\n\n // If removing last visible token, the previous token loses its separator\n if (tokenIndex === visibleTokens.length - 1 && tokenIndex > 0) {\n const previousToken = visibleTokens[tokenIndex - 1];\n if (previousToken) {\n additionalSeparators.push(previousToken.id);\n }\n }\n\n // Animate this token's separator and any additional ones\n animateSeparatorExit(id);\n additionalSeparators.forEach(sepId => animateSeparatorExit(sepId));\n\n logEventRef.current({\n type: 'token-remove',\n source: 'AnimatedTokensV2',\n message: `Token exiting: ${id}`,\n data: { id, additionalSeparators }\n });\n }, [visibleTokens, animateSeparatorExit]);\n\n const handleItemEnter = useCallback((id: string) => {\n logEventRef.current({\n type: 'token-add',\n source: 'AnimatedTokensV2',\n message: `Token entering: ${id}`,\n data: { id }\n });\n }, []);\n\n const showPlaceholder = tokens.length === 0 && !!placeholder;\n\n // Build children from list parts (elements + separators)\n const tokenElements = useMemo(() => {\n return listParts.map((part, partIndex) => {\n if (part.type === 'element') {\n const token = visibleTokens[part.index];\n if (!token) return null;\n\n const isExiting = exitingIdsRef.current.has(token.id);\n\n return (\n <span\n key={token.id}\n className={`waapi-token-wrapper ${tokenClassName}`}\n data-reorder-id={token.id}\n >\n <SlidingText\n text={token.text}\n mode={isExiting ? 'none' : textAnimationMode}\n direction={textDirection}\n staggerDelay={textStaggerDelay}\n duration={ANIMATION_DEFAULTS.DURATION_ENTER}\n blur={PRESETS.newToken.blur}\n widthAnimation={!isExiting && enableWidthAnimation}\n initial={isExiting ? false : 'initial'}\n animate=\"animate\"\n />\n </span>\n );\n }\n\n // Separator (literal) - apply shouldShowSeparator logic\n const prevToken = visibleTokens[part.index];\n if (!prevToken) return null;\n\n const tokenIndex = visibleTokens.findIndex(t => t.id === prevToken.id);\n const showSeparator = shouldShowSeparator(prevToken.id, tokenIndex);\n\n if (!showSeparator) return null;\n\n return (\n <span\n key={`sep-${prevToken.id}-${partIndex}`}\n ref={el => {\n if (el) separatorRefs.current.set(prevToken.id, el);\n else separatorRefs.current.delete(prevToken.id);\n }}\n className={`waapi-token-separator ${separatorClassName}`}\n data-separator-state={separatorStatesRef.current.get(prevToken.id)?.animationPhase ?? 'idle'}\n >\n {part.value}\n </span>\n );\n }).filter(Boolean);\n }, [listParts, visibleTokens, tokenClassName, textAnimationMode, textDirection, textStaggerDelay, enableWidthAnimation, separatorClassName, shouldShowSeparator]);\n\n // Get last separator for overflow (use formatted separator or fallback)\n const lastSeparator = useMemo(() => {\n if (separator !== undefined) return separator;\n // Find the last literal in parts for proper locale formatting\n const lastLiteral = [...listParts].reverse().find(p => p.type === 'literal');\n return lastLiteral?.value ?? ', ';\n }, [listParts, separator]);\n\n // Overflow element - now inside Reorder for FLIP coordination\n const overflowElement = useMemo(() => {\n if (overflowCount === 0 && !isOverflowExiting) return null;\n\n return (\n <div\n key=\"overflow-counter\"\n data-reorder-id=\"overflow-counter\"\n className={`waapi-token-overflow ${tokenClassName} ${\n isOverflowEntering ? 'entering' : ''\n } ${isOverflowExiting ? 'exiting' : ''}`}\n >\n {visibleTokens.length > 0 && (\n <span className={`waapi-token-separator ${separatorClassName}`}>\n {lastSeparator}\n </span>\n )}\n <span className={`waapi-token-separator ${separatorClassName}`}>+</span>\n <SlidingNumber\n value={overflowCount}\n duration={ANIMATION_DEFAULTS.DURATION_ENTER}\n fontSize=\"inherit\"\n fontWeight=\"inherit\"\n color=\"inherit\"\n />\n <SlidingText\n text=\" more\"\n mode={textAnimationMode}\n direction={textDirection}\n staggerDelay={textStaggerDelay}\n duration={\n isOverflowExiting\n ? ANIMATION_DEFAULTS.DURATION_EXIT\n : ANIMATION_DEFAULTS.DURATION_ENTER\n }\n blur={PRESETS.newToken.blur}\n initial={isOverflowEntering ? 'initial' : false}\n animate=\"animate\"\n />\n </div>\n );\n }, [overflowCount, isOverflowEntering, isOverflowExiting, tokenClassName, visibleTokens.length, lastSeparator, separatorClassName, textAnimationMode, textDirection, textStaggerDelay]);\n\n return (\n <div role=\"text\" className={`waapi-animated-tokens-container waapi-v2 ${className}`}>\n {showPlaceholder && (\n <SlidingText\n key=\"placeholder\"\n text={placeholder}\n mode={PRESETS.placeholder.mode}\n direction={PRESETS.placeholder.direction}\n blur={PRESETS.placeholder.blur}\n duration={PRESETS.placeholder.duration}\n initial=\"initial\"\n animate=\"animate\"\n className={`waapi-token-placeholder ${placeholderClassName}`}\n />\n )}\n\n <Reorder\n layout=\"horizontal\"\n onItemExit={handleItemExit}\n onItemEnter={handleItemEnter}\n >\n {tokenElements}\n {overflowElement}\n </Reorder>\n </div>\n );\n};\n","import { createContext, useContext } from 'react';\nimport type { MorphContextValue } from './types';\n\n/**\n * Context for sharing Morph state between components\n */\nexport const MorphContext = createContext<MorphContextValue | null>(null);\n\n/**\n * Hook to access Morph context\n * Must be used within a Morph component\n *\n * @throws Error if used outside Morph component\n */\nexport function useMorphContext(): MorphContextValue {\n const context = useContext(MorphContext);\n\n if (!context) {\n throw new Error(\n 'useMorphContext must be used within a <Morph> component. ' +\n 'Wrap your morphable components with <Morph>.'\n );\n }\n\n return context;\n}\n","import { useRef, useCallback, useEffect, useState } from 'react';\nimport { TIMING, EASINGS } from '../../../utils/animationUtils';\nimport type { FLIPClipPathAPI, FLIPClipPathOptions } from '../types';\n\nconst DEFAULT_DURATION = TIMING.FLIP_DURATION;\nconst DEFAULT_EASING = EASINGS.EASE_OUT_CUBIC;\n\n/**\n * Hook for FLIP + clip-path morphing technique\n *\n * This technique combines:\n * 1. FLIP for position/size transitions\n * 2. clip-path for shape morphing\n * 3. Opacity crossfade for smooth visual transition\n *\n * @example\n * ```tsx\n * const { isMorphing, morph, cancel } = useFLIPClipPath({ duration: 300 });\n *\n * const handleMorph = async () => {\n * await morph(fromRef.current, toRef.current);\n * };\n * ```\n */\nexport function useFLIPClipPath(options?: FLIPClipPathOptions): FLIPClipPathAPI {\n const [isMorphing, setIsMorphing] = useState(false);\n const activeAnimationsRef = useRef<Animation[]>([]);\n const optionsRef = useRef(options);\n\n useEffect(() => {\n optionsRef.current = options;\n }, [options]);\n\n const cancel = useCallback(() => {\n activeAnimationsRef.current.forEach(anim => anim.cancel());\n activeAnimationsRef.current = [];\n setIsMorphing(false);\n }, []);\n\n const morph = useCallback(async (\n fromElement: HTMLElement,\n toElement: HTMLElement\n ): Promise<void> => {\n if (isMorphing) {\n cancel();\n }\n\n setIsMorphing(true);\n\n const duration = optionsRef.current?.duration ?? DEFAULT_DURATION;\n const easing = optionsRef.current?.easing ?? DEFAULT_EASING;\n\n const fromRect = fromElement.getBoundingClientRect();\n const toRect = toElement.getBoundingClientRect();\n\n const deltaX = fromRect.left - toRect.left;\n const deltaY = fromRect.top - toRect.top;\n const scaleX = fromRect.width / toRect.width;\n const scaleY = fromRect.height / toRect.height;\n\n const fromBorderRadius = getComputedStyle(fromElement).borderRadius;\n const toBorderRadius = getComputedStyle(toElement).borderRadius;\n\n const fromClipPath = optionsRef.current?.clipPathStart ||\n borderRadiusToInset(fromBorderRadius, fromRect);\n const toClipPath = optionsRef.current?.clipPathEnd ||\n borderRadiusToInset(toBorderRadius, toRect);\n\n const originalFromStyles = {\n position: fromElement.style.position,\n opacity: fromElement.style.opacity,\n pointerEvents: fromElement.style.pointerEvents\n };\n\n const originalToStyles = {\n position: toElement.style.position,\n opacity: toElement.style.opacity,\n transform: toElement.style.transform,\n clipPath: toElement.style.clipPath\n };\n\n fromElement.style.position = 'absolute';\n fromElement.style.pointerEvents = 'none';\n\n toElement.style.position = 'relative';\n toElement.style.transform = `translate(${deltaX}px, ${deltaY}px) scale(${scaleX}, ${scaleY})`;\n toElement.style.clipPath = fromClipPath;\n toElement.style.opacity = '0';\n\n const fromFadeOut = fromElement.animate([\n { opacity: 1 },\n { opacity: 0 }\n ], {\n duration: duration * 0.5,\n easing,\n fill: 'forwards'\n });\n\n const toFadeIn = toElement.animate([\n { opacity: 0 },\n { opacity: 1 }\n ], {\n duration: duration * 0.5,\n delay: duration * 0.25,\n easing,\n fill: 'forwards'\n });\n\n const toTransform = toElement.animate([\n {\n transform: `translate(${deltaX}px, ${deltaY}px) scale(${scaleX}, ${scaleY})`,\n clipPath: fromClipPath\n },\n {\n transform: 'translate(0, 0) scale(1, 1)',\n clipPath: toClipPath\n }\n ], {\n duration,\n easing,\n fill: 'forwards'\n });\n\n activeAnimationsRef.current = [fromFadeOut, toFadeIn, toTransform];\n\n try {\n await Promise.all([\n fromFadeOut.finished,\n toFadeIn.finished,\n toTransform.finished\n ]);\n } catch {\n Object.assign(fromElement.style, originalFromStyles);\n Object.assign(toElement.style, originalToStyles);\n setIsMorphing(false);\n return;\n }\n\n Object.assign(fromElement.style, originalFromStyles);\n toElement.style.transform = '';\n toElement.style.clipPath = '';\n toElement.style.opacity = '1';\n\n activeAnimationsRef.current = [];\n setIsMorphing(false);\n }, [isMorphing, cancel]);\n\n useEffect(() => {\n return () => {\n cancel();\n };\n }, [cancel]);\n\n return {\n isMorphing,\n morph,\n cancel\n };\n}\n\nfunction borderRadiusToInset(borderRadius: string, rect: DOMRect): string {\n const radius = parseFloat(borderRadius) || 0;\n const percentX = (radius / rect.width) * 100;\n const percentY = (radius / rect.height) * 100;\n return `inset(0 round ${percentX}% ${percentY}%)`;\n}\n","import { useRef, useCallback, useState } from 'react';\nimport { TIMING, EASINGS } from '../../../utils/animationUtils';\nimport type { CSSGridMorphAPI, CSSGridMorphOptions } from '../types';\n\nconst DEFAULT_DURATION = TIMING.FLIP_DURATION;\nconst DEFAULT_EASING = EASINGS.MATERIAL_STANDARD;\n\n/**\n * Hook for CSS Grid-based expand/collapse animations\n *\n * Uses the grid-template-rows: 0fr/1fr technique for smooth height animations\n * without needing to know the content height in advance.\n *\n * CSS required on container:\n * ```css\n * .morph-container {\n * display: grid;\n * grid-template-rows: 0fr;\n * transition: grid-template-rows 300ms ease;\n * }\n * .morph-container.expanded {\n * grid-template-rows: 1fr;\n * }\n * .morph-content {\n * overflow: hidden;\n * }\n * ```\n *\n * @example\n * ```tsx\n * const { isExpanded, toggle, containerRef } = useCSSGridMorph();\n *\n * return (\n * <div ref={containerRef} className={isExpanded ? 'expanded' : ''}>\n * <div className=\"morph-content\">\n * Collapsible content\n * </div>\n * </div>\n * );\n * ```\n */\nexport function useCSSGridMorph(options?: CSSGridMorphOptions): CSSGridMorphAPI {\n const [isExpanded, setIsExpanded] = useState(false);\n const containerRef = useRef<HTMLElement | null>(null);\n const optionsRef = useRef(options);\n\n const duration = optionsRef.current?.duration ?? DEFAULT_DURATION;\n const easing = optionsRef.current?.easing ?? DEFAULT_EASING;\n\n const applyTransition = useCallback(() => {\n if (containerRef.current) {\n containerRef.current.style.transition = `grid-template-rows ${duration}ms ${easing}`;\n }\n }, [duration, easing]);\n\n const expand = useCallback(() => {\n applyTransition();\n if (containerRef.current) {\n containerRef.current.style.gridTemplateRows = '1fr';\n }\n setIsExpanded(true);\n }, [applyTransition]);\n\n const collapse = useCallback(() => {\n applyTransition();\n if (containerRef.current) {\n containerRef.current.style.gridTemplateRows = '0fr';\n }\n setIsExpanded(false);\n }, [applyTransition]);\n\n const toggle = useCallback(() => {\n if (isExpanded) {\n collapse();\n } else {\n expand();\n }\n }, [isExpanded, expand, collapse]);\n\n return {\n isExpanded,\n expand,\n collapse,\n toggle,\n containerRef\n };\n}\n","import { useRef, useCallback, useMemo } from 'react';\nimport type { ViewTransitionsAPI, ViewTransitionsOptions } from '../types';\n\n/**\n * Hook for View Transitions API\n *\n * Provides a wrapper around the native View Transitions API\n * with automatic feature detection and fallback handling.\n *\n * Browser support (as of 2025):\n * - Chrome 111+: ✅\n * - Firefox 144+: ✅\n * - Safari latest: ✅\n *\n * @example\n * ```tsx\n * const { isSupported, startTransition } = useViewTransitions({ name: 'hero' });\n *\n * const handleTransition = async () => {\n * await startTransition(() => {\n * setActiveImage(nextImage);\n * });\n * };\n * ```\n */\nexport function useViewTransitions(options?: ViewTransitionsOptions): ViewTransitionsAPI {\n const optionsRef = useRef(options);\n const typesRef = useRef<string[]>(options?.types || []);\n\n const isSupported = useMemo(() => {\n if (typeof document === 'undefined') return false;\n return 'startViewTransition' in document;\n }, []);\n\n const setTypes = useCallback((types: string[]) => {\n typesRef.current = types;\n }, []);\n\n const startTransition = useCallback(async (\n callback: () => void | Promise<void>\n ): Promise<void> => {\n if (!document.startViewTransition) {\n await callback();\n return;\n }\n\n const viewTransitionName = optionsRef.current?.name;\n\n const setupStyles = () => {\n if (viewTransitionName) {\n const styleEl = document.createElement('style');\n styleEl.id = `view-transition-${viewTransitionName}`;\n styleEl.textContent = `\n ::view-transition-old(${viewTransitionName}),\n ::view-transition-new(${viewTransitionName}) {\n animation-duration: 300ms;\n animation-timing-function: ease-out;\n }\n `;\n document.head.appendChild(styleEl);\n return () => {\n styleEl.remove();\n };\n }\n return () => {};\n };\n\n const cleanup = setupStyles();\n\n try {\n const transition = document.startViewTransition(callback);\n await transition.finished;\n } finally {\n cleanup();\n }\n }, []);\n\n return {\n isSupported,\n startTransition,\n setTypes\n };\n}\n","import { useCallback, useRef, useEffect } from 'react';\nimport { useFLIPClipPath } from './techniques/useFLIPClipPath';\nimport { useCSSGridMorph } from './techniques/useCSSGridMorph';\nimport { useViewTransitions } from './techniques/useViewTransitions';\nimport type { UseMorphReturn, UseMorphOptions, MorphTechnique } from './types';\n\n/**\n * Unified hook for morphing animations\n *\n * Provides access to multiple morphing techniques:\n * - FLIP + clip-path (default): Best for element-to-element transitions\n * - CSS Grid: Best for expand/collapse animations\n * - View Transitions: Best for page/route transitions (requires browser support)\n *\n * @example\n * ```tsx\n * const morph = useMorph({ technique: 'flip-clip-path' });\n *\n * // Using unified API:\n * await morph.morph(fromElement, toElement);\n *\n * // Or using individual techniques:\n * morph.cssGrid.toggle();\n * ```\n */\nexport function useMorph(options?: UseMorphOptions): UseMorphReturn {\n const technique: MorphTechnique = options?.technique ?? 'flip-clip-path';\n\n const optionsRef = useRef(options);\n useEffect(() => {\n optionsRef.current = options;\n }, [options]);\n\n const flipClipPath = useFLIPClipPath({\n duration: options?.duration,\n easing: options?.easing\n });\n\n const cssGrid = useCSSGridMorph({\n duration: options?.duration,\n easing: options?.easing\n });\n\n const viewTransitions = useViewTransitions();\n\n const isMorphing = flipClipPath.isMorphing;\n\n const morph = useCallback(async (\n fromElement: HTMLElement,\n toElement: HTMLElement\n ): Promise<void> => {\n optionsRef.current?.onMorphStart?.();\n\n const currentTechnique = optionsRef.current?.technique ?? 'flip-clip-path';\n\n switch (currentTechnique) {\n case 'flip-clip-path':\n await flipClipPath.morph(fromElement, toElement);\n break;\n\n case 'view-transitions':\n if (viewTransitions.isSupported) {\n await viewTransitions.startTransition(() => {\n fromElement.style.opacity = '0';\n toElement.style.opacity = '1';\n });\n } else {\n await flipClipPath.morph(fromElement, toElement);\n }\n break;\n\n case 'css-grid':\n cssGrid.toggle();\n break;\n }\n\n optionsRef.current?.onMorphEnd?.();\n }, [flipClipPath, viewTransitions, cssGrid]);\n\n const cancel = useCallback(() => {\n flipClipPath.cancel();\n }, [flipClipPath]);\n\n return {\n isMorphing,\n technique,\n isViewTransitionsSupported: viewTransitions.isSupported,\n morph,\n cancel,\n flipClipPath,\n cssGrid,\n viewTransitions\n };\n}\n","import { useMemo, type ReactNode } from 'react';\nimport { MorphContext } from './MorphContext';\nimport { useMorph } from './useMorph';\nimport type { MorphProps, MorphContextValue } from './types';\n\n/**\n * Container component for morphable elements\n *\n * Provides morphing capabilities to child components through context.\n *\n * @example\n * ```tsx\n * <Morph technique=\"flip-clip-path\" duration={300}>\n * <MorphSource ref={fromRef}>From Content</MorphSource>\n * <MorphTarget ref={toRef}>To Content</MorphTarget>\n * <button onClick={() => morph(fromRef.current, toRef.current)}>\n * Morph\n * </button>\n * </Morph>\n * ```\n */\nexport function Morph({\n children,\n technique = 'flip-clip-path',\n duration,\n easing,\n className = '',\n onMorphStart,\n onMorphEnd\n}: MorphProps): ReactNode {\n const morph = useMorph({\n technique,\n duration,\n easing,\n onMorphStart,\n onMorphEnd\n });\n\n const contextValue = useMemo<MorphContextValue>(() => ({\n morph\n }), [morph]);\n\n return (\n <MorphContext.Provider value={contextValue}>\n <div className={`morph-container ${className}`}>\n {children}\n </div>\n </MorphContext.Provider>\n );\n}\n\nexport type { MorphProps } from './types';\n","import type React from 'react';\nimport {\n slidingTextBaseStyles,\n CSS_VAR_NAMES,\n cssVar,\n getResponsiveTokenStyles,\n} from './cssTokens';\n\n// Re-export base styles for consumers\nexport const slidingTextStyles = slidingTextBaseStyles;\n\n// Helper para obtener estilos de token según estado y dirección\nexport const getSlidingTextTokenStyle = (\n state: 'enter-from' | 'enter-to' | 'exit-active',\n direction: 'vertical' | 'horizontal'\n): React.CSSProperties => {\n const base = { ...slidingTextBaseStyles.token };\n\n if (state === 'enter-from') {\n Object.assign(base, slidingTextBaseStyles.enterFrom);\n if (direction === 'vertical') {\n Object.assign(base, slidingTextBaseStyles.verticalEnterFrom);\n } else {\n Object.assign(base, slidingTextBaseStyles.horizontalEnterFrom);\n }\n } else if (state === 'enter-to') {\n Object.assign(base, slidingTextBaseStyles.enterTo);\n } else if (state === 'exit-active') {\n Object.assign(base, slidingTextBaseStyles.exitActive);\n if (direction === 'vertical') {\n Object.assign(base, slidingTextBaseStyles.verticalExitActive);\n } else {\n Object.assign(base, slidingTextBaseStyles.horizontalExitActive);\n }\n }\n\n return base;\n};\n\n// Helper para obtener estilos de container con responsive\nexport const getResponsiveSlidingTextStyle = (): React.CSSProperties => {\n const responsiveVars = getResponsiveTokenStyles();\n return {\n ...slidingTextBaseStyles.container,\n ...responsiveVars,\n };\n};\n\n// Export CSS variable names para uso avanzado\nexport { CSS_VAR_NAMES, cssVar };\n","import type React from 'react';\nimport {\n animatedTokensBaseStyles,\n CSS_VAR_NAMES,\n cssVar,\n getResponsiveTokenStyles,\n} from './cssTokens';\n\n// Re-export base styles for consumers\nexport const animatedTokensStyles = animatedTokensBaseStyles;\n\n// Helper para obtener estilos responsivos del container\nexport const getResponsiveAnimatedTokensStyle = (): React.CSSProperties => {\n const responsiveVars = getResponsiveTokenStyles();\n return {\n ...animatedTokensBaseStyles.container,\n ...responsiveVars,\n };\n};\n\n// Export CSS variable names para uso avanzado\nexport { CSS_VAR_NAMES, cssVar };\n"],"mappings":"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"}