refactor(theme):valid localstroage and organized theme codes
This commit is contained in:
@@ -1,63 +1,4 @@
|
|||||||
import React, { createContext, useContext, useEffect, useState } from 'react';
|
import { ThemeContextType } from "@/types/theme.interface";
|
||||||
|
import { createContext } from "react";
|
||||||
|
|
||||||
type ThemeMode = 'light' | 'dark';
|
export const ThemeContext = createContext<ThemeContextType | null>(null);
|
||||||
type AccentTheme = 'blue' | 'purple' | 'green';
|
|
||||||
|
|
||||||
interface ThemeContextType {
|
|
||||||
mode: ThemeMode;
|
|
||||||
accent: AccentTheme;
|
|
||||||
setMode: (mode: ThemeMode) => void;
|
|
||||||
setAccent: (accent: AccentTheme) => void;
|
|
||||||
toggleMode: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const ThemeContext = createContext<ThemeContextType | undefined>(undefined);
|
|
||||||
|
|
||||||
export function ThemeProvider({ children }: { children: React.ReactNode }) {
|
|
||||||
const [mode, setModeState] = useState<ThemeMode>('dark');
|
|
||||||
const [accent, setAccentState] = useState<AccentTheme>('blue');
|
|
||||||
|
|
||||||
// Initialize theme from localStorage
|
|
||||||
useEffect(() => {
|
|
||||||
const savedMode = localStorage.getItem('techzaa-mode') as ThemeMode;
|
|
||||||
const savedAccent = localStorage.getItem('techzaa-accent') as AccentTheme;
|
|
||||||
|
|
||||||
if (savedMode) setModeState(savedMode);
|
|
||||||
if (savedAccent) setAccentState(savedAccent);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
// Apply theme classes to document
|
|
||||||
useEffect(() => {
|
|
||||||
const root = document.documentElement;
|
|
||||||
|
|
||||||
// Apply dark/light mode
|
|
||||||
root.classList.remove('light', 'dark');
|
|
||||||
root.classList.add(mode);
|
|
||||||
|
|
||||||
// Apply accent theme
|
|
||||||
root.classList.remove('theme-blue', 'theme-purple', 'theme-green');
|
|
||||||
root.classList.add(`theme-${accent}`);
|
|
||||||
|
|
||||||
// Save to localStorage
|
|
||||||
localStorage.setItem('techzaa-mode', mode);
|
|
||||||
localStorage.setItem('techzaa-accent', accent);
|
|
||||||
}, [mode, accent]);
|
|
||||||
|
|
||||||
const setMode = (newMode: ThemeMode) => setModeState(newMode);
|
|
||||||
const setAccent = (newAccent: AccentTheme) => setAccentState(newAccent);
|
|
||||||
const toggleMode = () => setModeState(prev => prev === 'dark' ? 'light' : 'dark');
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ThemeContext.Provider value={{ mode, accent, setMode, setAccent, toggleMode }}>
|
|
||||||
{children}
|
|
||||||
</ThemeContext.Provider>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function useTheme() {
|
|
||||||
const context = useContext(ThemeContext);
|
|
||||||
if (context === undefined) {
|
|
||||||
throw new Error('useTheme must be used within a ThemeProvider');
|
|
||||||
}
|
|
||||||
return context;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import { ThemeContext } from "@/contexts/ThemeContext";
|
||||||
|
import { ThemeContextType } from "@/types/theme.interface";
|
||||||
|
import { useContext } from "react";
|
||||||
|
|
||||||
|
export function useTheme(): ThemeContextType {
|
||||||
|
const context = useContext(ThemeContext);
|
||||||
|
|
||||||
|
if (!context) {
|
||||||
|
throw new Error(
|
||||||
|
"useTheme must be used within a ThemeProvider"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return context;
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
import { ThemeContext } from "@/contexts/ThemeContext";
|
||||||
|
import { ThemeMode, AccentTheme } from "@/types/theme.interface";
|
||||||
|
import { useState, useEffect, useCallback, useMemo } from "react";
|
||||||
|
|
||||||
|
const STORAGE_KEYS = {
|
||||||
|
mode: "techzaa-mode",
|
||||||
|
accent: "techzaa-accent",
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
const isValidMode = (value: unknown): value is ThemeMode =>
|
||||||
|
value === "light" || value === "dark";
|
||||||
|
|
||||||
|
const isValidAccent = (value: unknown): value is AccentTheme =>
|
||||||
|
value === "blue" || value === "purple" || value === "green";
|
||||||
|
|
||||||
|
export function ThemeProvider({ children }: { children: React.ReactNode }) {
|
||||||
|
const [mode, setMode] = useState<ThemeMode>(() => {
|
||||||
|
if (typeof window === "undefined") return "dark";
|
||||||
|
|
||||||
|
const stored = localStorage.getItem(STORAGE_KEYS.mode);
|
||||||
|
|
||||||
|
return isValidMode(stored) ? stored : "dark";
|
||||||
|
});
|
||||||
|
|
||||||
|
const [accent, setAccent] = useState<AccentTheme>(() => {
|
||||||
|
if (typeof window === "undefined") return "blue";
|
||||||
|
|
||||||
|
const stored = localStorage.getItem(STORAGE_KEYS.accent);
|
||||||
|
|
||||||
|
return isValidAccent(stored) ? stored : "blue";
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (typeof document === "undefined") return;
|
||||||
|
|
||||||
|
const root = document.documentElement;
|
||||||
|
|
||||||
|
root.classList.remove("light", "dark");
|
||||||
|
root.classList.add(mode);
|
||||||
|
|
||||||
|
root.classList.remove("theme-blue", "theme-purple", "theme-green");
|
||||||
|
root.classList.add(`theme-${accent}`);
|
||||||
|
|
||||||
|
localStorage.setItem(STORAGE_KEYS.mode, mode);
|
||||||
|
localStorage.setItem(STORAGE_KEYS.accent, accent);
|
||||||
|
}, [mode, accent]);
|
||||||
|
|
||||||
|
const toggleMode = useCallback(() => {
|
||||||
|
setMode((prev) => (prev === "dark" ? "light" : "dark"));
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const value = useMemo(
|
||||||
|
() => ({
|
||||||
|
mode,
|
||||||
|
accent,
|
||||||
|
setMode,
|
||||||
|
setAccent,
|
||||||
|
toggleMode,
|
||||||
|
}),
|
||||||
|
[mode, accent, toggleMode],
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
export type ThemeMode = "light" | "dark";
|
||||||
|
export type AccentTheme = "blue" | "purple" | "green";
|
||||||
|
|
||||||
|
export interface ThemeContextType {
|
||||||
|
mode: ThemeMode;
|
||||||
|
accent: AccentTheme;
|
||||||
|
setMode: React.Dispatch<React.SetStateAction<ThemeMode>>;
|
||||||
|
setAccent: React.Dispatch<React.SetStateAction<AccentTheme>>;
|
||||||
|
toggleMode: () => void;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user