diff --git a/apps/app/components/core/custom-theme-selector.tsx b/apps/app/components/core/custom-theme-selector.tsx index 59aed8b2b..5ed3a3bef 100644 --- a/apps/app/components/core/custom-theme-selector.tsx +++ b/apps/app/components/core/custom-theme-selector.tsx @@ -13,29 +13,29 @@ import { ColorPickerInput } from "components/core"; import userService from "services/user.service"; // helper import { applyTheme } from "helpers/theme.helper"; +import { hexToRgb, rgbToHex } from "helpers/color.helper"; // types import { ICustomTheme } from "types"; type Props = { preLoadedData?: Partial | null; }; +const defaultValues = { + "accent-500": "#FE5050", + bgBase: "#FFF7F7", + bgSurface1: "#FFE0E0", + bgSurface2: "#FFF7F7", + border: "#FFC9C9", + darkPalette: false, + palette: "", + sidebar: "#FFFFFF", + textBase: "#430000", + textSecondary: "#323232", +}; export const CustomThemeSelector: React.FC = ({ preLoadedData }) => { const [darkPalette, setDarkPalette] = useState(false); - const defaultValues = { - accent: preLoadedData?.accent ?? "#FE5050", - bgBase: preLoadedData?.bgBase ?? "#FFF7F7", - bgSurface1: preLoadedData?.bgSurface1 ?? "#FFE0E0", - bgSurface2: preLoadedData?.bgSurface2 ?? "#FFF7F7", - border: preLoadedData?.border ?? "#FFC9C9", - darkPalette: preLoadedData?.darkPalette ?? false, - palette: preLoadedData?.palette ?? "", - sidebar: preLoadedData?.sidebar ?? "#FFFFFF", - textBase: preLoadedData?.textBase ?? "#430000", - textSecondary: preLoadedData?.textSecondary ?? "#323232", - }; - const { register, formState: { errors, isSubmitting }, @@ -50,17 +50,66 @@ export const CustomThemeSelector: React.FC = ({ preLoadedData }) => { const { setTheme } = useTheme(); const { mutateUser } = useUser(); + const calculateShade = (hexValue: string, shade: number): string => { + const { r, g, b } = hexToRgb(hexValue); + + if (shade > 500) { + let decimalValue = 0.1; + + if (shade === 600) decimalValue = 0.1; + else if (shade === 700) decimalValue = 0.2; + else if (shade === 800) decimalValue = 0.3; + else if (shade === 900) decimalValue = 0.4; + + const newR = Math.ceil(r * decimalValue); + const newG = Math.ceil(g * decimalValue); + const newB = Math.ceil(b * decimalValue); + + return rgbToHex({ r: newR, g: newG, b: newB }); + } else { + const decimalValue = 1 - (shade * 2) / 1000; + + const newR = Math.floor(r + (255 - r) * decimalValue); + const newG = Math.floor(g + (255 - g) * decimalValue); + const newB = Math.floor(b + (255 - b) * decimalValue); + + return rgbToHex({ r: newR, g: newG, b: newB }); + } + }; + const handleFormSubmit = async (formData: any) => { + const accent = { + 50: calculateShade(formData["accent-500"], 50), + 100: calculateShade(formData["accent-500"], 100), + 200: calculateShade(formData["accent-500"], 200), + 300: calculateShade(formData["accent-500"], 300), + 400: calculateShade(formData["accent-500"], 400), + 500: formData["accent-500"], + 600: calculateShade(formData["accent-500"], 600), + 700: calculateShade(formData["accent-500"], 700), + 800: calculateShade(formData["accent-500"], 800), + 900: calculateShade(formData["accent-500"], 900), + }; + await userService .updateUser({ theme: { - accent: formData.accent, + "accent-50": accent[50], + "accent-100": accent[100], + "accent-200": accent[200], + "accent-300": accent[300], + "accent-400": accent[400], + "accent-500": accent[500], + "accent-600": accent[600], + "accent-700": accent[700], + "accent-800": accent[800], + "accent-900": accent[900], bgBase: formData.bgBase, bgSurface1: formData.bgSurface1, bgSurface2: formData.bgSurface2, border: formData.border, darkPalette: darkPalette, - palette: `${formData.bgBase},${formData.bgSurface1},${formData.bgSurface2},${formData.border},${formData.sidebar},${formData.accent},${formData.textBase},${formData.textSecondary}`, + palette: `${formData.bgBase},${formData.bgSurface1},${formData.bgSurface2},${formData.border},${formData.sidebar},${accent[50]},${accent[100]},${accent[200]},${accent[300]},${accent[400]},${accent[500]},${accent[600]},${accent[700]},${accent[800]},${accent[900]},${formData.textBase},${formData.textSecondary}`, sidebar: formData.sidebar, textBase: formData.textBase, textSecondary: formData.textSecondary, @@ -69,8 +118,10 @@ export const CustomThemeSelector: React.FC = ({ preLoadedData }) => { .then((res) => { mutateUser((prevData) => { if (!prevData) return prevData; - return { ...prevData, user: res }; + + return { ...prevData, ...res }; }, false); + applyTheme(formData.palette, darkPalette); setTheme("custom"); }) @@ -79,10 +130,6 @@ export const CustomThemeSelector: React.FC = ({ preLoadedData }) => { const handleUpdateTheme = async (formData: any) => { await handleFormSubmit({ ...formData, darkPalette }); - - reset({ - ...defaultValues, - }); }; useEffect(() => { @@ -156,8 +203,8 @@ export const CustomThemeSelector: React.FC = ({ preLoadedData }) => {

