New booking panel
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import {
|
||||
type PropsWithChildren,
|
||||
createContext,
|
||||
useCallback,
|
||||
useContext,
|
||||
useEffect,
|
||||
useMemo,
|
||||
@@ -14,6 +15,7 @@ type ThemeContextValue = {
|
||||
theme: Theme;
|
||||
resolvedTheme: ResolvedTheme;
|
||||
setTheme: (theme: Theme) => void;
|
||||
toggleTheme: () => void;
|
||||
};
|
||||
|
||||
const THEME_STORAGE_KEY = 'theme';
|
||||
@@ -27,6 +29,18 @@ function applyResolvedTheme(theme: ResolvedTheme) {
|
||||
document.documentElement.classList.toggle('dark', theme === 'dark');
|
||||
}
|
||||
|
||||
function isEditableElement(target: EventTarget | null) {
|
||||
if (!(target instanceof HTMLElement)) return false;
|
||||
|
||||
const tagName = target.tagName.toLowerCase();
|
||||
return (
|
||||
tagName === 'input' ||
|
||||
tagName === 'textarea' ||
|
||||
tagName === 'select' ||
|
||||
target.isContentEditable
|
||||
);
|
||||
}
|
||||
|
||||
export function ThemeProvider({ children }: PropsWithChildren) {
|
||||
const [theme, setThemeState] = useState<Theme>('system');
|
||||
const [resolvedTheme, setResolvedTheme] = useState<ResolvedTheme>('light');
|
||||
@@ -54,18 +68,37 @@ export function ThemeProvider({ children }: PropsWithChildren) {
|
||||
return () => mediaQuery.removeEventListener('change', updateTheme);
|
||||
}, [theme]);
|
||||
|
||||
const setTheme = (nextTheme: Theme) => {
|
||||
const setTheme = useCallback((nextTheme: Theme) => {
|
||||
setThemeState(nextTheme);
|
||||
localStorage.setItem(THEME_STORAGE_KEY, nextTheme);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const toggleTheme = useCallback(() => {
|
||||
setTheme(resolvedTheme === 'dark' ? 'light' : 'dark');
|
||||
}, [resolvedTheme, setTheme]);
|
||||
|
||||
useEffect(() => {
|
||||
const handleKeyDown = (event: KeyboardEvent) => {
|
||||
if (isEditableElement(event.target)) return;
|
||||
if (!event.metaKey || event.ctrlKey || event.altKey || event.shiftKey) return;
|
||||
if (event.key.toLowerCase() !== 'd') return;
|
||||
|
||||
event.preventDefault();
|
||||
toggleTheme();
|
||||
};
|
||||
|
||||
window.addEventListener('keydown', handleKeyDown);
|
||||
return () => window.removeEventListener('keydown', handleKeyDown);
|
||||
}, [toggleTheme]);
|
||||
|
||||
const value = useMemo<ThemeContextValue>(
|
||||
() => ({
|
||||
theme,
|
||||
resolvedTheme,
|
||||
setTheme,
|
||||
toggleTheme,
|
||||
}),
|
||||
[theme, resolvedTheme]
|
||||
[theme, resolvedTheme, setTheme, toggleTheme]
|
||||
);
|
||||
|
||||
return <ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>;
|
||||
|
||||
Reference in New Issue
Block a user