diff --git a/src/Umbraco.Web.UI.Client/libs/extensions-registry/models.ts b/src/Umbraco.Web.UI.Client/libs/extensions-registry/models.ts index d8762da1e0..39d6390015 100644 --- a/src/Umbraco.Web.UI.Client/libs/extensions-registry/models.ts +++ b/src/Umbraco.Web.UI.Client/libs/extensions-registry/models.ts @@ -18,6 +18,7 @@ import type { ManifestCollectionBulkAction } from './collection-bulk-action.mode import type { ManifestCollectionView } from './collection-view.models'; import type { ManifestHealthCheck } from './health-check.models'; import type { ManifestSidebarMenuItem } from './sidebar-menu-item.models'; +import type { ManifestTheme } from './theme.models'; export * from './header-app.models'; export * from './section.models'; @@ -39,6 +40,7 @@ export * from './collection-bulk-action.models'; export * from './collection-view.models'; export * from './health-check.models'; export * from './sidebar-menu-item.models'; +export * from './theme.models'; export type ManifestTypes = | ManifestCustom @@ -63,7 +65,8 @@ export type ManifestTypes = | ManifestCollectionBulkAction | ManifestCollectionView | ManifestHealthCheck - | ManifestSidebarMenuItem; + | ManifestSidebarMenuItem + | ManifestTheme; export type ManifestStandardTypes = ManifestTypes['type']; @@ -78,11 +81,15 @@ export interface ManifestBase { weight?: number; } -export interface ManifestElement extends ManifestBase { +export interface ManifestWithLoader extends ManifestBase { + loader?: () => Promise; +} + +export interface ManifestElement extends ManifestWithLoader { type: ManifestStandardTypes; js?: string; elementName?: string; - loader?: () => Promise; + //loader?: () => Promise; meta?: any; } diff --git a/src/Umbraco.Web.UI.Client/libs/extensions-registry/theme.models.ts b/src/Umbraco.Web.UI.Client/libs/extensions-registry/theme.models.ts new file mode 100644 index 0000000000..792f01f0dd --- /dev/null +++ b/src/Umbraco.Web.UI.Client/libs/extensions-registry/theme.models.ts @@ -0,0 +1,7 @@ +import type { ManifestWithLoader } from "./models"; + + +// TODO: make or find type for JS Module with default export: Would be nice to support css file directly. +export interface ManifestTheme extends ManifestWithLoader { + type: 'theme'; +} diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/backoffice.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/backoffice.element.ts index 5915e9c6df..c34893a558 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/backoffice.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/backoffice.element.ts @@ -33,7 +33,7 @@ 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.service'; +import { UmbThemeContext } from './themes/theme.context'; import { UmbNotificationService, UMB_NOTIFICATION_SERVICE_CONTEXT_TOKEN } from '@umbraco-cms/notification'; // Domains diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/themes/manifests.ts b/src/Umbraco.Web.UI.Client/src/backoffice/themes/manifests.ts new file mode 100644 index 0000000000..c65556a0e4 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/backoffice/themes/manifests.ts @@ -0,0 +1,12 @@ +import type { ManifestTheme } from '@umbraco-cms/models'; + +export const themes: Array = [ + { + type: 'theme', + alias: 'umb-dark-theme', + name: 'Dark', + loader: () => import('./themes/dark.theme') + }, +]; + +export const manifests = themes; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/themes/theme.context.ts b/src/Umbraco.Web.UI.Client/src/backoffice/themes/theme.context.ts index d1c6a7ba13..c32d019f60 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/themes/theme.context.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/themes/theme.context.ts @@ -1,15 +1,21 @@ -import { dark, highContrast } from './themes'; +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'; import { umbExtensionsRegistry } from '@umbraco-cms/extensions-api'; -import { map } from 'rxjs'; import { UmbControllerHostInterface } from '@umbraco-cms/controller'; +import { ManifestTheme } from '@umbraco-cms/extensions-registry'; export interface UmbTheme { name: string; - css: 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 @@ -36,40 +42,47 @@ export class UmbThemeContext { this._host = host; - new UmbContextProviderController(host, UMB_THEME_SERVICE_CONTEXT_TOKEN, this); + 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'); - const storedTheme = localStorage.getItem('umb-theme'); + const storedTheme = localStorage.getItem(LOCAL_STORAGE_KEY); if(storedTheme) { - this.setThemeByName(storedTheme); + this.setThemeByAlias(storedTheme); } document.documentElement.insertAdjacentElement('beforeend', this.#styleElement); } - private setThemeByName(themeName: string | null) { + public setThemeByAlias(themeAlias: string | null) { - this.#theme.next(themeName); + this.#theme.next(themeAlias); this.themeSubscription?.destroy(); - if(themeName != null) { - localStorage.setItem('umb-theme', themeName); + if(themeAlias != null) { + localStorage.setItem(LOCAL_STORAGE_KEY, themeAlias); this.themeSubscription = new UmbObserverController(this._host, umbExtensionsRegistry.extensionsOfType('theme').pipe(map( - (value) => value.name === themeName + (extensions) => extensions.filter((extension) => extension.alias === themeAlias) )) , - (theme) => { + 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; + } // how to get CSS. //this.#styleElement.innerHTML = ""; } ); } else { - localStorage.removeItem('umb-theme'); + localStorage.removeItem(LOCAL_STORAGE_KEY); this.#styleElement.innerHTML = ""; } @@ -82,8 +95,15 @@ export class UmbThemeContext { */ } -export const UMB_THEME_SERVICE_CONTEXT_TOKEN = new UmbContextToken(UmbThemeContext.name); -function UmbObserveController(arg0: Subscription): any { - throw new Error('Function not implemented.'); -} +export const UMB_THEME_CONTEXT_TOKEN = new UmbContextToken('umbThemeContext'); + + +// TODO: Can we do this in a smarter way: +const registerExtensions = (manifests: Array) => { + manifests.forEach((manifest) => { + umbExtensionsRegistry.register(manifest); + }); +}; + +registerExtensions([...manifests]); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/themes/themes/dark.theme.ts b/src/Umbraco.Web.UI.Client/src/backoffice/themes/themes/dark.theme.ts index 205d01ec14..7a81f37f03 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/themes/themes/dark.theme.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/themes/themes/dark.theme.ts @@ -1,5 +1,5 @@ import { css } from 'lit'; -import { UmbTheme } from '../theme.service'; +import { UmbTheme } from '../theme.context'; // TODO: We should get this from UUI, and it should be served through an extension. const name = 'Dark'; @@ -53,7 +53,4 @@ const cssResult = css` } `; -export const dark: UmbTheme = { - name: name, - css: cssResult.cssText, -}; +export default cssResult.cssText; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/themes/themes/high-contrast.theme.ts b/src/Umbraco.Web.UI.Client/src/backoffice/themes/themes/high-contrast.theme.ts index d7b1090c91..0cbbdf5581 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/themes/themes/high-contrast.theme.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/themes/themes/high-contrast.theme.ts @@ -1,5 +1,5 @@ import { css } from 'lit'; -import { UmbTheme } from '../theme.service'; +import { UmbTheme } from '../theme.context'; // TODO: We should get this from UUI, and it should be served through an extension. const name = 'High Contrast'; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/themes/themes/index.ts b/src/Umbraco.Web.UI.Client/src/backoffice/themes/themes/index.ts deleted file mode 100644 index cbee9b178f..0000000000 --- a/src/Umbraco.Web.UI.Client/src/backoffice/themes/themes/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './dark.theme'; -export * from './high-contrast.theme'; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/users/current-user/user-dashboard-themes.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/users/current-user/user-dashboard-themes.element.ts index ffe608c698..6d08c10001 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/users/current-user/user-dashboard-themes.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/users/current-user/user-dashboard-themes.element.ts @@ -2,7 +2,7 @@ import { css, html } from 'lit'; import { UUITextStyles } from '@umbraco-ui/uui-css/lib'; import { customElement, state } from 'lit/decorators.js'; import { UUISelectEvent } from '@umbraco-ui/uui'; -import { UmbThemeService, UMB_THEME_SERVICE_CONTEXT_TOKEN } from '../../themes/theme.service'; +import { UmbThemeContext, UMB_THEME_CONTEXT_TOKEN } from '../../themes/theme.context'; import { UmbLitElement } from '@umbraco-cms/element'; @customElement('umb-user-dashboard-test') @@ -23,25 +23,29 @@ export class UmbUserDashboardTestElement extends UmbLitElement { `, ]; - #themeService?: UmbThemeService; + #themeService?: UmbThemeContext; @state() - private _theme = ''; + private _theme: string | null = null; @state() private _themes: Array = []; constructor() { super(); - this.consumeContext(UMB_THEME_SERVICE_CONTEXT_TOKEN, (instance) => { + this.consumeContext(UMB_THEME_CONTEXT_TOKEN, (instance) => { + + console.log("ThemeCOntext", instance) this.#themeService = instance; instance.theme.subscribe((theme) => { this._theme = theme; }); + + instance.setThemeByAlias('umb-dark-theme'); // TODO: We should get rid of the #themes state and instead use an extension point: - instance.themes.subscribe((themes) => { + /*instance.themes.subscribe((themes) => { this._themes = themes.map((t) => t.name); - }); + });*/ }); }