UNPKG

aura-glass

Version:

A comprehensive glassmorphism design system for React applications with 142+ production-ready components

256 lines (253 loc) 7.98 kB
import { useState, useRef, useCallback, useEffect } from 'react'; /** * Animation Orchestration Hook * Provides synchronized multi-element animation control with sequencing and choreography */ /** * Public animation stage states */ var PublicAnimationStage; (function (PublicAnimationStage) { PublicAnimationStage["IDLE"] = "idle"; PublicAnimationStage["PREPARING"] = "preparing"; PublicAnimationStage["ENTERING"] = "entering"; PublicAnimationStage["ACTIVE"] = "active"; PublicAnimationStage["EXITING"] = "exiting"; PublicAnimationStage["COMPLETED"] = "completed"; PublicAnimationStage["PAUSED"] = "paused"; PublicAnimationStage["ERROR"] = "error"; })(PublicAnimationStage || (PublicAnimationStage = {})); /** * Hook for orchestrating complex animations */ const useOrchestration = sequence => { const { elements = [], mode = 'sequential', staggerDelay = 100, loop = false, onSequenceStart, onSequenceComplete, onStageChange } = sequence; const [stage, setStage] = useState(PublicAnimationStage.IDLE); const [progress, setProgress] = useState(0); const [currentElementId, setCurrentElementId] = useState(null); const stateRef = useRef({ stage: PublicAnimationStage.IDLE, currentElementIndex: 0, iteration: 0, startTime: 0, pausedTime: 0, elapsedTime: 0 }); const timersRef = useRef([]); const activeAnimationsRef = useRef(new Set()); /** * Update stage */ const updateStage = useCallback(newStage => { setStage(newStage); stateRef.current.stage = newStage; onStageChange?.(newStage); }, [onStageChange]); /** * Clear all timers */ const clearAllTimers = useCallback(() => { timersRef.current.forEach(clearTimeout); timersRef.current = []; }, []); /** * Animate single element */ const animateElement = useCallback((element, delay = 0) => { return new Promise(resolve => { const timer = setTimeout(() => { activeAnimationsRef.current.add(element.id); setCurrentElementId(element.id); element.onStart?.(); // Simulate animation duration const animationTimer = setTimeout(() => { element.onComplete?.(); activeAnimationsRef.current.delete(element.id); resolve(); }, element.duration || 300); timersRef.current.push(animationTimer); }, delay); timersRef.current.push(timer); }); }, []); /** * Execute sequence in parallel mode */ const executeParallel = useCallback(async () => { updateStage(PublicAnimationStage.ENTERING); const promises = elements.map(element => animateElement(element, element.delay || 0)); await Promise.all(promises); updateStage(PublicAnimationStage.COMPLETED); }, [elements, animateElement, updateStage]); /** * Execute sequence in sequential mode */ const executeSequential = useCallback(async () => { updateStage(PublicAnimationStage.ENTERING); for (let i = 0; i < elements.length; i++) { stateRef.current.currentElementIndex = i; await animateElement(elements[i], elements[i].delay || 0); setProgress((i + 1) / elements.length * 100); } updateStage(PublicAnimationStage.COMPLETED); }, [elements, animateElement, updateStage]); /** * Execute sequence in stagger mode */ const executeStagger = useCallback(async () => { updateStage(PublicAnimationStage.ENTERING); const promises = elements.map((element, index) => animateElement(element, (element.delay || 0) + index * staggerDelay)); await Promise.all(promises); updateStage(PublicAnimationStage.COMPLETED); }, [elements, staggerDelay, animateElement, updateStage]); /** * Start the orchestration */ const start = useCallback(async () => { if (stateRef.current.stage !== PublicAnimationStage.IDLE) { return; } clearAllTimers(); updateStage(PublicAnimationStage.PREPARING); stateRef.current.startTime = Date.now(); stateRef.current.iteration = 0; onSequenceStart?.(); const execute = async () => { updateStage(PublicAnimationStage.ACTIVE); switch (mode) { case 'parallel': await executeParallel(); break; case 'sequential': await executeSequential(); break; case 'stagger': await executeStagger(); break; } // Handle looping if (loop) { stateRef.current.iteration += 1; if (typeof loop === 'number' && stateRef.current.iteration >= loop) { updateStage(PublicAnimationStage.COMPLETED); onSequenceComplete?.(); } else { // Restart sequence stateRef.current.currentElementIndex = 0; setProgress(0); await execute(); } } else { onSequenceComplete?.(); } }; await execute(); }, [loop, mode, clearAllTimers, updateStage, executeParallel, executeSequential, executeStagger, onSequenceStart, onSequenceComplete]); /** * Pause the orchestration */ const pause = useCallback(() => { if (stateRef.current.stage === PublicAnimationStage.ACTIVE) { clearAllTimers(); stateRef.current.pausedTime = Date.now(); updateStage(PublicAnimationStage.PAUSED); } }, [clearAllTimers, updateStage]); /** * Resume the orchestration */ const resume = useCallback(() => { if (stateRef.current.stage === PublicAnimationStage.PAUSED) { const pausedDuration = Date.now() - stateRef.current.pausedTime; stateRef.current.startTime += pausedDuration; updateStage(PublicAnimationStage.ACTIVE); // Would need to re-execute remaining animations } }, [updateStage]); /** * Stop the orchestration */ const stop = useCallback(() => { clearAllTimers(); activeAnimationsRef.current.clear(); setProgress(0); setCurrentElementId(null); stateRef.current.currentElementIndex = 0; stateRef.current.iteration = 0; updateStage(PublicAnimationStage.IDLE); }, [clearAllTimers, updateStage]); /** * Reset the orchestration */ const reset = useCallback(() => { stop(); }, [stop]); /** * Seek to specific element */ const seekToElement = useCallback(elementId => { const index = elements.findIndex(el => el.id === elementId); if (index !== -1) { stateRef.current.currentElementIndex = index; setCurrentElementId(elementId); setProgress(index / elements.length * 100); } }, [elements]); /** * Get current state */ const getState = useCallback(() => { return { stage: stateRef.current.stage, currentElementIndex: stateRef.current.currentElementIndex, currentElementId, iteration: stateRef.current.iteration, progress, activeAnimations: Array.from(activeAnimationsRef.current), elapsedTime: Date.now() - stateRef.current.startTime }; }, [currentElementId, progress]); // Cleanup on unmount useEffect(() => { return () => { clearAllTimers(); }; }, [clearAllTimers]); return { stage, progress, currentElementId, start, pause, resume, stop, reset, seekToElement, getState, isAnimating: stage === PublicAnimationStage.ACTIVE || stage === PublicAnimationStage.ENTERING, isPaused: stage === PublicAnimationStage.PAUSED, isCompleted: stage === PublicAnimationStage.COMPLETED }; }; /** * Higher-order function to create orchestration sequences */ const createOrchestrationSequence = (elements, options = {}) => ({ elements, mode: options.mode || 'sequential', staggerDelay: options.staggerDelay || 100, loop: options.loop || false, onSequenceStart: options.onSequenceStart, onSequenceComplete: options.onSequenceComplete, onStageChange: options.onStageChange }); export { PublicAnimationStage, createOrchestrationSequence, useOrchestration }; //# sourceMappingURL=useOrchestration.js.map