import {useState, createContext, useContext} from "react"; import {useLocalStorage} from "usehooks-ts"; import {configKey, initialConfig} from "./constants"; import { ConfigColors, Config, ConfigLayout, ThemeType, Radius, TemplateType, FontType, HeroUIScaling, Border, } from "./types"; export interface ThemeBuilderContextProps { config: Config; radiusValue: Radius; borderWidthValue: Border; templateTheme: TemplateType; font: FontType | undefined; scaling: HeroUIScaling; resetConfig: (theme: ThemeType, sync: boolean) => Config; setLayoutColor: ( newConfig: Partial, theme: ThemeType, sync: boolean, ) => void; setContentColor: (newConfig: Partial, theme: ThemeType) => void; setBorderWidth: (newConfig: Partial) => void; setBaseColor: ( newConfig: Partial, theme: ThemeType, sync: boolean, ) => void; setDefaultColor: ( newConfig: Partial, theme: ThemeType, sync: boolean, ) => void; setConfiguration: (newConfig: Config, theme: ThemeType, sync: boolean) => void; setLineHeight: (newConfig: Partial) => void; setFontSize: (newConfig: Partial) => void; setOtherParams: (newConfig: Partial) => void; setRadius: (newConfig: Partial) => void; setRadiusValue: (radius: Radius) => void; setBorderWidthValue: (borderWidth: Border) => void; setTemplateTheme: (theme: TemplateType) => void; setFont: (font: FontType) => void; setScaling: (scale: HeroUIScaling) => void; } const ThemeBuilderContext = createContext({ config: initialConfig, radiusValue: "md", borderWidthValue: "thick", templateTheme: "heroui", font: "Inter", scaling: 100, resetConfig: () => initialConfig, setLayoutColor: () => {}, setBorderWidth: () => {}, setBaseColor: () => {}, setConfiguration: () => {}, setLineHeight: () => {}, setFontSize: () => {}, setOtherParams: () => {}, setRadius: () => {}, setDefaultColor: () => {}, setContentColor: () => {}, setRadiusValue: () => {}, setBorderWidthValue: () => {}, setTemplateTheme: () => {}, setFont: () => {}, setScaling: () => {}, }); interface ThemeBuilderProviderProps { children: React.ReactNode; } export default function ThemeBuilderProvider({children}: ThemeBuilderProviderProps) { const [lsConfig] = useLocalStorage(configKey, initialConfig); const [config, setConfig] = useState(lsConfig); const [radiusValue, setRadiusValue] = useState("sm"); const [borderWidthValue, setBorderWidthValue] = useState("thin"); const [templateTheme, setTemplateTheme] = useState("heroui"); const [font, setFont] = useState(undefined); const [scaling, setScaling] = useState(100); const setConfiguration = (newConfig: Config, theme: ThemeType, sync: boolean) => { setConfig((prev) => sync ? newConfig : { ...prev, [theme]: newConfig[theme], }, ); }; const setTemplate = (template: TemplateType) => { setTemplateTheme(template); setConfig((prev) => { return { ...prev, name: template, }; }); }; const resetConfig = (theme: ThemeType, sync: boolean) => { let newConfig = initialConfig; setConfig((prev) => { newConfig = sync ? newConfig : { ...prev, [theme]: newConfig[theme], layout: newConfig.layout, }; return newConfig; }); return newConfig; }; const setBaseColor = ( newConfig: Partial, theme: ThemeType, sync: boolean, ) => { setConfig((prev) => sync ? { ...prev, light: { ...prev.light, baseColor: { ...prev.light.baseColor, ...newConfig, }, }, dark: { ...prev.dark, baseColor: { ...prev.dark.baseColor, ...newConfig, }, }, } : { ...prev, [theme]: { ...prev[theme], baseColor: { ...prev[theme].baseColor, ...newConfig, }, }, }, ); }; const setDefaultColor = ( newConfig: Partial, theme: ThemeType, sync: boolean, ) => { setConfig((prev) => sync ? { ...prev, light: { ...prev.light, defaultColor: { ...prev.light.defaultColor, ...newConfig, }, }, dark: { ...prev.dark, defaultColor: { ...prev.dark.defaultColor, ...newConfig, }, }, } : { ...prev, [theme]: { ...prev[theme], defaultColor: { ...prev[theme].defaultColor, ...newConfig, }, }, }, ); }; const setContentColor = (newConfig: Partial, theme: ThemeType) => { setConfig((prev) => ({ ...prev, [theme]: { ...prev[theme], contentColor: { ...prev[theme].contentColor, ...newConfig, }, }, })); }; const setLayoutColor = ( newConfig: Partial, theme: ThemeType, sync: boolean, ) => { setConfig((prev) => sync ? { ...prev, light: { ...prev.light, layoutColor: { ...prev.light.layoutColor, ...newConfig, }, }, dark: { ...prev.dark, layoutColor: { ...prev.dark.layoutColor, ...newConfig, }, }, } : { ...prev, [theme]: { ...prev[theme], layoutColor: { ...prev[theme].layoutColor, ...newConfig, }, }, }, ); }; const setBorderWidth = (newBorderWidths: Partial) => setConfig((prev) => ({ ...prev, layout: { ...prev.layout, borderWidth: { ...prev.layout.borderWidth, ...newBorderWidths, }, }, })); const setLineHeight = (newLineHeights: Partial) => setConfig((prev) => ({ ...prev, layout: { ...prev.layout, lineHeight: { ...prev.layout.lineHeight, ...newLineHeights, }, }, })); const setFontSize = (newFontSizes: Partial) => setConfig((prev) => ({ ...prev, layout: { ...prev.layout, fontSize: { ...prev.layout.fontSize, ...newFontSizes, }, }, })); const setRadius = (newRadius: Partial) => setConfig((prev) => ({ ...prev, layout: { ...prev.layout, radius: { ...prev.layout.radius, ...newRadius, }, }, })); const setOtherParams = (newOtherParams: Partial) => setConfig((prev) => ({ ...prev, layout: { ...prev.layout, otherParams: { ...prev.layout.otherParams, ...newOtherParams, }, }, })); return ( {children} ); } // Create a custom hook to use the ThemeBuilderContext export function useThemeBuilder(): ThemeBuilderContextProps { const context = useContext(ThemeBuilderContext); if (!context) { throw new Error("useThemeBuilder must be used within a ThemeBuilderProvider"); } return context; }