Accent

= ({ "--color-bg-base", "--color-bg-surface-1", "--color-bg-surface-2", + "--color-bg-sidebar", "--color-border", - "--color-bg-sidebar", - "--color-accent", + + "--color-accent-50", + "--color-accent-100", + "--color-accent-200", + "--color-accent-300", + "--color-accent-400", + "--color-accent-500", + "--color-accent-600", + "--color-accent-700", + "--color-accent-800", + "--color-accent-900", "--color-text-base", "--color-text-secondary", diff --git a/apps/app/helpers/color.helper.ts b/apps/app/helpers/color.helper.ts new file mode 100644 index 000000000..a1f915a1e --- /dev/null +++ b/apps/app/helpers/color.helper.ts @@ -0,0 +1,19 @@ +export type TRgb = { r: number; g: number; b: number }; + +export const hexToRgb = (hex: string): TRgb => { + const r = parseInt(hex.slice(1, 3), 16); + const g = parseInt(hex.slice(3, 5), 16); + const b = parseInt(hex.slice(5, 7), 16); + + return { r, g, b }; +}; + +export const rgbToHex = (rgb: TRgb): string => { + const { r, g, b } = rgb; + + const hexR = r.toString(16).padStart(2, "0"); + const hexG = g.toString(16).padStart(2, "0"); + const hexB = b.toString(16).padStart(2, "0"); + + return `#${hexR}${hexG}${hexB}`; +}; diff --git a/apps/app/helpers/theme.helper.ts b/apps/app/helpers/theme.helper.ts index d3d76f3e5..315679719 100644 --- a/apps/app/helpers/theme.helper.ts +++ b/apps/app/helpers/theme.helper.ts @@ -19,7 +19,16 @@ export const applyTheme = (palette: string, isDarkPalette: boolean) => { "--color-bg-surface-2", "--color-border", "--color-bg-sidebar", - "--color-accent", + "--color-accent-50", + "--color-accent-100", + "--color-accent-200", + "--color-accent-300", + "--color-accent-400", + "--color-accent-500", + "--color-accent-600", + "--color-accent-700", + "--color-accent-800", + "--color-accent-900", "--color-text-base", "--color-text-secondary", "color-scheme", diff --git a/apps/app/styles/globals.css b/apps/app/styles/globals.css index ccfda2248..4b9428f2b 100644 --- a/apps/app/styles/globals.css +++ b/apps/app/styles/globals.css @@ -23,7 +23,16 @@ --color-border: 229, 231, 235; --color-bg-sidebar: 255, 255, 255; - --color-accent: 63, 118, 255; + --color-accent-50: 63, 118, 255; + --color-accent-100: 63, 118, 255; + --color-accent-200: 63, 118, 255; + --color-accent-300: 63, 118, 255; + --color-accent-400: 63, 118, 255; + --color-accent-500: 63, 118, 255; + --color-accent-600: 63, 118, 255; + --color-accent-700: 63, 118, 255; + --color-accent-800: 63, 118, 255; + --color-accent-900: 63, 118, 255; --color-text-base: 3, 7, 18; --color-text-secondary: 55, 65, 81; diff --git a/apps/app/tailwind.config.js b/apps/app/tailwind.config.js index cc9863e90..bfa537413 100644 --- a/apps/app/tailwind.config.js +++ b/apps/app/tailwind.config.js @@ -6,9 +6,7 @@ function withOpacity(variableName) { }; } -function convertToRGB(variableName) { - return `rgb(var(${variableName}))`; -} +const convertToRGB = (variableName) => `rgb(var(${variableName}))`; module.exports = { darkMode: "class", @@ -17,7 +15,19 @@ module.exports = { extend: { colors: { brand: { - accent: withOpacity("--color-accent"), + accent: { + 50: convertToRGB("--color-accent-50"), + 100: convertToRGB("--color-accent-100"), + 200: convertToRGB("--color-accent-200"), + 300: convertToRGB("--color-accent-300"), + 400: convertToRGB("--color-accent-400"), + 500: convertToRGB("--color-accent-500"), + 600: convertToRGB("--color-accent-600"), + 700: convertToRGB("--color-accent-700"), + 800: convertToRGB("--color-accent-800"), + 900: convertToRGB("--color-accent-900"), + DEFAULT: convertToRGB("--color-accent-500"), + }, base: withOpacity("--color-bg-base"), }, }, diff --git a/apps/app/types/users.d.ts b/apps/app/types/users.d.ts index eaa75b75a..82a57361f 100644 --- a/apps/app/types/users.d.ts +++ b/apps/app/types/users.d.ts @@ -29,7 +29,16 @@ export interface IUser { } export interface ICustomTheme { - accent: string; + "accent-50": string; + "accent-100": string; + "accent-200": string; + "accent-300": string; + "accent-400": string; + "accent-500": string; + "accent-600": string; + "accent-700": string; + "accent-800": string; + "accent-900": string; bgBase: string; bgSurface1: string; bgSurface2: string;