diff --git a/web/src/admin/AdminInterface.ts b/web/src/admin/AdminInterface.ts index 3c167a04c..ba19cb4fa 100644 --- a/web/src/admin/AdminInterface.ts +++ b/web/src/admin/AdminInterface.ts @@ -70,7 +70,11 @@ export class AdminInterface extends Interface { display: none; } .pf-c-page { - background-color: transparent !important; + background-color: var(--pf-c-page--BackgroundColor) !important; + } + /* Global page background colour */ + :host([theme="dark"]) .pf-c-page { + --pf-c-page--BackgroundColor: var(--ak-dark-background); } `, ]; diff --git a/web/src/common/constants.ts b/web/src/common/constants.ts index 1ed19bfa5..7b5eb6cc1 100644 --- a/web/src/common/constants.ts +++ b/web/src/common/constants.ts @@ -17,6 +17,7 @@ export const EVENT_FLOW_ADVANCE = "ak-flow-advance"; export const EVENT_LOCALE_CHANGE = "ak-locale-change"; export const EVENT_REQUEST_POST = "ak-request-post"; export const EVENT_MESSAGE = "ak-message"; +export const EVENT_THEME_CHANGE = "ak-theme-change"; export const WS_MSG_TYPE_MESSAGE = "message"; export const WS_MSG_TYPE_REFRESH = "refresh"; diff --git a/web/src/elements/Base.ts b/web/src/elements/Base.ts index d8343bb9e..b866c0623 100644 --- a/web/src/elements/Base.ts +++ b/web/src/elements/Base.ts @@ -1,5 +1,4 @@ -import { EVENT_LOCALE_CHANGE } from "@goauthentik/common/constants"; -import { globalAK } from "@goauthentik/common/global"; +import { EVENT_LOCALE_CHANGE, EVENT_THEME_CHANGE } from "@goauthentik/common/constants"; import { uiConfig } from "@goauthentik/common/ui/config"; import { LitElement } from "lit"; @@ -68,6 +67,10 @@ export class AKElement extends LitElement { return root; } + async getTheme(): Promise { + return rootInterface()?.getTheme() || UiThemeEnum.Automatic; + } + async _initTheme(root: AdoptedStyleSheetsElement): Promise { // Early activate theme based on media query to prevent light flash // when dark is preferred @@ -77,7 +80,7 @@ export class AKElement extends LitElement { ? UiThemeEnum.Light : UiThemeEnum.Dark, ); - rootInterface()?._initTheme(root); + this._applyTheme(root, await this.getTheme()); } private async _initCustomCSS(root: ShadowRoot): Promise { @@ -120,33 +123,36 @@ export class AKElement extends LitElement { this._activateTheme(root, theme); } + static themeToStylesheet(theme?: UiThemeEnum): CSSStyleSheet | undefined { + if (theme === UiThemeEnum.Dark) { + return ThemeDark; + } + return undefined; + } + _activateTheme(root: AdoptedStyleSheetsElement, theme: UiThemeEnum) { - if (this._activeTheme === theme) { + console.log("activating theme", theme, this._activeTheme, this); + if (theme === this._activeTheme) { return; } // Make sure we only get to this callback once we've picked a concise theme choice this.dispatchEvent( - new CustomEvent("themeChange", { + new CustomEvent(EVENT_THEME_CHANGE, { bubbles: true, composed: true, detail: theme, }), ); - this._activeTheme = theme; this.setAttribute("theme", theme); - let stylesheet: CSSStyleSheet | undefined; - if (theme === UiThemeEnum.Dark) { - stylesheet = ThemeDark; - } - if (!stylesheet) { - return; - } - if (root.adoptedStyleSheets.indexOf(stylesheet) === -1) { + const stylesheet = AKElement.themeToStylesheet(theme); + const oldStylesheet = AKElement.themeToStylesheet(this._activeTheme); + if (stylesheet) { root.adoptedStyleSheets = [...root.adoptedStyleSheets, stylesheet]; - } else { - root.adoptedStyleSheets = root.adoptedStyleSheets.filter((v) => v !== stylesheet); } - this.requestUpdate(); + if (oldStylesheet) { + root.adoptedStyleSheets = root.adoptedStyleSheets.filter((v) => v !== oldStylesheet); + } + this._activeTheme = theme; } disconnectedCallback() { @@ -161,13 +167,8 @@ export class Interface extends AKElement { super._activateTheme(document, theme); } - async _initTheme(root: AdoptedStyleSheetsElement): Promise { - const bootstrapTheme = globalAK()?.tenant.uiTheme || UiThemeEnum.Automatic; - this._applyTheme(root, bootstrapTheme); - uiConfig().then((config) => { - if (config.theme.base) { - this._applyTheme(root, config.theme.base); - } - }); + async getTheme(): Promise { + const config = await uiConfig(); + return config.theme.base; } } diff --git a/web/src/elements/CodeMirror.ts b/web/src/elements/CodeMirror.ts index 5f0a6f2dd..f5425c4ce 100644 --- a/web/src/elements/CodeMirror.ts +++ b/web/src/elements/CodeMirror.ts @@ -13,6 +13,7 @@ import * as yamlMode from "@codemirror/legacy-modes/mode/yaml"; import { Compartment, EditorState, Extension } from "@codemirror/state"; import { oneDark } from "@codemirror/theme-one-dark"; import { EditorView, drawSelection, keymap, lineNumbers } from "@codemirror/view"; +import { EVENT_THEME_CHANGE } from "@goauthentik/common/constants"; import { AKElement } from "@goauthentik/elements/Base"; import YAML from "yaml"; @@ -128,7 +129,7 @@ export class CodeMirrorTextarea extends AKElement { } firstUpdated(): void { - this.addEventListener("themeChange", ((ev: CustomEvent) => { + this.addEventListener(EVENT_THEME_CHANGE, ((ev: CustomEvent) => { if (ev.detail === UiThemeEnum.Dark) { this.editor?.dispatch({ effects: this.theme.reconfigure(this.themeDark), diff --git a/web/src/elements/Diagram.ts b/web/src/elements/Diagram.ts index 5d8656daa..92aab2231 100644 --- a/web/src/elements/Diagram.ts +++ b/web/src/elements/Diagram.ts @@ -1,4 +1,4 @@ -import { EVENT_REFRESH } from "@goauthentik/common/constants"; +import { EVENT_REFRESH, EVENT_THEME_CHANGE } from "@goauthentik/common/constants"; import { AKElement } from "@goauthentik/elements/Base"; import "@goauthentik/elements/EmptyState"; import mermaid, { MermaidConfig } from "mermaid"; @@ -53,7 +53,7 @@ export class Diagram extends AKElement { firstUpdated(): void { if (this.handlerBound) return; window.addEventListener(EVENT_REFRESH, this.refreshHandler); - this.addEventListener("themeChange", ((ev: CustomEvent) => { + this.addEventListener(EVENT_THEME_CHANGE, ((ev: CustomEvent) => { if (ev.detail === UiThemeEnum.Dark) { this.config.theme = "dark"; } else { diff --git a/web/src/elements/charts/Chart.ts b/web/src/elements/charts/Chart.ts index 208e7df28..fb7c856ad 100644 --- a/web/src/elements/charts/Chart.ts +++ b/web/src/elements/charts/Chart.ts @@ -1,4 +1,4 @@ -import { EVENT_REFRESH } from "@goauthentik/common/constants"; +import { EVENT_REFRESH, EVENT_THEME_CHANGE } from "@goauthentik/common/constants"; import { AKElement } from "@goauthentik/elements/Base"; import "@goauthentik/elements/EmptyState"; import { @@ -93,7 +93,7 @@ export abstract class AKChart extends AKElement { super.connectedCallback(); window.addEventListener("resize", this.resizeHandler); this.addEventListener(EVENT_REFRESH, this.refreshHandler); - this.addEventListener("themeChange", ((ev: CustomEvent) => { + this.addEventListener(EVENT_THEME_CHANGE, ((ev: CustomEvent) => { if (ev.detail === UiThemeEnum.Light) { this.fontColour = FONT_COLOUR_LIGHT_MODE; } else { diff --git a/web/src/flow/FlowExecutor.ts b/web/src/flow/FlowExecutor.ts index 5d493796f..7f011d04c 100644 --- a/web/src/flow/FlowExecutor.ts +++ b/web/src/flow/FlowExecutor.ts @@ -8,7 +8,7 @@ import { globalAK } from "@goauthentik/common/global"; import { configureSentry } from "@goauthentik/common/sentry"; import { first } from "@goauthentik/common/utils"; import { WebsocketClient } from "@goauthentik/common/ws"; -import { AdoptedStyleSheetsElement, Interface } from "@goauthentik/elements/Base"; +import { Interface } from "@goauthentik/elements/Base"; import "@goauthentik/elements/LoadingOverlay"; import "@goauthentik/flow/stages/FlowErrorStage"; import "@goauthentik/flow/stages/RedirectStage"; @@ -182,9 +182,8 @@ export class FlowExecutor extends Interface implements StageHost { tenant().then((tenant) => (this.tenant = tenant)); } - async _initTheme(root: AdoptedStyleSheetsElement): Promise { - const bootstrapTheme = globalAK()?.tenant.uiTheme || UiThemeEnum.Automatic; - this._applyTheme(root, bootstrapTheme); + async getTheme(): Promise { + return globalAK()?.tenant.uiTheme || UiThemeEnum.Automatic; } submit(payload?: FlowChallengeResponseRequest): Promise {