1{"version":3,"file":"AnimationManager.js","sourceRoot":"","sources":["../../src/web/AnimationManager.tsx"],"names":[],"mappings":"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","sourcesContent":["import React from 'react';\n\nimport { ImageTransition } from '../Image.types';\n\ntype Callbacks = {\n  onReady?: (() => void) | null;\n  onAnimationFinished?: (() => void) | null;\n  onMount?: (() => void) | null;\n  onError?: (() => void) | null;\n};\n\nexport type AnimationManagerNode = [\n  key: string,\n  renderFunction: (\n    renderProps: NonNullable<Callbacks>\n  ) => (className: string, style: React.CSSProperties) => React.ReactElement\n];\n\nconst SUPPORTED_ANIMATIONS: ImageTransition['effect'][] = [\n  'cross-dissolve',\n  'flip-from-left',\n  'flip-from-right',\n  'flip-from-top',\n  'flip-from-bottom',\n];\n\ntype NodeStatus = 'mounted' | 'in' | 'active' | 'out' | 'errored';\n\nfunction useAnimationManagerNode(node: AnimationManagerNode | null, initialStatus?: NodeStatus) {\n  const newNode = React.useMemo(() => {\n    if (!node) {\n      return null;\n    }\n    const [animationKey, renderFunction] = node;\n    // key, ReactElement, ref, callbacks\n    return {\n      animationKey,\n      persistedElement: renderFunction,\n      status: (initialStatus || 'mounted') as NodeStatus,\n    };\n  }, [node?.[0]]);\n  return newNode;\n}\n\nfunction validateTimingFunctionForAnimation(\n  animationClass: ImageTransition['effect'],\n  timingFunction: ImageTransition['timing']\n) {\n  if (animationClass?.includes('flip')) {\n    if (timingFunction?.includes('ease')) {\n      return 'ease-in-out';\n    }\n    return 'linear';\n  }\n  return timingFunction || null;\n}\n\nfunction validateAnimationClass(effect: ImageTransition['effect']) {\n  if (SUPPORTED_ANIMATIONS.includes(effect)) {\n    return effect;\n  }\n  return 'cross-dissolve';\n}\n\nexport function getAnimatorFromTransition(transition: ImageTransition | null | undefined) {\n  if (!transition?.duration) {\n    return null;\n  }\n  const animationClass = validateAnimationClass(transition.effect);\n  if (!animationClass) {\n    return {\n      startingClass: '',\n      animateInClass: '',\n      animateOutClass: '',\n      containerClass: '',\n      timingFunction: 'linear',\n      animationClass: '',\n      duration: 0,\n    };\n  }\n\n  const timingFunction = validateTimingFunctionForAnimation(animationClass, transition.timing);\n  const timingClass = `image-timing-${timingFunction}`;\n\n  return {\n    startingClass: `${animationClass}-start`,\n    animateInClass: [animationClass, 'transitioning', `${animationClass}-active`, timingClass].join(\n      ' '\n    ),\n    animateOutClass: [animationClass, `${animationClass}-end`, timingClass].join(' '),\n    containerClass: `${animationClass}-container`,\n    timingFunction,\n    animationClass,\n    duration: transition?.duration || 0,\n  };\n}\n\ntype MountedAnimationNode = {\n  animationKey: string;\n  persistedElement: (\n    renderProps: Callbacks\n  ) => (className: string, style: React.CSSProperties) => React.ReactElement;\n  status: NodeStatus;\n};\n\nexport default function AnimationManager({\n  children: renderFunction,\n  initial,\n  transition,\n  recyclingKey,\n}: {\n  children: AnimationManagerNode;\n  initial: AnimationManagerNode | null;\n  transition: ImageTransition | null | undefined;\n  recyclingKey?: string | null | undefined;\n}) {\n  const animation = getAnimatorFromTransition(transition);\n\n  const initialNode = useAnimationManagerNode(initial, 'active');\n\n  const [nodes, setNodes] = React.useState<MountedAnimationNode[]>(\n    initialNode ? [initialNode] : []\n  );\n\n  const [prevRecyclingKey, setPrevRecyclingKey] = React.useState<string>(recyclingKey ?? '');\n  if (prevRecyclingKey !== (recyclingKey ?? '')) {\n    setPrevRecyclingKey(recyclingKey ?? '');\n    setNodes(initialNode ? [initialNode] : []);\n  }\n\n  const removeAllNodesOfKeyExceptShowing = (key?: string) => {\n    setNodes((n) =>\n      n.filter(\n        (node) =>\n          (key ? node.animationKey !== key : false) ||\n          node.status === 'in' ||\n          node.status === 'active'\n      )\n    );\n  };\n\n  const newNode = useAnimationManagerNode(renderFunction);\n\n  React.useEffect(() => {\n    setNodes((n) => {\n      if (!newNode) {\n        return n;\n      }\n      const existingNodeIndex = n.findIndex((node) => node.animationKey === newNode.animationKey);\n      if (existingNodeIndex >= 0) {\n        if (animation) {\n          return n.map((n2) =>\n            n2.animationKey === newNode.animationKey\n              ? { ...newNode, status: 'in' }\n              : { ...n2, status: 'out' }\n          );\n        } else {\n          return [{ ...newNode, status: 'in' }];\n        }\n      }\n      return [...n, newNode];\n    });\n  }, [newNode]);\n\n  function wrapNodeWithCallbacks(node: MountedAnimationNode) {\n    if (renderFunction[0] === node.animationKey) {\n      return renderFunction[1]({\n        onReady: () => {\n          if (animation) {\n            setNodes((nodes) =>\n              nodes.map((n) => (n === newNode ? { ...n, status: 'in' } : { ...n, status: 'out' }))\n            );\n          } else {\n            setNodes([{ ...node, status: 'in' }]);\n          }\n        },\n        onAnimationFinished: () => {\n          setNodes([{ ...node, status: 'in' }]);\n        },\n        onError: () => {\n          setNodes((nodes) => nodes.map((n) => (n === node ? { ...n, status: 'errored' } : n)));\n        },\n      });\n    }\n    if (initial?.[0] === node.animationKey) {\n      return initial[1]({\n        onAnimationFinished: () => {\n          if (node.status === 'out') {\n            removeAllNodesOfKeyExceptShowing(node.animationKey);\n          }\n        },\n        onError: () => {\n          setNodes((nodes) => nodes.map((n) => (n === node ? { ...n, status: 'errored' } : n)));\n        },\n      });\n    }\n    return node.persistedElement({\n      onAnimationFinished: () => {\n        removeAllNodesOfKeyExceptShowing(node.animationKey);\n      },\n    });\n  }\n  const styles = {\n    transitionDuration: `${animation?.duration || 0}ms`,\n    transitionTimingFunction: animation?.timingFunction || 'linear',\n  };\n  const classes = {\n    in: animation?.animateInClass,\n    out: animation?.animateOutClass,\n    mounted: animation?.startingClass,\n  };\n\n  return (\n    <>\n      {[...nodes]\n        .filter((n) => n.status !== 'errored')\n        .map((n) => (\n          <div className={animation?.containerClass} key={n.animationKey}>\n            {wrapNodeWithCallbacks(n)(classes[n.status], styles)}\n          </div>\n        ))}\n    </>\n  );\n}\n"]}