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:
@@ -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
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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,
|
||||
};
|
||||
@@ -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>
|
||||
`;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user