aboutsummaryrefslogtreecommitdiff
path: root/FrontEnd/src/app/palette.ts
diff options
context:
space:
mode:
authorcrupest <crupest@outlook.com>2021-05-06 20:57:18 +0800
committercrupest <crupest@outlook.com>2021-05-06 20:57:18 +0800
commit9b567399b4da6a2ef787160667105907ecdea41d (patch)
tree29dfacb012f929424dc453f8ba04034f57d91296 /FrontEnd/src/app/palette.ts
parentd7f32f764c28a923279a8d2133c471f2cf4afba1 (diff)
downloadtimeline-9b567399b4da6a2ef787160667105907ecdea41d.tar.gz
timeline-9b567399b4da6a2ef787160667105907ecdea41d.tar.bz2
timeline-9b567399b4da6a2ef787160667105907ecdea41d.zip
...
Diffstat (limited to 'FrontEnd/src/app/palette.ts')
-rw-r--r--FrontEnd/src/app/palette.ts111
1 files changed, 111 insertions, 0 deletions
diff --git a/FrontEnd/src/app/palette.ts b/FrontEnd/src/app/palette.ts
new file mode 100644
index 00000000..63a70c4e
--- /dev/null
+++ b/FrontEnd/src/app/palette.ts
@@ -0,0 +1,111 @@
+import Color from "color";
+import { BehaviorSubject, Observable } from "rxjs";
+
+function lightenBy(color: Color, ratio: number): Color {
+ const lightness = color.lightness();
+ return color.lightness(lightness + (100 - lightness) * ratio);
+}
+
+function darkenBy(color: Color, ratio: number): Color {
+ const lightness = color.lightness();
+ return color.lightness(lightness - lightness * ratio);
+}
+
+export interface PaletteColor {
+ color: string;
+ inactive: string;
+ lighter: string;
+ darker: string;
+ [key: string]: string;
+}
+
+export interface Palette {
+ primary: PaletteColor;
+ primaryEnhance: PaletteColor;
+ secondary: PaletteColor;
+ textPrimary: PaletteColor;
+ textOnPrimary: PaletteColor;
+ danger: PaletteColor;
+ success: PaletteColor;
+ [key: string]: PaletteColor;
+}
+
+export function generatePaletteColor(color: string): PaletteColor {
+ const c = Color(color);
+ return {
+ color: c.toString(),
+ inactive: (c.isLight() ? darkenBy(c, 0.2) : lightenBy(c, 0.2)).toString(),
+ lighter: lightenBy(c, 0.1).fade(0.1).toString(),
+ darker: darkenBy(c, 0.1).toString(),
+ };
+}
+
+export function generatePalette(options: {
+ primary: string;
+ primaryEnhance?: string;
+ secondary?: string;
+}): Palette {
+ const { primary, primaryEnhance, secondary } = options;
+ const p = Color(primary);
+ const pe =
+ primaryEnhance == null
+ ? lightenBy(p, 0.3).saturate(0.3)
+ : Color(primaryEnhance);
+ const s = secondary == null ? p.rotate(90) : Color(secondary);
+
+ return {
+ primary: generatePaletteColor(p.toString()),
+ primaryEnhance: generatePaletteColor(pe.toString()),
+ secondary: generatePaletteColor(s.toString()),
+ textPrimary: generatePaletteColor("#111111"),
+ textOnPrimary: generatePaletteColor(p.isLight() ? "black" : "white"),
+ danger: generatePaletteColor("red"),
+ success: generatePaletteColor("green"),
+ };
+}
+
+export function generatePaletteCSS(palette: Palette): string {
+ function toSnakeCase(s: string): string {
+ return s.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`);
+ }
+
+ const colors: [string, string][] = [];
+ for (const paletteColorName in palette) {
+ const paletteColor = palette[paletteColorName];
+ for (const variant in paletteColor) {
+ let key = `--tl-${toSnakeCase(paletteColorName)}`;
+ if (variant !== "color") key += `-${toSnakeCase(variant)}`;
+ key += "-color";
+ colors.push([key, paletteColor[variant]]);
+ }
+ }
+
+ return `:root {${colors
+ .map(([key, color]) => `${key} : ${color};`)
+ .join("")}}`;
+}
+
+const paletteSubject: BehaviorSubject<Palette> = new BehaviorSubject<Palette>(
+ generatePalette({ primary: "#007bff" })
+);
+
+export const palette$: Observable<Palette> = paletteSubject.asObservable();
+
+palette$.subscribe((palette) => {
+ let styleTag = document.getElementById("timeline-palette-css");
+ if (styleTag == null) {
+ styleTag = document.createElement("style");
+ document.head.append(styleTag);
+ }
+ styleTag.innerHTML = generatePaletteCSS(palette);
+});
+
+export function setPalette(palette: Palette): () => void {
+ const old = paletteSubject.value;
+
+ paletteSubject.next(palette);
+
+ return () => {
+ paletteSubject.next(old);
+ };
+}