aboutsummaryrefslogtreecommitdiffstats
path: root/src/utils/providers/motion-provider/motion-provider.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/utils/providers/motion-provider/motion-provider.tsx')
-rw-r--r--src/utils/providers/motion-provider/motion-provider.tsx83
1 files changed, 83 insertions, 0 deletions
diff --git a/src/utils/providers/motion-provider/motion-provider.tsx b/src/utils/providers/motion-provider/motion-provider.tsx
new file mode 100644
index 0000000..dfedcaa
--- /dev/null
+++ b/src/utils/providers/motion-provider/motion-provider.tsx
@@ -0,0 +1,83 @@
+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<SetStateAction<boolean>>;
+ toggleReducedMotion: () => void;
+};
+
+export const MotionContext = createContext<MotionContextProps>({
+ 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<MotionProviderProps> = ({
+ 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 (
+ <MotionContext.Provider value={value}>{children}</MotionContext.Provider>
+ );
+};