Merge branch 'feature/theme-extension-point' of https://github.com/umbraco/Umbraco.CMS.Backoffice into feature/theme-extension-point

# Conflicts:
#	src/backoffice/backoffice.element.ts
This commit is contained in:
Niels Lyngsø
2023-02-01 13:57:10 +01:00
8 changed files with 148 additions and 193 deletions

View File

@@ -33,8 +33,8 @@ import { UmbDocumentBlueprintTreeStore } from './documents/document-blueprints/d
import { UmbDataTypeDetailStore } from './settings/data-types/data-type.detail.store';
import { UmbDataTypeTreeStore } from './settings/data-types/data-type.tree.store';
import { UmbThemeContext } from './themes/theme.context';
import { UmbLanguageStore } from './settings/languages/language.store';
import { UmbThemeService, UMB_THEME_SERVICE_CONTEXT_TOKEN } from './themes/theme.service';
import { UmbNotificationService, UMB_NOTIFICATION_SERVICE_CONTEXT_TOKEN } from '@umbraco-cms/notification';
// Domains

View File

@@ -1,11 +1,25 @@
import type { ManifestTheme } from '@umbraco-cms/models';
export const themes: Array<ManifestTheme> = [
{
type: 'theme',
alias: 'umb-light-theme',
name: 'Light',
weight: 300,
},
{
type: 'theme',
alias: 'umb-dark-theme',
name: 'Dark',
loader: () => import('./themes/dark.theme')
loader: () => new Promise((resolve) => resolve('src/backoffice/themes/themes/dark.theme.css')),
weight: 200,
},
{
type: 'theme',
alias: 'umb-high-contrast-theme',
name: 'High contrast',
loader: () => new Promise((resolve) => resolve('src/backoffice/themes/themes/high-contrast.theme.css')),
weight: 100,
},
];

View File

