aboutsummaryrefslogtreecommitdiff
path: root/FrontEnd/src
diff options
context:
space:
mode:
Diffstat (limited to 'FrontEnd/src')
-rw-r--r--FrontEnd/src/app/index.sass5
-rw-r--r--FrontEnd/src/app/index.tsx2
-rw-r--r--FrontEnd/src/app/palette.ts111
-rw-r--r--FrontEnd/src/app/views/common/common.sass2
-rw-r--r--FrontEnd/src/app/views/timeline-common/TimelinePageTemplate.tsx7
-rw-r--r--FrontEnd/src/app/views/timeline-common/timeline-common.sass20
6 files changed, 128 insertions, 19 deletions
diff --git a/FrontEnd/src/app/index.sass b/FrontEnd/src/app/index.sass
index 19c57953..348219eb 100644
--- a/FrontEnd/src/app/index.sass
+++ b/FrontEnd/src/app/index.sass
@@ -15,11 +15,6 @@
@import './views/admin/admin'
-:root
- --tl-primary-color: #007bff
- --tl-text-on-primary-color: #ffffff
- --tl-text-inactive-on-primary-color: rgb(255 255 255 / 75%)
-
small
line-height: 1.2
diff --git a/FrontEnd/src/app/index.tsx b/FrontEnd/src/app/index.tsx
index 00a75a4a..64a2cd1f 100644
--- a/FrontEnd/src/app/index.tsx
+++ b/FrontEnd/src/app/index.tsx
@@ -12,4 +12,6 @@ import "./i18n";
import App from "./App";
+import "./palette";
+
ReactDOM.render(<App />, document.getElementById("app"));
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);
+ };
+}
diff --git a/FrontEnd/src/app/views/common/common.sass b/FrontEnd/src/app/views/common/common.sass
index fd0beb75..3561c3da 100644
--- a/FrontEnd/src/app/views/common/common.sass
+++ b/FrontEnd/src/app/views/common/common.sass
@@ -46,7 +46,7 @@
background-color: var(--tl-primary-color)
a
- color: var(--tl-text-inactive-on-primary-color)
+ color: var(--tl-text-on-primary-inactive-color)
text-decoration: none
margin: 0 1em
diff --git a/FrontEnd/src/app/views/timeline-common/TimelinePageTemplate.tsx b/FrontEnd/src/app/views/timeline-common/TimelinePageTemplate.tsx
index 5b6dfa9c..baaad139 100644
--- a/FrontEnd/src/app/views/timeline-common/TimelinePageTemplate.tsx
+++ b/FrontEnd/src/app/views/timeline-common/TimelinePageTemplate.tsx
@@ -11,6 +11,7 @@ import Timeline from "./Timeline";
import TimelinePostEdit from "./TimelinePostEdit";
import useReverseScrollPositionRemember from "@/utilities/useReverseScrollPositionRemember";
+import { generatePalette, setPalette } from "@/palette";
export interface TimelinePageCardProps {
timeline: HttpTimelineInfo;
@@ -75,6 +76,12 @@ const TimelinePageTemplate: React.FC<TimelinePageTemplateProps> = (props) => {
};
}, [timelineName, reloadKey]);
+ React.useEffect(() => {
+ if (timeline != null && timeline.color != null) {
+ return setPalette(generatePalette({ primary: timeline.color }));
+ }
+ }, [timeline]);
+
const [bottomSpaceHeight, setBottomSpaceHeight] = React.useState<number>(0);
const [timelineReloadKey, setTimelineReloadKey] = React.useState<number>(0);
diff --git a/FrontEnd/src/app/views/timeline-common/timeline-common.sass b/FrontEnd/src/app/views/timeline-common/timeline-common.sass
index 3734ba25..0b0bd24d 100644
--- a/FrontEnd/src/app/views/timeline-common/timeline-common.sass
+++ b/FrontEnd/src/app/views/timeline-common/timeline-common.sass
@@ -9,28 +9,20 @@
$timeline-line-width: 7px
$timeline-line-node-radius: 18px
-$timeline-line-color: $primary
-$timeline-line-color-current: #36c2e6
+$timeline-line-color: var(--tl-primary-color)
+$timeline-line-color-current: var(--tl-primary-enhance-color)
@keyframes timeline-line-node-noncurrent
- from
- background: $timeline-line-color
-
to
- background: color.adjust($timeline-line-color, $lightness: +10%)
- box-shadow: 0 0 20px 3px color.adjust($timeline-line-color, $lightness: +10%, $alpha: -0.1)
+ box-shadow: 0 0 20px 3px var(--tl-primary-lighter-color)
@keyframes timeline-line-node-current
- from
- background: $timeline-line-color-current
-
to
- background: color.adjust($timeline-line-color-current, $lightness: +10%)
- box-shadow: 0 0 20px 3px color.adjust($timeline-line-color-current, $lightness: +10%, $alpha: -0.1)
+ box-shadow: 0 0 20px 3px var(--tl-primary-enhance-lighter-color)
@keyframes timeline-line-node-loading
to
- box-shadow: 0 0 20px 3px color.adjust($timeline-line-color, $lightness: +20%)
+ box-shadow: 0 0 20px 3px var(--tl-primary-lighter-color)
@keyframes timeline-line-node-loading-edge
from
@@ -100,6 +92,7 @@ $timeline-line-color-current: #36c2e6
width: $timeline-line-node-radius + 2
height: $timeline-line-node-radius + 2
position: absolute
+ background: $timeline-line-color
left: -1px
top: -1px
border-radius: 50%
@@ -126,6 +119,7 @@ $timeline-line-color-current: #36c2e6
&.end
background: $timeline-line-color-current
.node
+ background: $timeline-line-color-current
animation-name: timeline-line-node-current
&.loading