ConceptPortal-public/rsconcept/frontend/src/context/ThemeContext.tsx

118 lines
3.1 KiB
TypeScript
Raw Normal View History

'use client';
2023-12-21 00:12:24 +03:00
import clsx from 'clsx';
import { createContext, useCallback, useContext, useLayoutEffect, useMemo, useState } from 'react';
2023-07-15 17:46:19 +03:00
2023-12-21 00:12:24 +03:00
import ConceptTooltip from '@/components/Common/ConceptTooltip';
import useLocalStorage from '@/hooks/useLocalStorage';
import { animationDuration } from '@/utils/animations';
import { darkT, IColorTheme, lightT } from '@/utils/color';
2023-12-21 00:12:24 +03:00
import { globalIDs } from '@/utils/constants';
2023-07-15 17:46:19 +03:00
interface IThemeContext {
2023-08-24 14:23:17 +03:00
viewportHeight: string
mainHeight: string
2023-08-27 15:39:49 +03:00
2023-08-27 00:19:19 +03:00
colors: IColorTheme
2023-08-27 15:39:49 +03:00
darkMode: boolean
2023-07-15 17:46:19 +03:00
toggleDarkMode: () => void
2023-08-27 15:39:49 +03:00
noNavigationAnimation: boolean
2023-08-27 15:39:49 +03:00
noNavigation: boolean
2023-09-05 00:23:53 +03:00
toggleNoNavigation: () => void
2023-08-27 15:39:49 +03:00
noFooter: boolean
setNoFooter: (value: boolean) => void
2023-09-05 00:23:53 +03:00
showScroll: boolean
setShowScroll: (value: boolean) => void
2023-07-15 17:46:19 +03:00
}
const ThemeContext = createContext<IThemeContext | null>(null);
export const useConceptTheme = () => {
const context = useContext(ThemeContext);
if (!context) {
2023-08-27 15:39:49 +03:00
throw new Error('useConceptTheme has to be used within <ThemeState.Provider>');
}
return context;
}
2023-07-15 17:46:19 +03:00
interface ThemeStateProps {
children: React.ReactNode
}
export const ThemeState = ({ children }: ThemeStateProps) => {
const [darkMode, setDarkMode] = useLocalStorage('darkMode', false);
2023-08-27 00:19:19 +03:00
const [colors, setColors] = useState<IColorTheme>(lightT);
const [noNavigation, setNoNavigation] = useState(false);
const [noNavigationAnimation, setNoNavigationAnimation] = useState(false);
2023-08-27 15:39:49 +03:00
const [noFooter, setNoFooter] = useState(false);
2023-09-05 00:23:53 +03:00
const [showScroll, setShowScroll] = useState(false);
2023-07-15 17:46:19 +03:00
2023-08-27 15:39:49 +03:00
function setDarkClass(isDark: boolean) {
2023-07-15 17:46:19 +03:00
const root = window.document.documentElement;
if (isDark) {
root.classList.add('dark');
} else {
root.classList.remove('dark');
}
2023-07-20 17:11:03 +03:00
root.setAttribute('data-color-scheme', !isDark ? 'light' : 'dark');
2023-08-27 15:39:49 +03:00
}
2023-07-15 17:46:19 +03:00
2023-08-24 14:23:17 +03:00
useLayoutEffect(() => {
setDarkClass(darkMode);
2023-07-15 17:46:19 +03:00
}, [darkMode]);
2023-08-27 00:19:19 +03:00
useLayoutEffect(() => {
setColors(darkMode ? darkT : lightT)
}, [darkMode, setColors]);
const toggleNoNavigation = useCallback(
() => {
if (noNavigation) {
setNoNavigationAnimation(false);
setNoNavigation(false);
} else {
setNoNavigationAnimation(true);
setTimeout(() => setNoNavigation(true), animationDuration.navigationToggle);
}
}, [noNavigation]);
2023-08-24 14:23:17 +03:00
const mainHeight = useMemo(
() => {
return !noNavigation ?
'calc(100vh - 7rem - 2px)'
: '100vh';
2023-08-24 14:23:17 +03:00
}, [noNavigation]);
const viewportHeight = useMemo(
() => {
return !noNavigation ?
'calc(100vh - 3rem - 2px)'
: '100vh';
2023-08-24 14:23:17 +03:00
}, [noNavigation]);
2023-07-15 17:46:19 +03:00
return (
<ThemeContext.Provider value={{
darkMode, colors,
noNavigationAnimation, noNavigation, noFooter, showScroll,
toggleDarkMode: () => setDarkMode(prev => !prev),
toggleNoNavigation: toggleNoNavigation,
setNoFooter, setShowScroll,
viewportHeight, mainHeight
}}>
2023-12-21 00:12:24 +03:00
<>
<ConceptTooltip float
id={`${globalIDs.tooltip}`}
layer='z-topmost'
place='right-start'
className={clsx(
'mt-3 translate-y-1/2',
'max-w-[20rem]'
)}
/>
{children}
</>
</ThemeContext.Provider>);
}