import { type Dispatch, type FC, type ReactNode, type SetStateAction, createContext, useMemo, useCallback, useEffect, } from 'react'; import { getDataAttributeFrom } from '../../helpers'; import { useLocalStorage } from '../../hooks'; type MotionContextProps = { isReduced: boolean; setIsReduced: Dispatch>; toggleReducedMotion: () => void; }; export const MotionContext = createContext({ isReduced: false, setIsReduced: (value) => value, toggleReducedMotion: () => null, }); const validator = (value: unknown): value is boolean => typeof value === 'boolean'; export type MotionProviderProps = { /** * The attribute name to append to document root. */ attribute: string; /** * The provider children. */ children?: ReactNode; /** * Is reduced motion currently active? * * @default false */ hasReducedMotion?: boolean; /** * The key to use in local storage. */ storageKey: string; }; export const MotionProvider: FC = ({ attribute, children, hasReducedMotion = false, storageKey, }) => { const [isReduced, setIsReduced] = useLocalStorage( storageKey, hasReducedMotion, validator ); const dataAttribute = getDataAttributeFrom(attribute); useEffect(() => { if (typeof window !== 'undefined') document.documentElement.setAttribute(dataAttribute, `${isReduced}`); return () => { document.documentElement.removeAttribute(dataAttribute); }; }, [dataAttribute, isReduced]); const toggleReducedMotion = useCallback(() => { setIsReduced((prevState) => !prevState); }, [setIsReduced]); const value: MotionContextProps = useMemo(() => { return { isReduced, setIsReduced, toggleReducedMotion }; }, [isReduced, setIsReduced, toggleReducedMotion]); return ( {children} ); };