diff options
Diffstat (limited to 'FrontEnd/src')
-rw-r--r-- | FrontEnd/src/app/index.sass | 5 | ||||
-rw-r--r-- | FrontEnd/src/app/index.tsx | 2 | ||||
-rw-r--r-- | FrontEnd/src/app/palette.ts | 111 | ||||
-rw-r--r-- | FrontEnd/src/app/views/common/common.sass | 2 | ||||
-rw-r--r-- | FrontEnd/src/app/views/timeline-common/TimelinePageTemplate.tsx | 7 | ||||
-rw-r--r-- | FrontEnd/src/app/views/timeline-common/timeline-common.sass | 20 |
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 |