phase-5: add runtime theme and branding engine services

This commit is contained in:
sdarbinyan
2026-07-03 01:33:52 +04:00
parent 3b2c1048c9
commit 7dca7fee7d
5 changed files with 120 additions and 0 deletions

View File

@@ -0,0 +1,36 @@
import { DOCUMENT } from '@angular/common';
import { Injectable, inject } from '@angular/core';
import { take } from 'rxjs/operators';
import { ConfigService } from '../../core/config/config.service';
import { BrandingConfig } from '../../shared/models/config';
@Injectable({ providedIn: 'root' })
export class BrandingEngineService {
private readonly document = inject(DOCUMENT);
private readonly configService = inject(ConfigService);
initialize(): void {
this.configService.loadBootstrap().pipe(take(1)).subscribe({
next: (bootstrap) => this.applyBranding(bootstrap.branding),
error: () => {
// Branding bootstrapping should not break app startup.
}
});
}
applyBranding(branding: BrandingConfig): void {
const root = this.document.documentElement;
root.style.setProperty('--brand-logo-url', `url('${branding.logoUrl}')`);
root.style.setProperty('--brand-logo-compact-url', `url('${branding.logoCompactUrl ?? branding.logoUrl}')`);
const existing = this.document.querySelector("link[rel='icon']") as HTMLLinkElement | null;
if (existing) {
existing.href = branding.faviconUrl;
} else {
const link = this.document.createElement('link');
link.rel = 'icon';
link.href = branding.faviconUrl;
this.document.head.appendChild(link);
}
}
}

View File

@@ -0,0 +1,34 @@
import { DOCUMENT } from '@angular/common';
import { Injectable, inject } from '@angular/core';
import { take } from 'rxjs/operators';
import { ConfigService } from '../../core/config/config.service';
import { ThemeConfig } from '../../shared/models/config';
import { mapThemeConfigToCssVariables } from '../mappers/theme-css-vars.mapper';
@Injectable({ providedIn: 'root' })
export class ThemeEngineService {
private readonly document = inject(DOCUMENT);
private readonly configService = inject(ConfigService);
initialize(): void {
this.configService.loadBootstrap().pipe(take(1)).subscribe({
next: (bootstrap) => this.applyTheme(bootstrap.theme),
error: () => {
// Theme bootstrapping should not break app startup.
}
});
}
applyTheme(theme: ThemeConfig): void {
const variables = mapThemeConfigToCssVariables(theme);
const root = this.document.documentElement;
for (const [name, value] of Object.entries(variables)) {
root.style.setProperty(name, value);
}
root.setAttribute('data-theme-id', theme.themeId);
root.setAttribute('data-theme-mode', theme.mode);
root.setAttribute('data-icon-set', theme.iconSet);
}
}