mirror of
https://github.com/nextui-org/nextui.git
synced 2025-12-08 19:26:11 +00:00
fix: adding tooltip to show items which are only for the visual purpose fix: persist the theme across reloads chore: modifying the mobile styles
357 lines
8.7 KiB
TypeScript
357 lines
8.7 KiB
TypeScript
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<ConfigColors["layoutColor"]>,
|
|
theme: ThemeType,
|
|
sync: boolean,
|
|
) => void;
|
|
setContentColor: (newConfig: Partial<ConfigColors["contentColor"]>, theme: ThemeType) => void;
|
|
setBorderWidth: (newConfig: Partial<ConfigLayout["borderWidth"]>) => void;
|
|
setBaseColor: (
|
|
newConfig: Partial<ConfigColors["baseColor"]>,
|
|
theme: ThemeType,
|
|
sync: boolean,
|
|
) => void;
|
|
setDefaultColor: (
|
|
newConfig: Partial<ConfigColors["defaultColor"]>,
|
|
theme: ThemeType,
|
|
sync: boolean,
|
|
) => void;
|
|
setConfiguration: (newConfig: Config, theme: ThemeType, sync: boolean) => void;
|
|
setLineHeight: (newConfig: Partial<ConfigLayout["lineHeight"]>) => void;
|
|
setFontSize: (newConfig: Partial<ConfigLayout["fontSize"]>) => void;
|
|
setOtherParams: (newConfig: Partial<ConfigLayout["otherParams"]>) => void;
|
|
setRadius: (newConfig: Partial<ConfigLayout["radius"]>) => void;
|
|
setRadiusValue: (radius: Radius) => void;
|
|
setBorderWidthValue: (borderWidth: Border) => void;
|
|
setTemplateTheme: (theme: TemplateType) => void;
|
|
setFont: (font: FontType) => void;
|
|
setScaling: (scale: HeroUIScaling) => void;
|
|
}
|
|
|
|
const ThemeBuilderContext = createContext<ThemeBuilderContextProps>({
|
|
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<Config>(configKey, initialConfig);
|
|
const [config, setConfig] = useState<Config>(lsConfig);
|
|
const [radiusValue, setRadiusValue] = useState<Radius>("sm");
|
|
const [borderWidthValue, setBorderWidthValue] = useState<Border>("thin");
|
|
const [templateTheme, setTemplateTheme] = useState<TemplateType>("heroui");
|
|
const [font, setFont] = useState<FontType | undefined>(undefined);
|
|
const [scaling, setScaling] = useState<HeroUIScaling>(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<ConfigColors["baseColor"]>,
|
|
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<ConfigColors["defaultColor"]>,
|
|
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<ConfigColors["contentColor"]>, theme: ThemeType) => {
|
|
setConfig((prev) => ({
|
|
...prev,
|
|
[theme]: {
|
|
...prev[theme],
|
|
contentColor: {
|
|
...prev[theme].contentColor,
|
|
...newConfig,
|
|
},
|
|
},
|
|
}));
|
|
};
|
|
|
|
const setLayoutColor = (
|
|
newConfig: Partial<ConfigColors["layoutColor"]>,
|
|
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<ConfigLayout["borderWidth"]>) =>
|
|
setConfig((prev) => ({
|
|
...prev,
|
|
layout: {
|
|
...prev.layout,
|
|
borderWidth: {
|
|
...prev.layout.borderWidth,
|
|
...newBorderWidths,
|
|
},
|
|
},
|
|
}));
|
|
|
|
const setLineHeight = (newLineHeights: Partial<ConfigLayout["lineHeight"]>) =>
|
|
setConfig((prev) => ({
|
|
...prev,
|
|
layout: {
|
|
...prev.layout,
|
|
lineHeight: {
|
|
...prev.layout.lineHeight,
|
|
...newLineHeights,
|
|
},
|
|
},
|
|
}));
|
|
|
|
const setFontSize = (newFontSizes: Partial<ConfigLayout["fontSize"]>) =>
|
|
setConfig((prev) => ({
|
|
...prev,
|
|
layout: {
|
|
...prev.layout,
|
|
fontSize: {
|
|
...prev.layout.fontSize,
|
|
...newFontSizes,
|
|
},
|
|
},
|
|
}));
|
|
|
|
const setRadius = (newRadius: Partial<ConfigLayout["radius"]>) =>
|
|
setConfig((prev) => ({
|
|
...prev,
|
|
layout: {
|
|
...prev.layout,
|
|
radius: {
|
|
...prev.layout.radius,
|
|
...newRadius,
|
|
},
|
|
},
|
|
}));
|
|
|
|
const setOtherParams = (newOtherParams: Partial<ConfigLayout["otherParams"]>) =>
|
|
setConfig((prev) => ({
|
|
...prev,
|
|
layout: {
|
|
...prev.layout,
|
|
otherParams: {
|
|
...prev.layout.otherParams,
|
|
...newOtherParams,
|
|
},
|
|
},
|
|
}));
|
|
|
|
return (
|
|
<ThemeBuilderContext.Provider
|
|
value={{
|
|
config,
|
|
radiusValue,
|
|
borderWidthValue,
|
|
templateTheme,
|
|
font,
|
|
scaling,
|
|
resetConfig,
|
|
setLayoutColor,
|
|
setBorderWidth,
|
|
setBaseColor,
|
|
setConfiguration,
|
|
setLineHeight,
|
|
setFontSize,
|
|
setOtherParams,
|
|
setRadius,
|
|
setDefaultColor,
|
|
setContentColor,
|
|
setRadiusValue,
|
|
setBorderWidthValue,
|
|
setTemplateTheme: setTemplate,
|
|
setFont,
|
|
setScaling,
|
|
}}
|
|
>
|
|
{children}
|
|
</ThemeBuilderContext.Provider>
|
|
);
|
|
}
|
|
|
|
// 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;
|
|
}
|