@@ -1,6 +1,4 @@
import { map } from 'rxjs';
//import { dark, highContrast } from './themes';
import type { CSSResult } from 'lit';
import { manifests } from './manifests';
import { UmbContextProviderController, UmbContextToken } from '@umbraco-cms/context-api';
import { StringState, UmbObserverController } from '@umbraco-cms/observable-api';
@@ -8,97 +6,63 @@ import { umbExtensionsRegistry } from '@umbraco-cms/extensions-api';
import { UmbControllerHostInterface } from '@umbraco-cms/controller';
import { ManifestTheme } from '@umbraco-cms/extensions-registry';
export interface UmbTheme {
name: string;
css: CSSResult;
}
const LOCAL_STORAGE_KEY = 'umb-theme-alias';
export class UmbThemeContext {
// TODO: Turn this into a extension type, get rid of the #themes subject and #themes observable
/*
#themes = new ArrayState(<Array<UmbTheme>>[
{
name: 'Light',
css: '',
},
]);
public readonly themes = this.#themes.asObservable();
*/
private _host: UmbControllerHostInterface;
#theme = new StringState(null);
#theme = new StringState('umb-light-theme');
public readonly theme = this.#theme.asObservable();
#styleElement: HTMLStyleElement;
private themeSubscription?: UmbObserverController;
#styleElement: HTMLLinkElement | null = null;
constructor(host: UmbControllerHostInterface) {
this._host = host;
console.log("Theme COntext")
new UmbContextProviderController(host, UMB_THEME_CONTEXT_TOKEN, this);
//TODO: Figure out how to extend this with themes from packages
//this.addTheme(dark);
//this.addTheme(highContrast);
this.#styleElement = document.createElement('style');
this.#styleElement = document.createElement('link');
this.#styleElement.setAttribute('rel', 'stylesheet');
document.head.appendChild(this.#styleElement);
const storedTheme = localStorage.getItem(LOCAL_STORAGE_KEY);
if(storedTheme) {
if (storedTheme) {
this.setThemeByAlias(storedTheme);
}
document.documentElement.insertAdjacentElement('beforeend', this.#styleElement);
}
public setThemeByAlias(themeAlias: string | null) {
public setThemeByAlias(themeAlias: string) {
this.#theme.next(themeAlias);
this.themeSubscription?.destroy();
if(themeAlias != null) {
if (themeAlias) {
localStorage.setItem(LOCAL_STORAGE_KEY, themeAlias);
this.themeSubscription = new UmbObserverController(this._host,
umbExtensionsRegistry.extensionsOfType('theme').pipe(map(
(extensions) => extensions.filter((extension) => extension.alias === themeAlias)
))
,
this.themeSubscription = new UmbObserverController(
this._host,
umbExtensionsRegistry
.extensionsOfType('theme')
.pipe(map((extensions) => extensions.filter((extension) => extension.alias === themeAlias))),
async (themes) => {
if (themes.length > 0 && themes[0].loader) {
const result = await themes[0].loader();
console.log("result from loader: ", result.default);
this.#styleElement.innerHTML = result.default;
const path = await themes[0].loader();
this.#styleElement?.setAttribute('href', path);
} else {
localStorage.removeItem(LOCAL_STORAGE_KEY);
this.#styleElement?.setAttribute('href', '');
}
// how to get CSS.
//this.#styleElement.innerHTML = "";
}
);
} else {
localStorage.removeItem(LOCAL_STORAGE_KEY);
this.#styleElement.innerHTML = "";
this.#styleElement?.setAttribute('href', '');
}
}
/*
public addTheme(theme: UmbTheme) {
this.#themes.next([...this.#themes.value, theme]);
}
*/
}
export const UMB_THEME_CONTEXT_TOKEN = new UmbContextToken<UmbThemeContext>('umbThemeContext');
// TODO: Can we do this in a smarter way:
const registerExtensions = (manifests: Array<ManifestTheme>) => {
manifests.forEach((manifest) => {

View File

@@ -0,0 +1,47 @@
:root {
--uui-color-selected: #316dca;
--uui-color-selected-emphasis: #3e79d0;
--uui-color-selected-standalone: #5b8dd7;
--uui-color-selected-contrast: #eeeeef;
--uui-color-current: #316dca;
--uui-color-current-emphasis: #3e79d0;
--uui-color-current-standalone: #5b8dd7;
--uui-color-current-contrast: #f000;
--uui-color-disabled: #434c56;
--uui-color-disabled-standalone: #545d68;
--uui-color-disabled-contrast: #fcfcfc4d;
--uui-color-header-surface: #21262e;
--uui-color-header-contrast: #eeeeef;
--uui-color-header-contrast-emphasis: #eeeeef;
--uui-color-focus: #316dca;
--uui-color-surface: #2d333b;
--uui-color-surface-alt: #373e47;
--uui-color-surface-emphasis: #434c56;
--uui-color-background: #21262e;
--uui-color-text: #eeeeef;
--uui-color-text-alt: #eeeeef;
--uui-color-interactive: #eeeeef;
--uui-color-interactive-emphasis: #eeeeef;
--uui-color-border: #434c56;
--uui-color-border-standalone: #545d68;
--uui-color-border-emphasis: #626e7b;
--uui-color-divider: #373e47;
--uui-color-divider-standalone: #434c56;
--uui-color-divider-emphasis: #545d68;
--uui-color-default: #316dca;
--uui-color-default-emphasis: #316dca;
--uui-color-default-standalone: #316dca;
--uui-color-default-contrast: #eeeeef;
--uui-color-warning: #af7c12;
--uui-color-warning-emphasis: #af7c12;
--uui-color-warning-standalone: #af7c12;
--uui-color-warning-contrast: #eeeeef;
--uui-color-danger: #ca3b37;
--uui-color-danger-emphasis: #ca3b37;
--uui-color-danger-standalone: #ca3b37;
--uui-color-danger-contrast: #eeeeef;
--uui-color-positive: #347d39;
--uui-color-positive-emphasis: #347d39;
--uui-color-positive-standalone: #347d39;
--uui-color-positive-contrast: #eeeeef;
}

View File

@@ -1,56 +0,0 @@
import { css } from 'lit';
import { UmbTheme } from '../theme.context';
// TODO: We should get this from UUI, and it should be served through an extension.
const name = 'Dark';
const cssResult = css`
:root {
--uui-color-selected: #316dca;
--uui-color-selected-emphasis: #3e79d0;
--uui-color-selected-standalone: #5b8dd7;
--uui-color-selected-contrast: #eeeeef;
--uui-color-current: #316dca;
--uui-color-current-emphasis: #3e79d0;
--uui-color-current-standalone: #5b8dd7;
--uui-color-current-contrast: #f000;
--uui-color-disabled: #434c56;
--uui-color-disabled-standalone: #545d68;
--uui-color-disabled-contrast: #fcfcfc4d;
--uui-color-header-surface: #21262e;
--uui-color-header-contrast: #eeeeef;
--uui-color-header-contrast-emphasis: #eeeeef;
--uui-color-focus: #316dca;
--uui-color-surface: #2d333b;
--uui-color-surface-alt: #373e47;
--uui-color-surface-emphasis: #434c56;
--uui-color-background: #21262e;
--uui-color-text: #eeeeef;
--uui-color-text-alt: #eeeeef;
--uui-color-interactive: #eeeeef;
--uui-color-interactive-emphasis: #eeeeef;
--uui-color-border: #434c56;
--uui-color-border-standalone: #545d68;
--uui-color-border-emphasis: #626e7b;
--uui-color-divider: #373e47;
--uui-color-divider-standalone: #434c56;
--uui-color-divider-emphasis: #545d68;
--uui-color-default: #316dca;
--uui-color-default-emphasis: #316dca;
--uui-color-default-standalone: #316dca;
--uui-color-default-contrast: #eeeeef;
--uui-color-warning: #af7c12;
--uui-color-warning-emphasis: #af7c12;
--uui-color-warning-standalone: #af7c12;
--uui-color-warning-contrast: #eeeeef;
--uui-color-danger: #ca3b37;
--uui-color-danger-emphasis: #ca3b37;
--uui-color-danger-standalone: #ca3b37;
--uui-color-danger-contrast: #eeeeef;
--uui-color-positive: #347d39;
--uui-color-positive-emphasis: #347d39;
--uui-color-positive-standalone: #347d39;
--uui-color-positive-contrast: #eeeeef;
}
`;
export default cssResult.cssText;

View File

@@ -0,0 +1,53 @@
:root {
--uui-color-selected: var(--uui-palette-violet-blue, #3544b1);
--uui-color-selected-emphasis: var(--uui-palette-violet-blue-light, rgb(70, 86, 200));
--uui-color-selected-standalone: var(--uui-palette-violet-blue-dark, rgb(54, 65, 156));
--uui-color-selected-contrast: #fff;
--uui-color-current: var(--uui-palette-spanish-pink, #f5c1bc);
--uui-color-current-emphasis: var(--uui-palette-spanish-pink-light, rgb(248, 214, 211));
--uui-color-current-standalone: var(--uui-palette-spanish-pink-dark, rgb(232, 192, 189));
--uui-color-current-contrast: var(--uui-palette-space-cadet, #1b264f);
--uui-color-disabled: var(--uui-palette-sand, #f3f3f5);
--uui-color-disabled-standalone: var(--uui-palette-sand-dark, rgb(226, 226, 226));
--uui-color-disabled-contrast: var(--uui-palette-grey, #c4c4c4);
--uui-color-header-surface: var(--uui-palette-space-cadet, #1b264f);
--uui-color-header-contrast: #fff;
--uui-color-header-contrast-emphasis: #fff;
--uui-color-focus: var(--uui-palette-malibu, #3879ff);
--uui-color-surface: #fff;
--uui-color-surface-alt: #fff;
--uui-color-surface-emphasis: #dadada;
--uui-color-background: #fff;
--uui-color-text: var(--uui-palette-black, #060606);
--uui-color-text-alt: var(--uui-palette-dune-black, #2e2b29);
--uui-color-interactive: var(--uui-palette-space-cadet, #1b264f);
--uui-color-interactive-emphasis: var(--uui-palette-violet-blue, #3544b1);
--uui-color-border: #000000;
--uui-color-border-standalone: #000000;
--uui-color-border-emphasis: #000000;
--uui-color-divider: #000000;
--uui-color-divider-standalone: #000000;
--uui-color-divider-emphasis: #000000;
--uui-color-default: var(--uui-palette-space-cadet, #1b264f);
--uui-color-default-emphasis: var(--uui-palette-violet-blue, #3544b1);
--uui-color-default-standalone: var(--uui-palette-space-cadet-dark, rgb(28, 35, 59));
--uui-color-default-contrast: #fff;
--uui-color-warning: #ffd621;
--uui-color-warning-emphasis: #ffdc41;
--uui-color-warning-standalone: #ffdd43;
--uui-color-warning-contrast: #000;
--uui-color-danger: #c60239;
--uui-color-danger-emphasis: #da114a;
--uui-color-danger-standalone: #d0003b;
--uui-color-danger-contrast: white;
--uui-color-positive: #0d8844;
--uui-color-positive-emphasis: #159c52;
--uui-color-positive-standalone: #1cae5e;
--uui-color-positive-contrast: #fff;
--uui-shadow-depth-1: 0 0 0px 1px black;
--uui-shadow-depth-2: 0 0 0px 1px black;
--uui-shadow-depth-3: 0 0 0px 1px black;
--uui-shadow-depth-4: 0 0 0px 1px black;
--uui-shadow-depth-5: 0 0 0px 1px black;
}

View File

@@ -1,65 +0,0 @@
import { css } from 'lit';
import { UmbTheme } from '../theme.context';
// TODO: We should get this from UUI, and it should be served through an extension.
const name = 'High Contrast';
const cssResult = css`
:root {
--uui-color-selected: var(--uui-palette-violet-blue, #3544b1);
--uui-color-selected-emphasis: var(--uui-palette-violet-blue-light, rgb(70, 86, 200));
--uui-color-selected-standalone: var(--uui-palette-violet-blue-dark, rgb(54, 65, 156));
--uui-color-selected-contrast: #fff;
--uui-color-current: var(--uui-palette-spanish-pink, #f5c1bc);
--uui-color-current-emphasis: var(--uui-palette-spanish-pink-light, rgb(248, 214, 211));
--uui-color-current-standalone: var(--uui-palette-spanish-pink-dark, rgb(232, 192, 189));
--uui-color-current-contrast: var(--uui-palette-space-cadet, #1b264f);
--uui-color-disabled: var(--uui-palette-sand, #f3f3f5);
--uui-color-disabled-standalone: var(--uui-palette-sand-dark, rgb(226, 226, 226));
--uui-color-disabled-contrast: var(--uui-palette-grey, #c4c4c4);
--uui-color-header-surface: var(--uui-palette-space-cadet, #1b264f);
--uui-color-header-contrast: #fff;
--uui-color-header-contrast-emphasis: #fff;
--uui-color-focus: var(--uui-palette-malibu, #3879ff);
--uui-color-surface: #fff;
--uui-color-surface-alt: #fff;
--uui-color-surface-emphasis: #dadada;
--uui-color-background: #fff;
--uui-color-text: var(--uui-palette-black, #060606);
--uui-color-text-alt: var(--uui-palette-dune-black, #2e2b29);
--uui-color-interactive: var(--uui-palette-space-cadet, #1b264f);
--uui-color-interactive-emphasis: var(--uui-palette-violet-blue, #3544b1);
--uui-color-border: #000000;
--uui-color-border-standalone: #000000;
--uui-color-border-emphasis: #000000;
--uui-color-divider: #000000;
--uui-color-divider-standalone: #000000;
--uui-color-divider-emphasis: #000000;
--uui-color-default: var(--uui-palette-space-cadet, #1b264f);
--uui-color-default-emphasis: var(--uui-palette-violet-blue, #3544b1);
--uui-color-default-standalone: var(--uui-palette-space-cadet-dark, rgb(28, 35, 59));
--uui-color-default-contrast: #fff;
--uui-color-warning: #ffd621;
--uui-color-warning-emphasis: #ffdc41;
--uui-color-warning-standalone: #ffdd43;
--uui-color-warning-contrast: #000;
--uui-color-danger: #c60239;
--uui-color-danger-emphasis: #da114a;
--uui-color-danger-standalone: #d0003b;
--uui-color-danger-contrast: white;
--uui-color-positive: #0d8844;
--uui-color-positive-emphasis: #159c52;
--uui-color-positive-standalone: #1cae5e;
--uui-color-positive-contrast: #fff;
--uui-shadow-depth-1: 0 0 0px 1px black;
--uui-shadow-depth-2: 0 0 0px 1px black;
--uui-shadow-depth-3: 0 0 0px 1px black;
--uui-shadow-depth-4: 0 0 0px 1px black;
--uui-shadow-depth-5: 0 0 0px 1px black;
}
`;
export const highContrast: UmbTheme = {
name: name,
css: cssResult.cssText,
};

View File

@@ -4,6 +4,8 @@ import { customElement, state } from 'lit/decorators.js';
import { UUISelectEvent } from '@umbraco-ui/uui';
import { UmbThemeContext, UMB_THEME_CONTEXT_TOKEN } from '../../themes/theme.context';
import { UmbLitElement } from '@umbraco-cms/element';
import { umbExtensionsRegistry } from '@umbraco-cms/extensions-api';
import { ManifestTheme } from '@umbraco-cms/extensions-registry';
@customElement('umb-user-dashboard-test')
export class UmbUserDashboardTestElement extends UmbLitElement {
@@ -26,26 +28,22 @@ export class UmbUserDashboardTestElement extends UmbLitElement {
#themeService?: UmbThemeContext;
@state()
private _theme: string | null = null;
private _themeAlias: string | null = null;
@state()
private _themes: Array<string> = [];
private _themes: Array<ManifestTheme> = [];
constructor() {
super();
this.consumeContext(UMB_THEME_CONTEXT_TOKEN, (instance) => {
console.log("ThemeCOntext", instance)
this.#themeService = instance;
instance.theme.subscribe((theme) => {
this._theme = theme;
instance.theme.subscribe((themeAlias) => {
this._themeAlias = themeAlias;
});
instance.setThemeByAlias('umb-dark-theme');
// TODO: We should get rid of the #themes state and instead use an extension point:
/*instance.themes.subscribe((themes) => {
this._themes = themes.map((t) => t.name);
});*/
umbExtensionsRegistry.extensionsOfType('theme').subscribe((themes) => {
this._themes = themes;
});
});
}
@@ -54,11 +52,11 @@ export class UmbUserDashboardTestElement extends UmbLitElement {
const theme = event.target.value.toString();
this.#themeService.changeTheme(theme);
this.#themeService.setThemeByAlias(theme);
}
get options() {
return this._themes.map((t) => ({ name: t, value: t, selected: t === this._theme }));
return this._themes.map((t) => ({ name: t.name, value: t.alias, selected: t.alias === this._themeAlias }));
}
render() {
@@ -67,7 +65,7 @@ export class UmbUserDashboardTestElement extends UmbLitElement {
<uui-select
label="theme select"
@change=${this._handleThemeChange}
.value=${this._theme}
.value=${this._themeAlias}
.options=${this.options}></uui-select>
`;
}