mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
207 lines
6.5 KiB
TypeScript
207 lines
6.5 KiB
TypeScript
|
// mobx
|
||
|
import { action, observable, makeObservable } from "mobx";
|
||
|
|
||
|
type TRgb = { r: number; g: number; b: number };
|
||
|
|
||
|
type TShades = {
|
||
|
10: TRgb;
|
||
|
20: TRgb;
|
||
|
30: TRgb;
|
||
|
40: TRgb;
|
||
|
50: TRgb;
|
||
|
60: TRgb;
|
||
|
70: TRgb;
|
||
|
80: TRgb;
|
||
|
90: TRgb;
|
||
|
100: TRgb;
|
||
|
200: TRgb;
|
||
|
300: TRgb;
|
||
|
400: TRgb;
|
||
|
500: TRgb;
|
||
|
600: TRgb;
|
||
|
700: TRgb;
|
||
|
800: TRgb;
|
||
|
900: TRgb;
|
||
|
};
|
||
|
|
||
|
export interface IThemeStore {
|
||
|
// observables
|
||
|
theme: string | undefined;
|
||
|
sidebarCollapsed: boolean | undefined;
|
||
|
// actions
|
||
|
toggleSidebar: (collapsed?: boolean) => void;
|
||
|
setTheme: (theme: any) => void;
|
||
|
}
|
||
|
|
||
|
export class ThemeStore implements IThemeStore {
|
||
|
// observables
|
||
|
sidebarCollapsed: boolean | undefined = undefined;
|
||
|
theme: string | undefined = undefined;
|
||
|
themePallette: any | undefined = undefined;
|
||
|
|
||
|
constructor() {
|
||
|
makeObservable(this, {
|
||
|
// observable
|
||
|
sidebarCollapsed: observable.ref,
|
||
|
theme: observable.ref,
|
||
|
// action
|
||
|
toggleSidebar: action,
|
||
|
setTheme: action,
|
||
|
// computed
|
||
|
});
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Toggle the sidebar collapsed state
|
||
|
* @param collapsed
|
||
|
*/
|
||
|
toggleSidebar = (collapsed?: boolean) => {
|
||
|
if (collapsed === undefined) {
|
||
|
this.sidebarCollapsed = !this.sidebarCollapsed;
|
||
|
} else {
|
||
|
this.sidebarCollapsed = collapsed;
|
||
|
}
|
||
|
localStorage.setItem(
|
||
|
"god_mode_sidebar_collapsed",
|
||
|
this.sidebarCollapsed.toString()
|
||
|
);
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Sets the user theme and applies it to the platform
|
||
|
* @param _theme
|
||
|
*/
|
||
|
setTheme = async (_theme: { theme: any }) => {
|
||
|
try {
|
||
|
const currentTheme: string = _theme?.theme?.theme?.toString();
|
||
|
// updating the local storage theme value
|
||
|
localStorage.setItem("theme", currentTheme);
|
||
|
// updating the mobx theme value
|
||
|
this.theme = currentTheme;
|
||
|
// applying the theme to platform if the selected theme is custom
|
||
|
if (currentTheme === "custom" && this.themePallette) {
|
||
|
this.applyTheme(
|
||
|
this.themePallette?.theme?.palette !== ",,,,"
|
||
|
? this.themePallette?.theme?.palette
|
||
|
: "#0d101b,#c5c5c5,#3f76ff,#0d101b,#c5c5c5",
|
||
|
this.themePallette?.theme?.darkPalette
|
||
|
);
|
||
|
} else this.unsetCustomCssVariables();
|
||
|
} catch (error) {
|
||
|
console.error("setting user theme error", error);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
calculateShades = (hexValue: string): TShades => {
|
||
|
const shades: Partial<TShades> = {};
|
||
|
const { r, g, b } = this.hexToRgb(hexValue);
|
||
|
|
||
|
const convertHexToSpecificShade = (shade: number): TRgb => {
|
||
|
if (shade <= 100) {
|
||
|
const decimalValue = (100 - shade) / 100;
|
||
|
|
||
|
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 {
|
||
|
r: newR,
|
||
|
g: newG,
|
||
|
b: newB,
|
||
|
};
|
||
|
} else {
|
||
|
const decimalValue = 1 - Math.ceil((shade - 100) / 100) / 10;
|
||
|
|
||
|
const newR = Math.ceil(r * decimalValue);
|
||
|
const newG = Math.ceil(g * decimalValue);
|
||
|
const newB = Math.ceil(b * decimalValue);
|
||
|
|
||
|
return {
|
||
|
r: newR,
|
||
|
g: newG,
|
||
|
b: newB,
|
||
|
};
|
||
|
}
|
||
|
};
|
||
|
|
||
|
for (let i = 10; i <= 900; i >= 100 ? (i += 100) : (i += 10))
|
||
|
shades[i as keyof TShades] = convertHexToSpecificShade(i);
|
||
|
|
||
|
return shades as TShades;
|
||
|
};
|
||
|
|
||
|
unsetCustomCssVariables = () => {
|
||
|
for (let i = 10; i <= 900; i >= 100 ? (i += 100) : (i += 10)) {
|
||
|
const dom = document.querySelector<HTMLElement>("[data-theme='custom']");
|
||
|
dom?.style.removeProperty(`--color-background-${i}`);
|
||
|
dom?.style.removeProperty(`--color-text-${i}`);
|
||
|
dom?.style.removeProperty(`--color-border-${i}`);
|
||
|
dom?.style.removeProperty(`--color-primary-${i}`);
|
||
|
dom?.style.removeProperty(`--color-sidebar-background-${i}`);
|
||
|
dom?.style.removeProperty(`--color-sidebar-text-${i}`);
|
||
|
dom?.style.removeProperty(`--color-sidebar-border-${i}`);
|
||
|
dom?.style.removeProperty("--color-scheme");
|
||
|
}
|
||
|
};
|
||
|
|
||
|
applyTheme = (palette: string, isDarkPalette: boolean) => {
|
||
|
if (!palette) return;
|
||
|
const dom = document?.querySelector<HTMLElement>("[data-theme='custom']");
|
||
|
// palette: [bg, text, primary, sidebarBg, sidebarText]
|
||
|
const values: string[] = palette.split(",");
|
||
|
values.push(isDarkPalette ? "dark" : "light");
|
||
|
|
||
|
const bgShades = this.calculateShades(values[0]);
|
||
|
const textShades = this.calculateShades(values[1]);
|
||
|
const primaryShades = this.calculateShades(values[2]);
|
||
|
const sidebarBackgroundShades = this.calculateShades(values[3]);
|
||
|
const sidebarTextShades = this.calculateShades(values[4]);
|
||
|
|
||
|
for (let i = 10; i <= 900; i >= 100 ? (i += 100) : (i += 10)) {
|
||
|
const shade = i as keyof TShades;
|
||
|
|
||
|
const bgRgbValues = `${bgShades[shade].r}, ${bgShades[shade].g}, ${bgShades[shade].b}`;
|
||
|
const textRgbValues = `${textShades[shade].r}, ${textShades[shade].g}, ${textShades[shade].b}`;
|
||
|
const primaryRgbValues = `${primaryShades[shade].r}, ${primaryShades[shade].g}, ${primaryShades[shade].b}`;
|
||
|
const sidebarBackgroundRgbValues = `${sidebarBackgroundShades[shade].r}, ${sidebarBackgroundShades[shade].g}, ${sidebarBackgroundShades[shade].b}`;
|
||
|
const sidebarTextRgbValues = `${sidebarTextShades[shade].r}, ${sidebarTextShades[shade].g}, ${sidebarTextShades[shade].b}`;
|
||
|
|
||
|
dom?.style.setProperty(`--color-background-${shade}`, bgRgbValues);
|
||
|
dom?.style.setProperty(`--color-text-${shade}`, textRgbValues);
|
||
|
dom?.style.setProperty(`--color-primary-${shade}`, primaryRgbValues);
|
||
|
dom?.style.setProperty(
|
||
|
`--color-sidebar-background-${shade}`,
|
||
|
sidebarBackgroundRgbValues
|
||
|
);
|
||
|
dom?.style.setProperty(
|
||
|
`--color-sidebar-text-${shade}`,
|
||
|
sidebarTextRgbValues
|
||
|
);
|
||
|
|
||
|
if (i >= 100 && i <= 400) {
|
||
|
const borderShade =
|
||
|
i === 100 ? 70 : i === 200 ? 80 : i === 300 ? 90 : 100;
|
||
|
|
||
|
dom?.style.setProperty(
|
||
|
`--color-border-${shade}`,
|
||
|
`${bgShades[borderShade].r}, ${bgShades[borderShade].g}, ${bgShades[borderShade].b}`
|
||
|
);
|
||
|
dom?.style.setProperty(
|
||
|
`--color-sidebar-border-${shade}`,
|
||
|
`${sidebarBackgroundShades[borderShade].r}, ${sidebarBackgroundShades[borderShade].g}, ${sidebarBackgroundShades[borderShade].b}`
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
dom?.style.setProperty("--color-scheme", values[5]);
|
||
|
};
|
||
|
|
||
|
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 };
|
||
|
};
|
||
|
}
|