import { classNames, NumberFormatDisplay } from "../../utils" import { useNumberFormatter } from "../useNumberFormatter" import { usePrevious } from "../usePrevious" export type UseChangeAnimationOptions = { value: number | undefined | null onChangeAnimationClassName?: string onPositiveChangeAnimationClassName?: string onNegativeChangeAnimationClassName?: string display?: NumberFormatDisplay } export const useChangeAnimation = ({ value, onChangeAnimationClassName = "animate-change-animation-bg", onPositiveChangeAnimationClassName = "animate-positive-change-bg", onNegativeChangeAnimationClassName = "animate-negative-change-bg", display, }: UseChangeAnimationOptions) => { const format = useNumberFormatter() const formattedValue = format(value, { display }) const previousValue = usePrevious(value) const previousFormattedValue = usePrevious(formattedValue) const hasFormattedValueChanged = typeof previousFormattedValue !== "undefined" && previousFormattedValue !== formattedValue const hasRawValueChanged = typeof previousValue !== "undefined" && previousValue !== value const animationClassName = classNames({ // Apply change animation if the formatted value has changed. ...{ [onChangeAnimationClassName]: hasFormattedValueChanged }, // Apply positive/negative change classes if the raw value has changed. ...(typeof previousValue === "number" && hasRawValueChanged ? { [onPositiveChangeAnimationClassName ?? ""]: (value ?? 0) > previousValue, [onNegativeChangeAnimationClassName ?? ""]: (value ?? 0) < previousValue, } : {}), }) // The re-render key is used to force the component to re-render when the value changes (e.g. to re-apply the animations) return { animationClassName, rerenderKey: formattedValue } }