chore: update theming structure (#1422)
* chore: store various shades of accent color * refactor: custom theme selector * refactor: custom theme selector * chore: update custom theme input labels * fix: color generator function logic * fix: accent color preloaded data * chore: new theming structure * chore: update shades calculation logic * refactor: variable names * chore: update color scheming * chore: new color scheming * refactor: themes folder structure * chore: update classnames according to new variables * Revert "chore: update classnames according to new variables" This reverts commit 60a87453b21768167e37889e709c12287ca07b08. * chore: remove temp file
This commit is contained in:
parent
d564ea8898
commit
a14f8c281b
15 changed files with 645 additions and 337 deletions
|
|
@ -22,7 +22,7 @@ export const AnalyticsYearWiseIssues: React.FC<Props> = ({ defaultAnalytics }) =
|
|||
data={[
|
||||
{
|
||||
id: "issues_closed",
|
||||
color: "rgb(var(--color-accent))",
|
||||
color: "rgb(var(--color-primary-100))",
|
||||
data: MONTHS_LIST.map((month) => ({
|
||||
x: month.label.substring(0, 3),
|
||||
y:
|
||||
|
|
@ -50,7 +50,7 @@ export const AnalyticsYearWiseIssues: React.FC<Props> = ({ defaultAnalytics }) =
|
|||
</div>
|
||||
)}
|
||||
theme={{
|
||||
background: "rgb(var(--color-bg-base))",
|
||||
background: "rgb(var(--color-background-100))",
|
||||
}}
|
||||
enableArea
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -1,198 +0,0 @@
|
|||
import React, { useEffect, useState } from "react";
|
||||
|
||||
import { useTheme } from "next-themes";
|
||||
|
||||
import { useForm } from "react-hook-form";
|
||||
|
||||
// hooks
|
||||
import useUser from "hooks/use-user";
|
||||
// ui
|
||||
import { PrimaryButton } from "components/ui";
|
||||
import { ColorPickerInput } from "components/core";
|
||||
// services
|
||||
import userService from "services/user.service";
|
||||
// helper
|
||||
import { applyTheme } from "helpers/theme.helper";
|
||||
// types
|
||||
import { ICustomTheme } from "types";
|
||||
|
||||
type Props = {
|
||||
preLoadedData?: Partial<ICustomTheme> | null;
|
||||
};
|
||||
|
||||
export const CustomThemeSelector: React.FC<Props> = ({ 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 },
|
||||
handleSubmit,
|
||||
watch,
|
||||
setValue,
|
||||
reset,
|
||||
} = useForm<any>({
|
||||
defaultValues,
|
||||
});
|
||||
|
||||
const { setTheme } = useTheme();
|
||||
const { mutateUser } = useUser();
|
||||
|
||||
const handleFormSubmit = async (formData: any) => {
|
||||
await userService
|
||||
.updateUser({
|
||||
theme: {
|
||||
accent: formData.accent,
|
||||
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}`,
|
||||
sidebar: formData.sidebar,
|
||||
textBase: formData.textBase,
|
||||
textSecondary: formData.textSecondary,
|
||||
},
|
||||
})
|
||||
.then((res) => {
|
||||
mutateUser((prevData) => {
|
||||
if (!prevData) return prevData;
|
||||
return { ...prevData, user: res };
|
||||
}, false);
|
||||
applyTheme(formData.palette, darkPalette);
|
||||
setTheme("custom");
|
||||
})
|
||||
.catch((err) => console.log(err));
|
||||
};
|
||||
|
||||
const handleUpdateTheme = async (formData: any) => {
|
||||
await handleFormSubmit({ ...formData, darkPalette });
|
||||
|
||||
reset({
|
||||
...defaultValues,
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
reset({
|
||||
...defaultValues,
|
||||
...preLoadedData,
|
||||
});
|
||||
}, [preLoadedData, reset]);
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit(handleUpdateTheme)}>
|
||||
<div className="space-y-5">
|
||||
<h3 className="text-lg font-semibold text-brand-base">Customize your theme</h3>
|
||||
<div className="space-y-4">
|
||||
<div className="grid grid-cols-1 gap-x-6 gap-y-4 sm:grid-cols-3">
|
||||
<div className="flex flex-col items-start gap-2">
|
||||
<h3 className="text-left text-base text-brand-secondary">Background</h3>
|
||||
<ColorPickerInput
|
||||
name="bgBase"
|
||||
error={errors.bgBase}
|
||||
watch={watch}
|
||||
setValue={setValue}
|
||||
register={register}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col items-start gap-2">
|
||||
<h3 className="text-left text-base text-brand-secondary">Background surface 1</h3>
|
||||
<ColorPickerInput
|
||||
name="bgSurface1"
|
||||
error={errors.bgSurface1}
|
||||
watch={watch}
|
||||
setValue={setValue}
|
||||
register={register}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col items-start gap-2">
|
||||
<h3 className="text-left text-base text-brand-secondary">Background surface 2</h3>
|
||||
<ColorPickerInput
|
||||
name="bgSurface2"
|
||||
error={errors.bgSurface2}
|
||||
watch={watch}
|
||||
setValue={setValue}
|
||||
register={register}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col items-start gap-2">
|
||||
<h3 className="text-left text-base text-brand-secondary">Border</h3>
|
||||
<ColorPickerInput
|
||||
name="border"
|
||||
error={errors.border}
|
||||
watch={watch}
|
||||
setValue={setValue}
|
||||
register={register}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col items-start gap-2">
|
||||
<h3 className="text-left text-base text-brand-secondary">Sidebar</h3>
|
||||
<ColorPickerInput
|
||||
name="sidebar"
|
||||
error={errors.sidebar}
|
||||
watch={watch}
|
||||
setValue={setValue}
|
||||
register={register}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col items-start gap-2">
|
||||
<h3 className="text-left text-base text-brand-secondary">Accent</h3>
|
||||
<ColorPickerInput
|
||||
name="accent"
|
||||
error={errors.accent}
|
||||
watch={watch}
|
||||
setValue={setValue}
|
||||
register={register}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col items-start gap-2">
|
||||
<h3 className="text-left text-base text-brand-secondary">Text primary</h3>
|
||||
<ColorPickerInput
|
||||
name="textBase"
|
||||
error={errors.textBase}
|
||||
watch={watch}
|
||||
setValue={setValue}
|
||||
register={register}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col items-start gap-2">
|
||||
<h3 className="text-left text-base text-brand-secondary">Text secondary</h3>
|
||||
<ColorPickerInput
|
||||
name="textSecondary"
|
||||
error={errors.textSecondary}
|
||||
watch={watch}
|
||||
setValue={setValue}
|
||||
register={register}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-5 flex justify-end gap-2">
|
||||
<PrimaryButton type="submit" loading={isSubmitting}>
|
||||
{isSubmitting ? "Creating Theme..." : "Set Theme"}
|
||||
</PrimaryButton>
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
|
|
@ -5,10 +5,8 @@ export * from "./gantt-chart-view";
|
|||
export * from "./list-view";
|
||||
export * from "./modals";
|
||||
export * from "./spreadsheet-view";
|
||||
export * from "./theme";
|
||||
export * from "./sidebar";
|
||||
export * from "./issues-view";
|
||||
export * from "./image-picker-popover";
|
||||
export * from "./feeds";
|
||||
export * from "./theme-switch";
|
||||
export * from "./custom-theme-selector";
|
||||
export * from "./color-picker-input";
|
||||
|
|
|
|||
|
|
@ -16,9 +16,11 @@ import { Popover, Transition } from "@headlessui/react";
|
|||
import { Input } from "components/ui";
|
||||
// icons
|
||||
import { ColorPickerIcon } from "components/icons";
|
||||
// types
|
||||
import { ICustomTheme } from "types";
|
||||
|
||||
type Props = {
|
||||
name: string;
|
||||
name: keyof ICustomTheme;
|
||||
watch: UseFormWatch<any>;
|
||||
setValue: UseFormSetValue<any>;
|
||||
error: FieldError | Merge<FieldError, FieldErrorsImpl<any>> | undefined;
|
||||
|
|
@ -31,31 +33,25 @@ export const ColorPickerInput: React.FC<Props> = ({ name, watch, setValue, error
|
|||
setValue(name, hex);
|
||||
};
|
||||
|
||||
const getColorText = (colorName: string) => {
|
||||
const getColorText = (colorName: keyof ICustomTheme) => {
|
||||
switch (colorName) {
|
||||
case "accent":
|
||||
return "Accent";
|
||||
case "bgBase":
|
||||
case "background":
|
||||
return "Background";
|
||||
case "bgSurface1":
|
||||
return "Background surface 1";
|
||||
case "bgSurface2":
|
||||
return "Background surface 2";
|
||||
case "border":
|
||||
return "Border";
|
||||
case "sidebar":
|
||||
return "Sidebar";
|
||||
case "textBase":
|
||||
return "Text primary";
|
||||
case "textSecondary":
|
||||
return "Text secondary";
|
||||
case "text":
|
||||
return "Text";
|
||||
case "primary":
|
||||
return "Primary(Theme)";
|
||||
case "sidebarBackground":
|
||||
return "Sidebar Background";
|
||||
case "sidebarText":
|
||||
return "Sidebar Text";
|
||||
default:
|
||||
return "Color";
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="relative ">
|
||||
<div className="relative">
|
||||
<Input
|
||||
id={name}
|
||||
name={name}
|
||||
167
apps/app/components/core/theme/custom-theme-selector.tsx
Normal file
167
apps/app/components/core/theme/custom-theme-selector.tsx
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
import React, { useEffect, useState } from "react";
|
||||
|
||||
import { useTheme } from "next-themes";
|
||||
|
||||
import { useForm } from "react-hook-form";
|
||||
|
||||
// hooks
|
||||
import useUser from "hooks/use-user";
|
||||
// ui
|
||||
import { PrimaryButton } from "components/ui";
|
||||
import { ColorPickerInput } from "components/core";
|
||||
// services
|
||||
import userService from "services/user.service";
|
||||
// helper
|
||||
import { applyTheme } from "helpers/theme.helper";
|
||||
// types
|
||||
import { ICustomTheme } from "types";
|
||||
|
||||
type Props = {
|
||||
preLoadedData?: Partial<ICustomTheme> | null;
|
||||
};
|
||||
|
||||
const defaultValues: ICustomTheme = {
|
||||
background: "#fff7f7",
|
||||
text: "#ffc9c9",
|
||||
primary: "#fe5050",
|
||||
sidebarBackground: "#ffffff",
|
||||
sidebarText: "#000000",
|
||||
darkPalette: false,
|
||||
palette: "",
|
||||
};
|
||||
|
||||
export const CustomThemeSelector: React.FC<Props> = ({ preLoadedData }) => {
|
||||
const [darkPalette, setDarkPalette] = useState(false);
|
||||
|
||||
const {
|
||||
register,
|
||||
formState: { errors, isSubmitting },
|
||||
handleSubmit,
|
||||
watch,
|
||||
setValue,
|
||||
reset,
|
||||
} = useForm<ICustomTheme>({
|
||||
defaultValues,
|
||||
});
|
||||
|
||||
const { setTheme } = useTheme();
|
||||
const { mutateUser } = useUser();
|
||||
|
||||
const handleFormSubmit = async (formData: ICustomTheme) => {
|
||||
const payload: ICustomTheme = {
|
||||
background: formData.background,
|
||||
text: formData.text,
|
||||
primary: formData.primary,
|
||||
sidebarBackground: formData.sidebarBackground,
|
||||
sidebarText: formData.sidebarText,
|
||||
darkPalette: darkPalette,
|
||||
palette: `${formData.background},${formData.text},${formData.primary},${formData.sidebarBackground},${formData.sidebarText}`,
|
||||
};
|
||||
|
||||
await userService
|
||||
.updateUser({
|
||||
theme: payload,
|
||||
})
|
||||
.then((res) => {
|
||||
mutateUser((prevData) => {
|
||||
if (!prevData) return prevData;
|
||||
|
||||
return { ...prevData, ...res };
|
||||
}, false);
|
||||
|
||||
applyTheme(payload.palette, darkPalette);
|
||||
setTheme("custom");
|
||||
})
|
||||
.catch((err) => console.log(err));
|
||||
};
|
||||
|
||||
const handleUpdateTheme = async (formData: any) => {
|
||||
await handleFormSubmit({ ...formData, darkPalette });
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
reset({
|
||||
...defaultValues,
|
||||
...preLoadedData,
|
||||
});
|
||||
}, [preLoadedData, reset]);
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit(handleUpdateTheme)}>
|
||||
<div className="space-y-5">
|
||||
<h3 className="text-lg font-semibold text-brand-base">Customize your theme</h3>
|
||||
<div className="space-y-4">
|
||||
<div className="grid grid-cols-1 gap-x-6 gap-y-4 sm:grid-cols-2 md:grid-cols-3">
|
||||
<div className="flex flex-col items-start gap-2">
|
||||
<h3 className="text-left text-sm font-medium text-brand-secondary">
|
||||
Background color
|
||||
</h3>
|
||||
<ColorPickerInput
|
||||
name="background"
|
||||
error={errors.background}
|
||||
watch={watch}
|
||||
setValue={setValue}
|
||||
register={register}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col items-start gap-2">
|
||||
<h3 className="text-left text-sm font-medium text-brand-secondary">Text color</h3>
|
||||
<ColorPickerInput
|
||||
name="text"
|
||||
error={errors.text}
|
||||
watch={watch}
|
||||
setValue={setValue}
|
||||
register={register}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col items-start gap-2">
|
||||
<h3 className="text-left text-sm font-medium text-brand-secondary">
|
||||
Primary(Theme) color
|
||||
</h3>
|
||||
<ColorPickerInput
|
||||
name="primary"
|
||||
error={errors.primary}
|
||||
watch={watch}
|
||||
setValue={setValue}
|
||||
register={register}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col items-start gap-2">
|
||||
<h3 className="text-left text-sm font-medium text-brand-secondary">
|
||||
Sidebar background color
|
||||
</h3>
|
||||
<ColorPickerInput
|
||||
name="sidebarBackground"
|
||||
error={errors.sidebarBackground}
|
||||
watch={watch}
|
||||
setValue={setValue}
|
||||
register={register}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col items-start gap-2">
|
||||
<h3 className="text-left text-sm font-medium text-brand-secondary">
|
||||
Sidebar text color
|
||||
</h3>
|
||||
<ColorPickerInput
|
||||
name="sidebarText"
|
||||
error={errors.sidebarText}
|
||||
watch={watch}
|
||||
setValue={setValue}
|
||||
register={register}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-5 flex justify-end gap-2">
|
||||
<PrimaryButton type="submit" loading={isSubmitting}>
|
||||
{isSubmitting ? "Creating Theme..." : "Set Theme"}
|
||||
</PrimaryButton>
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
3
apps/app/components/core/theme/index.ts
Normal file
3
apps/app/components/core/theme/index.ts
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
export * from "./color-picker-input";
|
||||
export * from "./custom-theme-selector";
|
||||
export * from "./theme-switch";
|
||||
|
|
@ -6,8 +6,6 @@ import { useTheme } from "next-themes";
|
|||
import { THEMES_OBJ } from "constants/themes";
|
||||
// ui
|
||||
import { CustomSelect } from "components/ui";
|
||||
// helper
|
||||
import { applyTheme } from "helpers/theme.helper";
|
||||
// types
|
||||
import { ICustomTheme, IUser } from "types";
|
||||
|
||||
|
|
@ -79,19 +77,14 @@ export const ThemeSwitch: React.FC<Props> = ({
|
|||
if (!customThemeSelectorOptions) setCustomThemeSelectorOptions(true);
|
||||
} else {
|
||||
if (customThemeSelectorOptions) setCustomThemeSelectorOptions(false);
|
||||
const cssVars = [
|
||||
"--color-bg-base",
|
||||
"--color-bg-surface-1",
|
||||
"--color-bg-surface-2",
|
||||
|
||||
"--color-border",
|
||||
"--color-bg-sidebar",
|
||||
"--color-accent",
|
||||
|
||||
"--color-text-base",
|
||||
"--color-text-secondary",
|
||||
];
|
||||
cssVars.forEach((cssVar) => document.documentElement.style.removeProperty(cssVar));
|
||||
for (let i = 10; i <= 900; i >= 100 ? (i += 100) : (i += 10)) {
|
||||
document.documentElement.style.removeProperty(`--color-background-${i}`);
|
||||
document.documentElement.style.removeProperty(`--color-text-${i}`);
|
||||
document.documentElement.style.removeProperty(`--color-primary-${i}`);
|
||||
document.documentElement.style.removeProperty(`--color-sidebar-background-${i}`);
|
||||
document.documentElement.style.removeProperty(`--color-sidebar-text-${i}`);
|
||||
}
|
||||
}
|
||||
setTheme(value);
|
||||
document.documentElement.style.setProperty("color-scheme", type);
|
||||
Loading…
Add table
Add a link
Reference in a new issue