From 811806ec6f87f2121b0da2c59e7c6753ccb00f95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jesper=20M=C3=B8ller=20Jensen?= <26099018+JesmoDev@users.noreply.github.com> Date: Mon, 23 Jan 2023 06:26:36 +0100 Subject: [PATCH 01/35] theme user-dashboard --- .../user-dashboard-test.element.ts | 94 ++++++++++++++++++- 1 file changed, 90 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/users/current-user/user-dashboard-test.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/users/current-user/user-dashboard-test.element.ts index e2db3b031e..bc11a06edf 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/users/current-user/user-dashboard-test.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/users/current-user/user-dashboard-test.element.ts @@ -1,6 +1,6 @@ -import { css, html } from 'lit'; +import { css, html, PropertyValueMap } from 'lit'; import { UUITextStyles } from '@umbraco-ui/uui-css/lib'; -import { customElement } from 'lit/decorators.js'; +import { customElement, state } from 'lit/decorators.js'; import { UmbLitElement } from '@umbraco-cms/element'; @customElement('umb-user-dashboard-test') @@ -24,10 +24,96 @@ export class UmbUserDashboardTestElement extends UmbLitElement { `, ]; + private _darkTheme = css` + :root { + --uui-color-selected: #4e78cc; + --uui-color-selected-emphasis: #4e78cc; + --uui-color-selected-standalone: #4e78cc; + --uui-color-selected-contrast: #fff; + --uui-color-current: #f5c1bc; + --uui-color-current-emphasis: #f8d6d3; + --uui-color-current-standalone: #e8c0bd; + --uui-color-current-contrast: #f000; + --uui-color-disabled: purple; + --uui-color-disabled-standalone: purple; + --uui-color-disabled-contrast: #fcfcfc4d; + --uui-color-header-surface: #1e2228; + --uui-color-header-contrast: #ffffffcc; + --uui-color-header-contrast-emphasis: #fff; + --uui-color-focus: #4e78cc; + --uui-color-surface: #23272e; + --uui-color-surface-alt: #2c313c; + --uui-color-surface-emphasis: #2c313c; + --uui-color-background: #1e2228; + --uui-color-text: #eeeeef; + --uui-color-text-alt: #eeeeef; + --uui-color-interactive: #eeeeef; + --uui-color-interactive-emphasis: #eeeeef; + --uui-color-border: #41414c; + --uui-color-border-standalone: #4a4a55; + --uui-color-border-emphasis: #484853; + --uui-color-divider: #41414c; + --uui-color-divider-standalone: #41414c; + --uui-color-divider-emphasis: #41414c; + --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; + } + `; + + private _styleElement?: HTMLStyleElement; + + @state() + private _darkThemeEnabled = true; + + constructor() { + super(); + const darkTheme = document.getElementById('dark-theme'); + if (!darkTheme) { + this._styleElement = document.createElement('style'); + this._styleElement.innerText = this._darkTheme.cssText; + this._styleElement.id = 'dark-theme'; + } else { + this._styleElement = darkTheme as HTMLStyleElement; + this._darkThemeEnabled = true; + } + } + + private _toggleTheme() { + this._darkThemeEnabled = !this._darkThemeEnabled; + } + + //if _darkThemeEnabledIs true, add the style element to the dom + //if _darkThemeEnabledIs false, remove the style element from the dom + protected updated(_changedProperties: PropertyValueMap | Map): void { + super.updated(_changedProperties); + + if (_changedProperties.has('_darkThemeEnabled') && this._styleElement) { + if (this._darkThemeEnabled) { + document.documentElement.insertAdjacentElement('beforeend', this._styleElement); + } else { + this._styleElement.remove(); + } + } + } + render() { return html` - Custom User Dashboard -

This is an example of a custom user dashboard using the user dashboard extension point

+ Dark theme toggle + ${this._styleElement ? 'Toggle Light' : 'Toggle Dark'} `; } } From b019ab0e1fc7af4a871d8ab1796f26d6f5091894 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jesper=20M=C3=B8ller=20Jensen?= <26099018+JesmoDev@users.noreply.github.com> Date: Mon, 23 Jan 2023 06:59:36 +0100 Subject: [PATCH 02/35] added theme service --- .../src/backoffice/backoffice.element.ts | 2 + .../user-dashboard-test.element.ts | 89 +++-------------- .../src/core/theme/theme.service.ts | 96 +++++++++++++++++++ 3 files changed, 111 insertions(+), 76 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/core/theme/theme.service.ts 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 2712f528bc..78252cdaab 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/backoffice.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/backoffice.element.ts @@ -41,6 +41,7 @@ import './users'; import './packages'; import './search'; import './shared'; +import { UmbThemeService, UMB_THEME_SERVICE_CONTEXT_TOKEN } from 'src/core/theme/theme.service'; @defineElement('umb-backoffice') export class UmbBackofficeElement extends UmbLitElement { @@ -80,6 +81,7 @@ export class UmbBackofficeElement extends UmbLitElement { this.provideContext(UMB_CURRENT_USER_HISTORY_STORE_CONTEXT_TOKEN, new UmbCurrentUserHistoryStore()); this.provideContext(UMB_DICTIONARY_STORE_CONTEXT_TOKEN, new UmbDictionaryStore(this)); this.provideContext(UMB_DOCUMENT_BLUEPRINT_STORE_CONTEXT_TOKEN, new UmbDocumentBlueprintStore(this)); + this.provideContext(UMB_THEME_SERVICE_CONTEXT_TOKEN, new UmbThemeService()); } render() { diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/users/current-user/user-dashboard-test.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/users/current-user/user-dashboard-test.element.ts index bc11a06edf..6276c8d9b1 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/users/current-user/user-dashboard-test.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/users/current-user/user-dashboard-test.element.ts @@ -2,6 +2,7 @@ import { css, html, PropertyValueMap } from 'lit'; import { UUITextStyles } from '@umbraco-ui/uui-css/lib'; import { customElement, state } from 'lit/decorators.js'; import { UmbLitElement } from '@umbraco-cms/element'; +import { UmbThemeService, UMB_THEME_SERVICE_CONTEXT_TOKEN } from 'src/core/theme/theme.service'; @customElement('umb-user-dashboard-test') export class UmbUserDashboardTestElement extends UmbLitElement { @@ -24,96 +25,32 @@ export class UmbUserDashboardTestElement extends UmbLitElement { `, ]; - private _darkTheme = css` - :root { - --uui-color-selected: #4e78cc; - --uui-color-selected-emphasis: #4e78cc; - --uui-color-selected-standalone: #4e78cc; - --uui-color-selected-contrast: #fff; - --uui-color-current: #f5c1bc; - --uui-color-current-emphasis: #f8d6d3; - --uui-color-current-standalone: #e8c0bd; - --uui-color-current-contrast: #f000; - --uui-color-disabled: purple; - --uui-color-disabled-standalone: purple; - --uui-color-disabled-contrast: #fcfcfc4d; - --uui-color-header-surface: #1e2228; - --uui-color-header-contrast: #ffffffcc; - --uui-color-header-contrast-emphasis: #fff; - --uui-color-focus: #4e78cc; - --uui-color-surface: #23272e; - --uui-color-surface-alt: #2c313c; - --uui-color-surface-emphasis: #2c313c; - --uui-color-background: #1e2228; - --uui-color-text: #eeeeef; - --uui-color-text-alt: #eeeeef; - --uui-color-interactive: #eeeeef; - --uui-color-interactive-emphasis: #eeeeef; - --uui-color-border: #41414c; - --uui-color-border-standalone: #4a4a55; - --uui-color-border-emphasis: #484853; - --uui-color-divider: #41414c; - --uui-color-divider-standalone: #41414c; - --uui-color-divider-emphasis: #41414c; - --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; - } - `; - - private _styleElement?: HTMLStyleElement; + #themeService?: UmbThemeService; @state() - private _darkThemeEnabled = true; + private _theme = ''; constructor() { super(); - const darkTheme = document.getElementById('dark-theme'); - if (!darkTheme) { - this._styleElement = document.createElement('style'); - this._styleElement.innerText = this._darkTheme.cssText; - this._styleElement.id = 'dark-theme'; - } else { - this._styleElement = darkTheme as HTMLStyleElement; - this._darkThemeEnabled = true; - } + this.consumeContext(UMB_THEME_SERVICE_CONTEXT_TOKEN, (instance) => { + this.#themeService = instance; + instance.theme.subscribe((theme) => { + this._theme = theme; + }); + }); } private _toggleTheme() { - this._darkThemeEnabled = !this._darkThemeEnabled; - } + if (!this.#themeService) return; + console.log('toggle', this._theme); - //if _darkThemeEnabledIs true, add the style element to the dom - //if _darkThemeEnabledIs false, remove the style element from the dom - protected updated(_changedProperties: PropertyValueMap | Map): void { - super.updated(_changedProperties); - - if (_changedProperties.has('_darkThemeEnabled') && this._styleElement) { - if (this._darkThemeEnabled) { - document.documentElement.insertAdjacentElement('beforeend', this._styleElement); - } else { - this._styleElement.remove(); - } - } + this.#themeService.changeTheme(this._theme !== 'dark' ? 'dark' : 'light'); } render() { return html` Dark theme toggle - ${this._styleElement ? 'Toggle Light' : 'Toggle Dark'} + Toggle `; } } diff --git a/src/Umbraco.Web.UI.Client/src/core/theme/theme.service.ts b/src/Umbraco.Web.UI.Client/src/core/theme/theme.service.ts new file mode 100644 index 0000000000..72a7c6cc0b --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/core/theme/theme.service.ts @@ -0,0 +1,96 @@ +import { BehaviorSubject } from 'rxjs'; +import { UmbContextToken } from '@umbraco-cms/context-api'; +import { css } from 'lit'; + +export interface UmbTheme { + name: string; + css: string; +} + +export class UmbThemeService { + #themes = new BehaviorSubject(>[ + { + name: 'light', + css: '', + }, + ]); + public readonly themes = this.#themes.asObservable(); + + #theme = new BehaviorSubject('light'); + public readonly theme = this.#theme.asObservable(); + + #styleElement: HTMLStyleElement; + + constructor() { + this.addTheme({ name: 'dark', css: _darkTheme.cssText }); + this.#styleElement = document.createElement('style'); + + document.documentElement.insertAdjacentElement('beforeend', this.#styleElement); + } + + public changeTheme(theme: string) { + this.#theme.next(theme); + + const themeCss = this.#themes.value.find((t) => t.name === theme)?.css; + + if (themeCss !== undefined) { + this.#styleElement.innerHTML = themeCss; + } + } + + public addTheme(theme: UmbTheme) { + this.#themes.next([...this.#themes.value, theme]); + } +} + +export const UMB_THEME_SERVICE_CONTEXT_TOKEN = new UmbContextToken(UmbThemeService.name); + +const _darkTheme = css` + :root { + --uui-color-selected: #4e78cc; + --uui-color-selected-emphasis: #4e78cc; + --uui-color-selected-standalone: #4e78cc; + --uui-color-selected-contrast: #fff; + --uui-color-current: #f5c1bc; + --uui-color-current-emphasis: #f8d6d3; + --uui-color-current-standalone: #e8c0bd; + --uui-color-current-contrast: #f000; + --uui-color-disabled: purple; + --uui-color-disabled-standalone: purple; + --uui-color-disabled-contrast: #fcfcfc4d; + --uui-color-header-surface: #1e2228; + --uui-color-header-contrast: #ffffffcc; + --uui-color-header-contrast-emphasis: #fff; + --uui-color-focus: #4e78cc; + --uui-color-surface: #23272e; + --uui-color-surface-alt: #2c313c; + --uui-color-surface-emphasis: #2c313c; + --uui-color-background: #1e2228; + --uui-color-text: #eeeeef; + --uui-color-text-alt: #eeeeef; + --uui-color-interactive: #eeeeef; + --uui-color-interactive-emphasis: #eeeeef; + --uui-color-border: #41414c; + --uui-color-border-standalone: #4a4a55; + --uui-color-border-emphasis: #484853; + --uui-color-divider: #41414c; + --uui-color-divider-standalone: #41414c; + --uui-color-divider-emphasis: #41414c; + --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; + } +`; From 5ed5eb985ec95e225700acfb061590de113853e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jesper=20M=C3=B8ller=20Jensen?= <26099018+JesmoDev@users.noreply.github.com> Date: Mon, 23 Jan 2023 07:01:41 +0100 Subject: [PATCH 03/35] set dark theme as default --- src/Umbraco.Web.UI.Client/src/core/theme/theme.service.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/core/theme/theme.service.ts b/src/Umbraco.Web.UI.Client/src/core/theme/theme.service.ts index 72a7c6cc0b..967e82ae65 100644 --- a/src/Umbraco.Web.UI.Client/src/core/theme/theme.service.ts +++ b/src/Umbraco.Web.UI.Client/src/core/theme/theme.service.ts @@ -16,7 +16,7 @@ export class UmbThemeService { ]); public readonly themes = this.#themes.asObservable(); - #theme = new BehaviorSubject('light'); + #theme = new BehaviorSubject('dark'); public readonly theme = this.#theme.asObservable(); #styleElement: HTMLStyleElement; @@ -24,6 +24,7 @@ export class UmbThemeService { constructor() { this.addTheme({ name: 'dark', css: _darkTheme.cssText }); this.#styleElement = document.createElement('style'); + this.changeTheme(this.#theme.value); document.documentElement.insertAdjacentElement('beforeend', this.#styleElement); } From a087814f4f6ffcba284f21224a7b774ac5be7668 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jesper=20M=C3=B8ller=20Jensen?= <26099018+JesmoDev@users.noreply.github.com> Date: Mon, 23 Jan 2023 07:08:10 +0100 Subject: [PATCH 04/35] styling --- .../src/core/theme/theme.service.ts | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/core/theme/theme.service.ts b/src/Umbraco.Web.UI.Client/src/core/theme/theme.service.ts index 967e82ae65..2381336b1b 100644 --- a/src/Umbraco.Web.UI.Client/src/core/theme/theme.service.ts +++ b/src/Umbraco.Web.UI.Client/src/core/theme/theme.service.ts @@ -52,31 +52,31 @@ const _darkTheme = css` --uui-color-selected-emphasis: #4e78cc; --uui-color-selected-standalone: #4e78cc; --uui-color-selected-contrast: #fff; - --uui-color-current: #f5c1bc; - --uui-color-current-emphasis: #f8d6d3; - --uui-color-current-standalone: #e8c0bd; + --uui-color-current: #4e78cc; + --uui-color-current-emphasis: #4e78cc; + --uui-color-current-standalone: #4e78cc; --uui-color-current-contrast: #f000; --uui-color-disabled: purple; --uui-color-disabled-standalone: purple; --uui-color-disabled-contrast: #fcfcfc4d; - --uui-color-header-surface: #1e2228; + --uui-color-header-surface: #21262e; --uui-color-header-contrast: #ffffffcc; --uui-color-header-contrast-emphasis: #fff; --uui-color-focus: #4e78cc; - --uui-color-surface: #23272e; - --uui-color-surface-alt: #2c313c; - --uui-color-surface-emphasis: #2c313c; - --uui-color-background: #1e2228; + --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: #41414c; - --uui-color-border-standalone: #4a4a55; - --uui-color-border-emphasis: #484853; - --uui-color-divider: #41414c; - --uui-color-divider-standalone: #41414c; - --uui-color-divider-emphasis: #41414c; + --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; From 160ea211c91dcfc57505b7d9a3b98589ba73bdb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jesper=20M=C3=B8ller=20Jensen?= <26099018+JesmoDev@users.noreply.github.com> Date: Tue, 24 Jan 2023 00:10:14 +0100 Subject: [PATCH 05/35] changed to a select --- .../users/current-user/manifests.ts | 12 +++---- ...nt.ts => user-dashboard-themes.element.ts} | 33 +++++++++++++------ .../src/core/theme/theme.service.ts | 24 +++++++------- 3 files changed, 41 insertions(+), 28 deletions(-) rename src/Umbraco.Web.UI.Client/src/backoffice/users/current-user/{user-dashboard-test.element.ts => user-dashboard-themes.element.ts} (63%) diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/users/current-user/manifests.ts b/src/Umbraco.Web.UI.Client/src/backoffice/users/current-user/manifests.ts index 5cf2fcce1a..63d58c00a4 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/users/current-user/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/users/current-user/manifests.ts @@ -3,13 +3,13 @@ import type { ManifestHeaderApp, ManifestUserDashboard } from '@umbraco-cms/mode export const userDashboards: Array = [ { type: 'userDashboard', - alias: 'Umb.UserDashboard.Test', - name: 'Test User Dashboard', - loader: () => import('./user-dashboard-test.element'), - weight: 2, + alias: 'Umb.UserDashboard.Themes', + name: 'Themes User Dashboard', + loader: () => import('./user-dashboard-themes.element'), + weight: 1, meta: { - label: 'Test User Dashboard', - pathname: 'test/test/test', + label: 'Themes User Dashboard', + pathname: 'themes', }, }, ]; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/users/current-user/user-dashboard-test.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/users/current-user/user-dashboard-themes.element.ts similarity index 63% rename from src/Umbraco.Web.UI.Client/src/backoffice/users/current-user/user-dashboard-test.element.ts rename to src/Umbraco.Web.UI.Client/src/backoffice/users/current-user/user-dashboard-themes.element.ts index 6276c8d9b1..d7d8374ae8 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/users/current-user/user-dashboard-test.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/users/current-user/user-dashboard-themes.element.ts @@ -3,6 +3,7 @@ import { UUITextStyles } from '@umbraco-ui/uui-css/lib'; import { customElement, state } from 'lit/decorators.js'; import { UmbLitElement } from '@umbraco-cms/element'; import { UmbThemeService, UMB_THEME_SERVICE_CONTEXT_TOKEN } from 'src/core/theme/theme.service'; +import { UUISelectEvent } from '@umbraco-ui/uui'; @customElement('umb-user-dashboard-test') export class UmbUserDashboardTestElement extends UmbLitElement { @@ -15,13 +16,10 @@ export class UmbUserDashboardTestElement extends UmbLitElement { gap: var(--uui-size-space-4); padding: var(--uui-size-space-5); border: 1px solid var(--uui-color-border); - background: var(--uui-color-positive); - color: var(--uui-color-positive-contrast); + background: var(--uui-color-surface); + color: var(--uui-color-text); border-radius: var(--uui-border-radius); } - p { - margin: 0; - } `, ]; @@ -30,6 +28,9 @@ export class UmbUserDashboardTestElement extends UmbLitElement { @state() private _theme = ''; + @state() + private _themes: Array = []; + constructor() { super(); this.consumeContext(UMB_THEME_SERVICE_CONTEXT_TOKEN, (instance) => { @@ -37,20 +38,32 @@ export class UmbUserDashboardTestElement extends UmbLitElement { instance.theme.subscribe((theme) => { this._theme = theme; }); + instance.themes.subscribe((themes) => { + this._themes = themes.map((t) => t.name); + }); }); } - private _toggleTheme() { + private _handleThemeChange(event: UUISelectEvent) { if (!this.#themeService) return; - console.log('toggle', this._theme); - this.#themeService.changeTheme(this._theme !== 'dark' ? 'dark' : 'light'); + const theme = event.target.value.toString(); + + this.#themeService.changeTheme(theme); + } + + get options() { + return this._themes.map((t) => ({ name: t, value: t, selected: t === this._theme })); } render() { return html` - Dark theme toggle - Toggle + Select Theme + `; } } diff --git a/src/Umbraco.Web.UI.Client/src/core/theme/theme.service.ts b/src/Umbraco.Web.UI.Client/src/core/theme/theme.service.ts index 2381336b1b..c71296963c 100644 --- a/src/Umbraco.Web.UI.Client/src/core/theme/theme.service.ts +++ b/src/Umbraco.Web.UI.Client/src/core/theme/theme.service.ts @@ -48,21 +48,21 @@ export const UMB_THEME_SERVICE_CONTEXT_TOKEN = new UmbContextToken Date: Tue, 24 Jan 2023 00:11:21 +0100 Subject: [PATCH 06/35] cleanup --- .../users/current-user/user-dashboard-themes.element.ts | 4 ++-- src/Umbraco.Web.UI.Client/src/core/theme/theme.service.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) 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 d7d8374ae8..03cd6a9c1f 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 @@ -1,9 +1,9 @@ -import { css, html, PropertyValueMap } from 'lit'; +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 { UmbLitElement } from '@umbraco-cms/element'; import { UmbThemeService, UMB_THEME_SERVICE_CONTEXT_TOKEN } from 'src/core/theme/theme.service'; -import { UUISelectEvent } from '@umbraco-ui/uui'; @customElement('umb-user-dashboard-test') export class UmbUserDashboardTestElement extends UmbLitElement { diff --git a/src/Umbraco.Web.UI.Client/src/core/theme/theme.service.ts b/src/Umbraco.Web.UI.Client/src/core/theme/theme.service.ts index c71296963c..34c541aa76 100644 --- a/src/Umbraco.Web.UI.Client/src/core/theme/theme.service.ts +++ b/src/Umbraco.Web.UI.Client/src/core/theme/theme.service.ts @@ -1,6 +1,6 @@ import { BehaviorSubject } from 'rxjs'; -import { UmbContextToken } from '@umbraco-cms/context-api'; import { css } from 'lit'; +import { UmbContextToken } from '@umbraco-cms/context-api'; export interface UmbTheme { name: string; From 99c3d559d89efedfa264e06de7e4848c99041151 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jesper=20M=C3=B8ller=20Jensen?= <26099018+JesmoDev@users.noreply.github.com> Date: Tue, 24 Jan 2023 00:34:20 +0100 Subject: [PATCH 07/35] added high contrast --- .../src/core/theme/theme.service.ts | 59 ++++++++++++++++++- 1 file changed, 58 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/core/theme/theme.service.ts b/src/Umbraco.Web.UI.Client/src/core/theme/theme.service.ts index 34c541aa76..19858b3ede 100644 --- a/src/Umbraco.Web.UI.Client/src/core/theme/theme.service.ts +++ b/src/Umbraco.Web.UI.Client/src/core/theme/theme.service.ts @@ -16,13 +16,14 @@ export class UmbThemeService { ]); public readonly themes = this.#themes.asObservable(); - #theme = new BehaviorSubject('dark'); + #theme = new BehaviorSubject('high-contrast'); public readonly theme = this.#theme.asObservable(); #styleElement: HTMLStyleElement; constructor() { this.addTheme({ name: 'dark', css: _darkTheme.cssText }); + this.addTheme({ name: 'high-contrast', css: _hightContrastTheme.cssText }); this.#styleElement = document.createElement('style'); this.changeTheme(this.#theme.value); @@ -95,3 +96,59 @@ const _darkTheme = css` --uui-color-positive-contrast: #eeeeef; } `; + +const _hightContrastTheme = 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; + } +`; From 7c66cad35c4ca696230c07431611e477f7b20474 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jesper=20M=C3=B8ller=20Jensen?= <26099018+JesmoDev@users.noreply.github.com> Date: Tue, 24 Jan 2023 02:39:22 +0100 Subject: [PATCH 08/35] added localStorage --- src/Umbraco.Web.UI.Client/src/core/theme/theme.service.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/core/theme/theme.service.ts b/src/Umbraco.Web.UI.Client/src/core/theme/theme.service.ts index 19858b3ede..13a6588522 100644 --- a/src/Umbraco.Web.UI.Client/src/core/theme/theme.service.ts +++ b/src/Umbraco.Web.UI.Client/src/core/theme/theme.service.ts @@ -25,13 +25,15 @@ export class UmbThemeService { this.addTheme({ name: 'dark', css: _darkTheme.cssText }); this.addTheme({ name: 'high-contrast', css: _hightContrastTheme.cssText }); this.#styleElement = document.createElement('style'); - this.changeTheme(this.#theme.value); + const storedTheme = localStorage.getItem('umb-theme'); + this.changeTheme(storedTheme ?? this.#theme.value); document.documentElement.insertAdjacentElement('beforeend', this.#styleElement); } public changeTheme(theme: string) { this.#theme.next(theme); + localStorage.setItem('umb-theme', theme); const themeCss = this.#themes.value.find((t) => t.name === theme)?.css; From a521971958d45c63661d98d2adc33fbb8034eeda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jesper=20M=C3=B8ller=20Jensen?= <26099018+JesmoDev@users.noreply.github.com> Date: Tue, 24 Jan 2023 03:20:07 +0100 Subject: [PATCH 09/35] moved themes to separate files --- .../src/core/theme/theme.service.ts | 118 ++---------------- .../src/core/theme/themes/dark.theme.ts | 58 +++++++++ .../core/theme/themes/high-contrast.theme.ts | 64 ++++++++++ .../src/core/theme/themes/index.ts | 2 + 4 files changed, 131 insertions(+), 111 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/core/theme/themes/dark.theme.ts create mode 100644 src/Umbraco.Web.UI.Client/src/core/theme/themes/high-contrast.theme.ts create mode 100644 src/Umbraco.Web.UI.Client/src/core/theme/themes/index.ts diff --git a/src/Umbraco.Web.UI.Client/src/core/theme/theme.service.ts b/src/Umbraco.Web.UI.Client/src/core/theme/theme.service.ts index 13a6588522..f58a16a624 100644 --- a/src/Umbraco.Web.UI.Client/src/core/theme/theme.service.ts +++ b/src/Umbraco.Web.UI.Client/src/core/theme/theme.service.ts @@ -1,5 +1,5 @@ import { BehaviorSubject } from 'rxjs'; -import { css } from 'lit'; +import { dark, highContrast } from './themes'; import { UmbContextToken } from '@umbraco-cms/context-api'; export interface UmbTheme { @@ -10,20 +10,22 @@ export interface UmbTheme { export class UmbThemeService { #themes = new BehaviorSubject(>[ { - name: 'light', + name: 'Light', css: '', }, ]); public readonly themes = this.#themes.asObservable(); - #theme = new BehaviorSubject('high-contrast'); + #theme = new BehaviorSubject('Light'); public readonly theme = this.#theme.asObservable(); #styleElement: HTMLStyleElement; constructor() { - this.addTheme({ name: 'dark', css: _darkTheme.cssText }); - this.addTheme({ name: 'high-contrast', css: _hightContrastTheme.cssText }); + //TODO: Figure out how to handle community themes. + this.addTheme(dark); + this.addTheme(highContrast); + this.#styleElement = document.createElement('style'); const storedTheme = localStorage.getItem('umb-theme'); this.changeTheme(storedTheme ?? this.#theme.value); @@ -48,109 +50,3 @@ export class UmbThemeService { } export const UMB_THEME_SERVICE_CONTEXT_TOKEN = new UmbContextToken(UmbThemeService.name); - -const _darkTheme = 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; - } -`; - -const _hightContrastTheme = 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; - } -`; diff --git a/src/Umbraco.Web.UI.Client/src/core/theme/themes/dark.theme.ts b/src/Umbraco.Web.UI.Client/src/core/theme/themes/dark.theme.ts new file mode 100644 index 0000000000..c4a593c652 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/core/theme/themes/dark.theme.ts @@ -0,0 +1,58 @@ +import { css } from 'lit'; +import { UmbTheme } from '../theme.service'; + +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 const dark: UmbTheme = { + name: name, + css: cssResult.cssText, +}; diff --git a/src/Umbraco.Web.UI.Client/src/core/theme/themes/high-contrast.theme.ts b/src/Umbraco.Web.UI.Client/src/core/theme/themes/high-contrast.theme.ts new file mode 100644 index 0000000000..a32b903c35 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/core/theme/themes/high-contrast.theme.ts @@ -0,0 +1,64 @@ +import { css } from 'lit'; +import { UmbTheme } from '../theme.service'; + +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, +}; diff --git a/src/Umbraco.Web.UI.Client/src/core/theme/themes/index.ts b/src/Umbraco.Web.UI.Client/src/core/theme/themes/index.ts new file mode 100644 index 0000000000..cbee9b178f --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/core/theme/themes/index.ts @@ -0,0 +1,2 @@ +export * from './dark.theme'; +export * from './high-contrast.theme'; From 1f770840d043b0d73e8b327f4e544c5fdf27f574 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 25 Jan 2023 16:23:41 +0100 Subject: [PATCH 10/35] interface --- .../workspace-entity-context.interface.ts | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-content/workspace-entity-context.interface.ts diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-content/workspace-entity-context.interface.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-content/workspace-entity-context.interface.ts new file mode 100644 index 0000000000..01fc98131e --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-content/workspace-entity-context.interface.ts @@ -0,0 +1,25 @@ +import { Observable } from "rxjs"; + +export interface UmbWorkspaceEntityContextInterface { + + readonly data:Observable; + readonly name:Observable; + + entityKey?: string; + entityType: string; + + + getData(): T; + + load(entityKey: string): void; + + create(parentKey: string | null): void; + + getStore(): unknown; + + setPropertyValue(alias: string, value: unknown): void; + + save(): Promise; + + destroy(): void; +} From 339db3eb6f6b0589b5f98f4f7ce05b52baecca56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Wed, 25 Jan 2023 16:25:34 +0100 Subject: [PATCH 11/35] optional destroy on context instances --- .../libs/context-api/provide/context-provider.controller.ts | 1 + .../libs/context-api/provide/context-provider.ts | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/src/Umbraco.Web.UI.Client/libs/context-api/provide/context-provider.controller.ts b/src/Umbraco.Web.UI.Client/libs/context-api/provide/context-provider.controller.ts index 88bebc98d2..f5ec707687 100644 --- a/src/Umbraco.Web.UI.Client/libs/context-api/provide/context-provider.controller.ts +++ b/src/Umbraco.Web.UI.Client/libs/context-api/provide/context-provider.controller.ts @@ -20,6 +20,7 @@ export class UmbContextProviderController } public destroy() { + super.destroy(); if (this.host) { this.host.removeController(this); } diff --git a/src/Umbraco.Web.UI.Client/libs/context-api/provide/context-provider.ts b/src/Umbraco.Web.UI.Client/libs/context-api/provide/context-provider.ts index f883c75c6a..ddad843889 100644 --- a/src/Umbraco.Web.UI.Client/libs/context-api/provide/context-provider.ts +++ b/src/Umbraco.Web.UI.Client/libs/context-api/provide/context-provider.ts @@ -53,4 +53,10 @@ export class UmbContextProvider { event.stopPropagation(); event.callback(this.#instance); }; + + + destroy(): void { + // I want to make sure to call this, but for now it was too overwhelming to require the destroy method on context instances. + (this.#instance as any).destroy?.(); + }; } From f7e8d145ec804168a8997074be7341e33d4b20d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Thu, 26 Jan 2023 08:49:03 +0100 Subject: [PATCH 12/35] move --- .../workspace-entity-context.interface.ts | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/{workspace-content => workspace-context}/workspace-entity-context.interface.ts (100%) diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-content/workspace-entity-context.interface.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-context/workspace-entity-context.interface.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-content/workspace-entity-context.interface.ts rename to src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-context/workspace-entity-context.interface.ts From b554850078aea00ac98fa5cc65f2fcfeacbabbb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Thu, 26 Jan 2023 10:31:51 +0100 Subject: [PATCH 13/35] observablePart as method pon deepState --- .../libs/observable-api/deep-state.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/Umbraco.Web.UI.Client/libs/observable-api/deep-state.ts b/src/Umbraco.Web.UI.Client/libs/observable-api/deep-state.ts index 03f2097033..ffa3124955 100644 --- a/src/Umbraco.Web.UI.Client/libs/observable-api/deep-state.ts +++ b/src/Umbraco.Web.UI.Client/libs/observable-api/deep-state.ts @@ -1,4 +1,5 @@ import { BehaviorSubject } from "rxjs"; +import { createObservablePart } from "./create-observable-part.method"; // TODO: Should this handle array as well? @@ -50,6 +51,13 @@ export class DeepState extends BehaviorSubject { super(deepFreeze(initialData)); } + observablePart( + mappingFunction: MappingFunction, + memoizationFunction?: MemoizationFunction + ) { + return createObservablePart(this, mappingFunction, memoizationFunction); + } + next(newData: T): void { const frozenData = deepFreeze(newData); // Only update data if its different than current data. From d9d255904d076a17e38634723dad065d260f47f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Thu, 26 Jan 2023 10:37:21 +0100 Subject: [PATCH 14/35] change to getObservablePart --- src/Umbraco.Web.UI.Client/libs/observable-api/deep-state.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/libs/observable-api/deep-state.ts b/src/Umbraco.Web.UI.Client/libs/observable-api/deep-state.ts index ffa3124955..a0cd9f94fe 100644 --- a/src/Umbraco.Web.UI.Client/libs/observable-api/deep-state.ts +++ b/src/Umbraco.Web.UI.Client/libs/observable-api/deep-state.ts @@ -51,7 +51,7 @@ export class DeepState extends BehaviorSubject { super(deepFreeze(initialData)); } - observablePart( + getObservablePart( mappingFunction: MappingFunction, memoizationFunction?: MemoizationFunction ) { From 8b124b3e4ceb4f3984347801b02a58796ff6fde2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Thu, 26 Jan 2023 10:39:39 +0100 Subject: [PATCH 15/35] split code into document workspace context --- .../workspace/document-workspace.context.ts | 119 ++++++++++++++++-- .../workspace/document-workspace.element.ts | 4 +- .../workspace-context/workspace-context.ts | 14 +++ .../workspace-entity-context.interface.ts | 9 +- 4 files changed, 131 insertions(+), 15 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-context/workspace-context.ts diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/document-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/document-workspace.context.ts index 3f89ad1f4b..3ca13efeed 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/document-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/document-workspace.context.ts @@ -1,10 +1,14 @@ -import { UmbWorkspaceContentContext } from '../../../shared/components/workspace/workspace-content/workspace-content.context'; -import { UMB_DOCUMENT_DETAIL_STORE_CONTEXT_TOKEN } from '../document.detail.store'; -import type { UmbDocumentDetailStore } from '../document.detail.store'; -import type { UmbControllerHostInterface } from '@umbraco-cms/controller'; +import { v4 as uuidv4 } from 'uuid'; +import { UmbDocumentDetailStore, UMB_DOCUMENT_DETAIL_STORE_CONTEXT_TOKEN } from '../document.detail.store'; +import type { UmbWorkspaceEntityContextInterface } from '../../../shared/components/workspace/workspace-context/workspace-entity-context.interface'; +import { UmbWorkspaceContext } from '../../../shared/components/workspace/workspace-context/workspace-context'; +import { UmbControllerHostInterface } from '@umbraco-cms/controller'; import type { DocumentDetails } from '@umbraco-cms/models'; -import { appendToFrozenArray } from '@umbraco-cms/observable-api'; +import { appendToFrozenArray, createObservablePart, ObjectState, UmbObserverController } from '@umbraco-cms/observable-api'; +import { UmbContextConsumerController } from '@umbraco-cms/context-api'; +import { UmbNotificationDefaultData, UmbNotificationService, UMB_NOTIFICATION_SERVICE_CONTEXT_TOKEN } from '@umbraco-cms/notification'; +/* const DefaultDocumentData = { key: '', name: '', @@ -33,14 +37,111 @@ const DefaultDocumentData = { }, ], } as DocumentDetails; +*/ + +export class UmbDocumentWorkspaceContext extends UmbWorkspaceContext implements UmbWorkspaceEntityContextInterface { + + + #data = new ObjectState(undefined); + public readonly data = this.#data.asObservable(); + public readonly name = this.#data.getObservablePart((data) => data?.name); + + #isNew = false; + private _entityKey?: string; + + protected _storeSubscription?: UmbObserverController; + + + private _notificationService?: UmbNotificationService; + private _store?: UmbDocumentDetailStore; + -export class UmbWorkspaceDocumentContext extends UmbWorkspaceContentContext { constructor(host: UmbControllerHostInterface) { - super(host, DefaultDocumentData, UMB_DOCUMENT_DETAIL_STORE_CONTEXT_TOKEN.toString(), 'document'); + super(host); + + new UmbContextConsumerController(this._host, UMB_NOTIFICATION_SERVICE_CONTEXT_TOKEN, (_instance) => { + this._notificationService = _instance; + }); + new UmbContextConsumerController(this._host, UMB_DOCUMENT_DETAIL_STORE_CONTEXT_TOKEN, (_instance) => { + this._store = _instance; + this._observeStore(); + }); + - console.log("UMB_DOCUMENT_DETAIL_STORE_CONTEXT_TOKEN", UMB_DOCUMENT_DETAIL_STORE_CONTEXT_TOKEN.toString()) } + private _observeStore() { + if (!this._store || !this._entityKey) { + return; + } + + if (!this.#isNew) { + this._storeSubscription?.destroy(); + this._storeSubscription = new UmbObserverController( + this._host, + this._store.getByKey(this._entityKey), + (content) => { + if (!content) return; // TODO: Handle nicely if there is no content data. + this.#data.next(content as any); + } + ); + } + } + + public getStore() { + return this._store; + } + + load(entityKey: string) { + this.#isNew = false; + this._entityKey = entityKey; + this._observeStore(); + } + + create(parentKey: string | null) { + this.#isNew = true; + this._entityKey = uuidv4(); + console.log("I'm new, and I will be created under ", parentKey); + } + + getData() { + return this.#data.getValue(); + } + + save(): Promise { + + if (!this._store) { + // TODO: add a more beautiful error: + console.error('Could not save cause workspace context has no store.'); + return Promise.resolve(); + } + + const documentData = this.getData(); + if(!documentData) { + console.error('Could not save cause workspace context has no data.'); + return Promise.resolve(); + } + + return this._store + .save([documentData]) + .then(() => { + const data: UmbNotificationDefaultData = { message: 'Document Saved' }; + this._notificationService?.peek('positive', { data }); + }) + .catch(() => { + const data: UmbNotificationDefaultData = { message: 'Failed to save Document' }; + this._notificationService?.peek('danger', { data }); + }); + } + + // TODO: how can we make sure to call this, we might need to turn this thing into a ContextProvider(extending) for it to call destroy? + public destroy(): void { + this.#data.unsubscribe(); + } + + + + public setPropertyValue(alias: string, value: unknown) { // TODO: make sure to check that we have a details model? otherwise fail? 8This can be relevant if we use the same context for tree actions? @@ -48,7 +149,7 @@ export class UmbWorkspaceDocumentContext extends UmbWorkspaceContentContext x.alias); - this._data.update({data: newDataSet}); + this.#data.update({data: newDataSet}); } /* diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/document-workspace.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/document-workspace.element.ts index 7f3f28b048..bff2b20d6e 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/document-workspace.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/document-workspace.element.ts @@ -1,7 +1,7 @@ import { UUITextStyles } from '@umbraco-ui/uui-css/lib'; import { css, html } from 'lit'; import { customElement, property } from 'lit/decorators.js'; -import { UmbWorkspaceDocumentContext } from './document-workspace.context'; +import { UmbDocumentWorkspaceContext } from './document-workspace.context'; import { UmbLitElement } from '@umbraco-cms/element'; import type { UmbWorkspaceEntityElement } from 'src/backoffice/shared/components/workspace/workspace-entity-element.interface'; @@ -35,7 +35,7 @@ export class UmbDocumentWorkspaceElement extends UmbLitElement implements UmbWor this._workspaceContext.create(parentKey); } - private _workspaceContext: UmbWorkspaceDocumentContext = new UmbWorkspaceDocumentContext(this); + private _workspaceContext: UmbDocumentWorkspaceContext = new UmbDocumentWorkspaceContext(this); render() { return html``; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-context/workspace-context.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-context/workspace-context.ts new file mode 100644 index 0000000000..9740db85c7 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-context/workspace-context.ts @@ -0,0 +1,14 @@ +import { UmbContextProviderController } from '@umbraco-cms/context-api'; +import { UmbControllerHostInterface } from '@umbraco-cms/controller'; + + +export abstract class UmbWorkspaceContext { + + protected _host: UmbControllerHostInterface; + + constructor(host: UmbControllerHostInterface) { + this._host = host; + new UmbContextProviderController(host, 'UmbWorkspaceContext', this); + } + +} diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-context/workspace-entity-context.interface.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-context/workspace-entity-context.interface.ts index 01fc98131e..6172536a23 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-context/workspace-entity-context.interface.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-context/workspace-entity-context.interface.ts @@ -2,11 +2,12 @@ import { Observable } from "rxjs"; export interface UmbWorkspaceEntityContextInterface { - readonly data:Observable; - readonly name:Observable; - entityKey?: string; - entityType: string; + readonly data: Observable; + readonly name: Observable; + + //entityKey?: string; + //entityType: string; getData(): T; From 6c4933a3a0d27ce56f7c5e06103a0f60906a1884 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Thu, 26 Jan 2023 12:16:15 +0100 Subject: [PATCH 16/35] getScaffold method --- src/Umbraco.Web.UI.Client/libs/store/store.ts | 23 ++++++++++++-- .../document-blueprint.detail.store.ts | 5 +++ .../document-type.detail.store.ts | 14 +++++++++ .../documents/document.detail.store.ts | 31 +++++++++++++++++++ .../media/media/media.detail.store.ts | 28 ++++++++++++++++- 5 files changed, 97 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/libs/store/store.ts b/src/Umbraco.Web.UI.Client/libs/store/store.ts index 6a2b3c0122..52c66bd926 100644 --- a/src/Umbraco.Web.UI.Client/libs/store/store.ts +++ b/src/Umbraco.Web.UI.Client/libs/store/store.ts @@ -14,12 +14,21 @@ export interface UmbTreeStore extends UmbDataStore { getTreeItemChildren(key: string): Observable>; } -export interface UmbContentStore extends UmbDataStore { +export interface UmbEntityStore extends UmbDataStore { + + /** + * @description - Request scaffold data by entityType and . The data is added to the store and is returned as an Observable. + * @param {string} key + * @return {*} {T} + * @memberof UmbEntityStore + */ + getScaffold: (entityType: string, parentKey: string | null) => T; + /** * @description - Request data by key. The data is added to the store and is returned as an Observable. * @param {string} key * @return {*} {(Observable)} - * @memberof UmbDataStoreBase + * @memberof UmbEntityStore */ getByKey(key: string): Observable; @@ -27,7 +36,15 @@ export interface UmbContentStore extends UmbDataStore { * @description - Save data. * @param {object} data * @return {*} {(Promise)} - * @memberof UmbContentStore + * @memberof UmbEntityStore */ save(data: T[]): Promise; } + + +export interface UmbContentStore extends UmbEntityStore { + + // TODO: make something that is specific for UmbContentStore + save(data: T[]): Promise; + +} diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-blueprints/document-blueprint.detail.store.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-blueprints/document-blueprint.detail.store.ts index 3e44801258..5ef48f1ba7 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-blueprints/document-blueprint.detail.store.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-blueprints/document-blueprint.detail.store.ts @@ -44,6 +44,11 @@ export class UmbDocumentBlueprintDetailStore extends UmbStoreBase { ); } + getScaffold(entityType: string, parentKey: string | null) { + return { + } as DocumentBlueprintDetails; + } + // TODO: make sure UI somehow can follow the status of this action. /** * @description - Save a DocumentBlueprint. diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/document-type.detail.store.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/document-type.detail.store.ts index 62b8c31c5d..d2ce543b6d 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/document-type.detail.store.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/document-type.detail.store.ts @@ -24,6 +24,20 @@ export class UmbDocumentTypeDetailStore extends UmbStoreBase { super(host, UMB_DOCUMENT_TYPE_DETAIL_STORE_CONTEXT_TOKEN.toString()); } + + getScaffold(entityType: string, parentKey: string | null) { + return { + key: '', + name: '', + icon: '', + type: '', + hasChildren: false, + parentKey: '', + alias: '', + properties: [], + } as DocumentTypeDetails; + } + /** * @description - Request a Data Type by key. The Data Type is added to the store and is returned as an Observable. * @param {string} key diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/document.detail.store.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/document.detail.store.ts index d02ab4c941..f8dcb37367 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/document.detail.store.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/document.detail.store.ts @@ -38,6 +38,37 @@ export class UmbDocumentDetailStore extends UmbStoreBase implements UmbContentSt ); } + getScaffold(entityType: string, parentKey: string | null) { + return { + key: '', + name: '', + icon: '', + type: '', + hasChildren: false, + parentKey: '', + isTrashed: false, + properties: [ + { + alias: '', + label: '', + description: '', + dataTypeKey: '', + }, + ], + data: [ + { + alias: '', + value: '', + }, + ], + variants: [ + { + name: '', + }, + ], + } as DocumentDetails; + } + // TODO: make sure UI somehow can follow the status of this action. save(data: DocumentDetails[]) { // fetch from server and update store diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/media/media/media.detail.store.ts b/src/Umbraco.Web.UI.Client/src/backoffice/media/media/media.detail.store.ts index 8783a9a6a2..4ba307da9c 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/media/media/media.detail.store.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/media/media/media.detail.store.ts @@ -17,7 +17,7 @@ export const UMB_MEDIA_DETAIL_STORE_CONTEXT_TOKEN = new UmbContextToken { - #data = new ArrayState([], (x) => x.key); + #data = new ArrayState([], (x) => x.key); constructor(host: UmbControllerHostInterface) { @@ -37,6 +37,32 @@ export class UmbMediaDetailStore extends UmbStoreBase implements UmbContentStore ); } + getScaffold(entityType: string, parentKey: string | null) { + return { + key: '', + name: '', + icon: '', + type: '', + hasChildren: false, + parentKey: '', + isTrashed: false, + properties: [ + { + alias: '', + label: '', + description: '', + dataTypeKey: '', + }, + ], + data: [ + { + alias: '', + value: '', + }, + ] + } as MediaDetails; + } + // TODO: make sure UI somehow can follow the status of this action. save(data: MediaDetails[]) { // fetch from server and update store From dca92b18f6bd7043963ff769ebb7ace962fcb24b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jesper=20M=C3=B8ller=20Jensen?= <26099018+JesmoDev@users.noreply.github.com> Date: Fri, 27 Jan 2023 03:22:11 +0100 Subject: [PATCH 17/35] cleanup --- .../src/backoffice/backoffice.element.ts | 13 ++++++++----- .../theme => backoffice/themes}/theme.service.ts | 2 +- .../themes}/themes/dark.theme.ts | 0 .../themes}/themes/high-contrast.theme.ts | 0 .../theme => backoffice/themes}/themes/index.ts | 0 .../current-user/user-dashboard-themes.element.ts | 2 +- 6 files changed, 10 insertions(+), 7 deletions(-) rename src/Umbraco.Web.UI.Client/src/{core/theme => backoffice/themes}/theme.service.ts (95%) rename src/Umbraco.Web.UI.Client/src/{core/theme => backoffice/themes}/themes/dark.theme.ts (100%) rename src/Umbraco.Web.UI.Client/src/{core/theme => backoffice/themes}/themes/high-contrast.theme.ts (100%) rename src/Umbraco.Web.UI.Client/src/{core/theme => backoffice/themes}/themes/index.ts (100%) 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 1d684f6192..e521c4d613 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/backoffice.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/backoffice.element.ts @@ -2,7 +2,6 @@ import { defineElement } from '@umbraco-ui/uui-base/lib/registration'; import { UUITextStyles } from '@umbraco-ui/uui-css/lib'; import { css, html } from 'lit'; - import { UmbModalService, UMB_MODAL_SERVICE_CONTEXT_TOKEN } from '../core/modal'; import { UmbUserStore } from './users/users/user.store'; import { UmbUserGroupStore } from './users/user-groups/user-group.store'; @@ -12,9 +11,12 @@ import { UMB_CURRENT_USER_HISTORY_STORE_CONTEXT_TOKEN, } from './users/current-user/current-user-history.store'; -import { UmbBackofficeContext, UMB_BACKOFFICE_CONTEXT_TOKEN } from './shared/components/backoffice-frame/backoffice.context'; -import {UmbDocumentTypeDetailStore} from './documents/document-types/document-type.detail.store'; -import {UmbDocumentTypeTreeStore} from './documents/document-types/document-type.tree.store'; +import { + UmbBackofficeContext, + UMB_BACKOFFICE_CONTEXT_TOKEN, +} from './shared/components/backoffice-frame/backoffice.context'; +import { UmbDocumentTypeDetailStore } from './documents/document-types/document-type.detail.store'; +import { UmbDocumentTypeTreeStore } from './documents/document-types/document-type.tree.store'; import { UmbMediaTypeDetailStore } from './media/media-types/media-type.detail.store'; import { UmbMediaTypeTreeStore } from './media/media-types/media-type.tree.store'; import { UmbDocumentDetailStore } from './documents/documents/document.detail.store'; @@ -31,9 +33,9 @@ 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 { UmbThemeService } from './themes/theme.service'; import { UmbNotificationService, UMB_NOTIFICATION_SERVICE_CONTEXT_TOKEN } from '@umbraco-cms/notification'; - // Domains import './settings'; import './documents'; @@ -91,6 +93,7 @@ export class UmbBackofficeElement extends UmbLitElement { new UmbDictionaryTreeStore(this); new UmbDocumentBlueprintDetailStore(this); new UmbDocumentBlueprintTreeStore(this); + new UmbThemeService(); this.provideContext(UMB_BACKOFFICE_CONTEXT_TOKEN, new UmbBackofficeContext()); this.provideContext(UMB_CURRENT_USER_HISTORY_STORE_CONTEXT_TOKEN, new UmbCurrentUserHistoryStore()); diff --git a/src/Umbraco.Web.UI.Client/src/core/theme/theme.service.ts b/src/Umbraco.Web.UI.Client/src/backoffice/themes/theme.service.ts similarity index 95% rename from src/Umbraco.Web.UI.Client/src/core/theme/theme.service.ts rename to src/Umbraco.Web.UI.Client/src/backoffice/themes/theme.service.ts index f58a16a624..851fe1ee6d 100644 --- a/src/Umbraco.Web.UI.Client/src/core/theme/theme.service.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/themes/theme.service.ts @@ -22,7 +22,7 @@ export class UmbThemeService { #styleElement: HTMLStyleElement; constructor() { - //TODO: Figure out how to handle community themes. + //TODO: Figure out how to extend this with themes from packages this.addTheme(dark); this.addTheme(highContrast); diff --git a/src/Umbraco.Web.UI.Client/src/core/theme/themes/dark.theme.ts b/src/Umbraco.Web.UI.Client/src/backoffice/themes/themes/dark.theme.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/core/theme/themes/dark.theme.ts rename to src/Umbraco.Web.UI.Client/src/backoffice/themes/themes/dark.theme.ts diff --git a/src/Umbraco.Web.UI.Client/src/core/theme/themes/high-contrast.theme.ts b/src/Umbraco.Web.UI.Client/src/backoffice/themes/themes/high-contrast.theme.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/core/theme/themes/high-contrast.theme.ts rename to src/Umbraco.Web.UI.Client/src/backoffice/themes/themes/high-contrast.theme.ts diff --git a/src/Umbraco.Web.UI.Client/src/core/theme/themes/index.ts b/src/Umbraco.Web.UI.Client/src/backoffice/themes/themes/index.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/core/theme/themes/index.ts rename to src/Umbraco.Web.UI.Client/src/backoffice/themes/themes/index.ts 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 03cd6a9c1f..f036ce0456 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,8 +2,8 @@ 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 { UmbLitElement } from '@umbraco-cms/element'; -import { UmbThemeService, UMB_THEME_SERVICE_CONTEXT_TOKEN } from 'src/core/theme/theme.service'; @customElement('umb-user-dashboard-test') export class UmbUserDashboardTestElement extends UmbLitElement { From 090bf44d7991a37d64861b54e9abb56a17e38382 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jesper=20M=C3=B8ller=20Jensen?= <26099018+JesmoDev@users.noreply.github.com> Date: Fri, 27 Jan 2023 03:28:28 +0100 Subject: [PATCH 18/35] provide theme service --- .../src/backoffice/backoffice.element.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 e521c4d613..217ba77ce6 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 { UmbThemeService } from './themes/theme.service'; +import { UmbThemeService, UMB_THEME_SERVICE_CONTEXT_TOKEN } from './themes/theme.service'; import { UmbNotificationService, UMB_NOTIFICATION_SERVICE_CONTEXT_TOKEN } from '@umbraco-cms/notification'; // Domains @@ -93,10 +93,10 @@ export class UmbBackofficeElement extends UmbLitElement { new UmbDictionaryTreeStore(this); new UmbDocumentBlueprintDetailStore(this); new UmbDocumentBlueprintTreeStore(this); - new UmbThemeService(); this.provideContext(UMB_BACKOFFICE_CONTEXT_TOKEN, new UmbBackofficeContext()); this.provideContext(UMB_CURRENT_USER_HISTORY_STORE_CONTEXT_TOKEN, new UmbCurrentUserHistoryStore()); + this.provideContext(UMB_THEME_SERVICE_CONTEXT_TOKEN, new UmbThemeService()); } render() { From 3f1a1ac063ae95f54d7a4998ae09f11ec8972f99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jesper=20M=C3=B8ller=20Jensen?= <26099018+JesmoDev@users.noreply.github.com> Date: Fri, 27 Jan 2023 03:43:20 +0100 Subject: [PATCH 19/35] fix current user --- .../src/backoffice/users/current-user/current-user.store.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/users/current-user/current-user.store.ts b/src/Umbraco.Web.UI.Client/src/backoffice/users/current-user/current-user.store.ts index 02f701b2ec..6f6dcdc54e 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/users/current-user/current-user.store.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/users/current-user/current-user.store.ts @@ -1,11 +1,12 @@ +import { umbUsersData } from '../../../core/mocks/data/users.data'; import { umbracoPath } from '@umbraco-cms/utils'; import type { UserDetails } from '@umbraco-cms/models'; import { UmbContextToken } from '@umbraco-cms/context-api'; import { ObjectState } from '@umbraco-cms/observable-api'; export class UmbCurrentUserStore { - - private _currentUser = new ObjectState(undefined); + //TODO: Temp solution to get a current user. Replace when we have a real user service + private _currentUser = new ObjectState(umbUsersData.getAll()[0]); public readonly currentUser = this._currentUser.asObservable(); /** From e2c1fb15f1be1c5885f373b1e678cc7029cb57cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Fri, 27 Jan 2023 09:29:55 +0100 Subject: [PATCH 20/35] mega refactor commit --- .../consume/context-consumer.controller.ts | 2 +- src/Umbraco.Web.UI.Client/libs/store/store.ts | 10 +- .../document-type.detail.store.ts | 5 +- .../document-type-workspace.context.ts | 46 ++--- .../document-type-workspace.element.ts | 4 +- .../workspace/document-workspace.context.ts | 159 +++--------------- .../workspace/document-workspace.element.ts | 2 +- .../media-types/media-type.detail.store.ts | 20 ++- .../workspace/media-workspace.context.ts | 60 +++---- .../member-group.details.store.ts | 13 +- .../member-types/member-type.detail.store.ts | 9 +- .../data-types/data-type.detail.store.ts | 21 ++- .../workspace/data-type-workspace.context.ts | 72 ++++---- .../workspace/data-type-workspace.element.ts | 2 +- .../data-type-workspace-view-edit.element.ts | 10 +- .../variant-selector.element.ts | 12 +- .../workspace-property.context.ts | 6 +- .../workspace-action-node-save.element.ts | 6 +- .../workspace-view-collection.element.ts | 13 +- .../workspace-view-content-edit.element.ts | 6 +- .../workspace-view-content-info.element.ts | 6 +- .../workspace-content.context.ts | 125 -------------- .../entity-manager-controller.ts | 133 +++++++++++++++ .../workspace-entity-context.interface.ts | 9 +- .../dictionary/dictionary.detail.store.ts | 11 +- .../users/user-groups/user-group.store.ts | 28 ++- .../workspace/user-group-workspace.context.ts | 45 ++--- .../workspace/user-group-workspace.element.ts | 33 ++-- .../src/backoffice/users/users/user.store.ts | 25 ++- .../users/workspace/user-workspace.context.ts | 51 +++--- .../users/workspace/user-workspace.element.ts | 2 +- 31 files changed, 464 insertions(+), 482 deletions(-) delete mode 100644 src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-content/workspace-content.context.ts create mode 100644 src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-context/entity-manager-controller.ts diff --git a/src/Umbraco.Web.UI.Client/libs/context-api/consume/context-consumer.controller.ts b/src/Umbraco.Web.UI.Client/libs/context-api/consume/context-consumer.controller.ts index 20fdb24379..534cd6a04f 100644 --- a/src/Umbraco.Web.UI.Client/libs/context-api/consume/context-consumer.controller.ts +++ b/src/Umbraco.Web.UI.Client/libs/context-api/consume/context-consumer.controller.ts @@ -1,7 +1,7 @@ -import type { UmbControllerHostInterface, UmbControllerInterface } from '@umbraco-cms/controller'; import { UmbContextToken } from '../context-token'; import { UmbContextConsumer } from './context-consumer'; import { UmbContextCallback } from './context-request.event'; +import type { UmbControllerHostInterface, UmbControllerInterface } from '@umbraco-cms/controller'; export class UmbContextConsumerController extends UmbContextConsumer diff --git a/src/Umbraco.Web.UI.Client/libs/store/store.ts b/src/Umbraco.Web.UI.Client/libs/store/store.ts index 52c66bd926..dbaf6e17a2 100644 --- a/src/Umbraco.Web.UI.Client/libs/store/store.ts +++ b/src/Umbraco.Web.UI.Client/libs/store/store.ts @@ -14,13 +14,13 @@ export interface UmbTreeStore extends UmbDataStore { getTreeItemChildren(key: string): Observable>; } -export interface UmbEntityStore extends UmbDataStore { +export interface UmbEntityDetailStore extends UmbDataStore { /** * @description - Request scaffold data by entityType and . The data is added to the store and is returned as an Observable. * @param {string} key * @return {*} {T} - * @memberof UmbEntityStore + * @memberof UmbEntityDetailStore */ getScaffold: (entityType: string, parentKey: string | null) => T; @@ -28,7 +28,7 @@ export interface UmbEntityStore extends UmbDataStore { * @description - Request data by key. The data is added to the store and is returned as an Observable. * @param {string} key * @return {*} {(Observable)} - * @memberof UmbEntityStore + * @memberof UmbEntityDetailStore */ getByKey(key: string): Observable; @@ -36,13 +36,13 @@ export interface UmbEntityStore extends UmbDataStore { * @description - Save data. * @param {object} data * @return {*} {(Promise)} - * @memberof UmbEntityStore + * @memberof UmbEntityDetailStore */ save(data: T[]): Promise; } -export interface UmbContentStore extends UmbEntityStore { +export interface UmbContentStore extends UmbEntityDetailStore { // TODO: make something that is specific for UmbContentStore save(data: T[]): Promise; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/document-type.detail.store.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/document-type.detail.store.ts index d2ce543b6d..4835577c3a 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/document-type.detail.store.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/document-type.detail.store.ts @@ -1,7 +1,7 @@ import type { DocumentTypeDetails } from '@umbraco-cms/models'; import { UmbContextToken } from '@umbraco-cms/context-api'; import { createObservablePart, ArrayState } from '@umbraco-cms/observable-api'; -import { UmbStoreBase } from '@umbraco-cms/store'; +import { UmbEntityDetailStore, UmbStoreBase } from '@umbraco-cms/store'; import { UmbControllerHostInterface } from '@umbraco-cms/controller'; @@ -14,8 +14,7 @@ export const UMB_DOCUMENT_TYPE_DETAIL_STORE_CONTEXT_TOKEN = new UmbContextToken< * @extends {UmbStoreBase} * @description - Details Data Store for Document Types */ -export class UmbDocumentTypeDetailStore extends UmbStoreBase { - +export class UmbDocumentTypeDetailStore extends UmbStoreBase implements UmbEntityDetailStore { #data = new ArrayState([], (x) => x.key); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/workspace/document-type-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/workspace/document-type-workspace.context.ts index 0214a2a2c4..537335a1be 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/workspace/document-type-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/workspace/document-type-workspace.context.ts @@ -1,29 +1,31 @@ -import { UmbWorkspaceContentContext } from '../../../shared/components/workspace/workspace-content/workspace-content.context'; -import { - UmbDocumentTypeDetailStore, - UMB_DOCUMENT_TYPE_DETAIL_STORE_CONTEXT_TOKEN, -} from '../document-type.detail.store'; -import { UmbControllerHostInterface } from '@umbraco-cms/controller'; +import { UmbEntityWorkspaceManager } from '../../../shared/components/workspace/workspace-context/entity-manager-controller'; +import { UmbWorkspaceContext } from '../../../shared/components/workspace/workspace-context/workspace-context'; +import { UmbWorkspaceEntityContextInterface } from '../../../shared/components/workspace/workspace-context/workspace-entity-context.interface'; +import { UmbDocumentTypeDetailStore, UMB_DOCUMENT_TYPE_DETAIL_STORE_CONTEXT_TOKEN } from '../document-type.detail.store'; import type { DocumentTypeDetails } from '@umbraco-cms/models'; -const DefaultDocumentTypeData = { - key: '', - name: '', - icon: '', - type: '', - hasChildren: false, - parentKey: '', - alias: '', - properties: [], -} as DocumentTypeDetails; +export class UmbWorkspaceDocumentTypeContext extends UmbWorkspaceContext implements UmbWorkspaceEntityContextInterface { -export class UmbWorkspaceDocumentTypeContext extends UmbWorkspaceContentContext< - DocumentTypeDetails, - UmbDocumentTypeDetailStore -> { - constructor(host: UmbControllerHostInterface) { - super(host, DefaultDocumentTypeData, UMB_DOCUMENT_TYPE_DETAIL_STORE_CONTEXT_TOKEN.toString(), 'documentType'); + #manager = new UmbEntityWorkspaceManager(this._host, 'document-type', UMB_DOCUMENT_TYPE_DETAIL_STORE_CONTEXT_TOKEN); + + public readonly data = this.#manager.state.asObservable(); + public readonly name = this.#manager.state.getObservablePart((state) => state?.name); + + + setName(name: string) { + this.#manager.state.update({name: name}) } + setIcon(icon: string) { + this.#manager.state.update({icon: icon}) + } + getEntityType = this.#manager.getEntityType; + getEntityKey = this.#manager.getEntityKey; + getStore = this.#manager.getStore; + getData = this.#manager.getData; + load = this.#manager.load; + create = this.#manager.create; + save = this.#manager.save; + destroy = this.#manager.destroy; public setPropertyValue(alias: string, value: unknown) { throw new Error('setPropertyValue is not implemented for UmbWorkspaceDocumentTypeContext'); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/workspace/document-type-workspace.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/workspace/document-type-workspace.element.ts index 3c799f13c7..1bf3b63e90 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/workspace/document-type-workspace.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/workspace/document-type-workspace.element.ts @@ -89,7 +89,7 @@ export class UmbDocumentTypeWorkspaceElement extends UmbLitElement implements Um const target = event.composedPath()[0] as UUIInputElement; if (typeof target?.value === 'string') { - this._workspaceContext?.update({ name: target.value }); + this._workspaceContext?.setName(target.value); } } } @@ -98,7 +98,7 @@ export class UmbDocumentTypeWorkspaceElement extends UmbLitElement implements Um const modalHandler = this._modalService?.iconPicker(); modalHandler?.onClose().then((saved) => { - if (saved) this._workspaceContext?.update({ icon: saved.icon }); + if (saved) this._workspaceContext?.setIcon(saved.icon); console.log(saved); // TODO save color ALIAS as well }); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/document-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/document-workspace.context.ts index 3ca13efeed..3618663545 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/document-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/document-workspace.context.ts @@ -1,157 +1,46 @@ -import { v4 as uuidv4 } from 'uuid'; import { UmbDocumentDetailStore, UMB_DOCUMENT_DETAIL_STORE_CONTEXT_TOKEN } from '../document.detail.store'; import type { UmbWorkspaceEntityContextInterface } from '../../../shared/components/workspace/workspace-context/workspace-entity-context.interface'; import { UmbWorkspaceContext } from '../../../shared/components/workspace/workspace-context/workspace-context'; -import { UmbControllerHostInterface } from '@umbraco-cms/controller'; +import { UmbEntityWorkspaceManager } from '../../../shared/components/workspace/workspace-context/entity-manager-controller'; import type { DocumentDetails } from '@umbraco-cms/models'; -import { appendToFrozenArray, createObservablePart, ObjectState, UmbObserverController } from '@umbraco-cms/observable-api'; -import { UmbContextConsumerController } from '@umbraco-cms/context-api'; -import { UmbNotificationDefaultData, UmbNotificationService, UMB_NOTIFICATION_SERVICE_CONTEXT_TOKEN } from '@umbraco-cms/notification'; - -/* -const DefaultDocumentData = { - key: '', - name: '', - icon: '', - type: '', - hasChildren: false, - parentKey: '', - isTrashed: false, - properties: [ - { - alias: '', - label: '', - description: '', - dataTypeKey: '', - }, - ], - data: [ - { - alias: '', - value: '', - }, - ], - variants: [ - { - name: '', - }, - ], -} as DocumentDetails; -*/ +import { appendToFrozenArray } from '@umbraco-cms/observable-api'; export class UmbDocumentWorkspaceContext extends UmbWorkspaceContext implements UmbWorkspaceEntityContextInterface { - #data = new ObjectState(undefined); - public readonly data = this.#data.asObservable(); - public readonly name = this.#data.getObservablePart((data) => data?.name); - - #isNew = false; - private _entityKey?: string; - - protected _storeSubscription?: UmbObserverController; - - - private _notificationService?: UmbNotificationService; - private _store?: UmbDocumentDetailStore; - - - constructor(host: UmbControllerHostInterface) { - super(host); - - new UmbContextConsumerController(this._host, UMB_NOTIFICATION_SERVICE_CONTEXT_TOKEN, (_instance) => { - this._notificationService = _instance; - }); - new UmbContextConsumerController(this._host, UMB_DOCUMENT_DETAIL_STORE_CONTEXT_TOKEN, (_instance) => { - this._store = _instance; - this._observeStore(); - }); + #manager = new UmbEntityWorkspaceManager(this._host, 'document', UMB_DOCUMENT_DETAIL_STORE_CONTEXT_TOKEN); + public readonly data = this.#manager.state.asObservable(); + public readonly name = this.#manager.state.getObservablePart((state) => state?.name); + setName(name: string) { + this.#manager.state.update({name: name}) } - - private _observeStore() { - if (!this._store || !this._entityKey) { - return; - } - - if (!this.#isNew) { - this._storeSubscription?.destroy(); - this._storeSubscription = new UmbObserverController( - this._host, - this._store.getByKey(this._entityKey), - (content) => { - if (!content) return; // TODO: Handle nicely if there is no content data. - this.#data.next(content as any); - } - ); - } - } - - public getStore() { - return this._store; - } - - load(entityKey: string) { - this.#isNew = false; - this._entityKey = entityKey; - this._observeStore(); - } - - create(parentKey: string | null) { - this.#isNew = true; - this._entityKey = uuidv4(); - console.log("I'm new, and I will be created under ", parentKey); - } - - getData() { - return this.#data.getValue(); - } - - save(): Promise { - - if (!this._store) { - // TODO: add a more beautiful error: - console.error('Could not save cause workspace context has no store.'); - return Promise.resolve(); - } - - const documentData = this.getData(); - if(!documentData) { - console.error('Could not save cause workspace context has no data.'); - return Promise.resolve(); - } - - return this._store - .save([documentData]) - .then(() => { - const data: UmbNotificationDefaultData = { message: 'Document Saved' }; - this._notificationService?.peek('positive', { data }); - }) - .catch(() => { - const data: UmbNotificationDefaultData = { message: 'Failed to save Document' }; - this._notificationService?.peek('danger', { data }); - }); - } - - // TODO: how can we make sure to call this, we might need to turn this thing into a ContextProvider(extending) for it to call destroy? - public destroy(): void { - this.#data.unsubscribe(); - } + getEntityType = this.#manager.getEntityType; + getEntityKey = this.#manager.getEntityKey; + getStore = this.#manager.getStore; + getData = this.#manager.getData; + load = this.#manager.load; + create = this.#manager.create; + save = this.#manager.save; + destroy = this.#manager.destroy; + // This could eventually be moved out as well? + setPropertyValue(alias: string, value: unknown) { - - public setPropertyValue(alias: string, value: unknown) { - - // TODO: make sure to check that we have a details model? otherwise fail? 8This can be relevant if we use the same context for tree actions? const entry = {alias: alias, value: value}; - const newDataSet = appendToFrozenArray(this._data.getValue().data, entry, x => x.alias); + const currentData = this.#manager.getData(); + if (currentData) { + const newDataSet = appendToFrozenArray(currentData.data, entry, x => x.alias); - this.#data.update({data: newDataSet}); + this.#manager.state.update({data: newDataSet}); + } } + + /* concept notes: diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/document-workspace.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/document-workspace.element.ts index bff2b20d6e..0a2301f756 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/document-workspace.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/document-workspace.element.ts @@ -1,9 +1,9 @@ import { UUITextStyles } from '@umbraco-ui/uui-css/lib'; import { css, html } from 'lit'; import { customElement, property } from 'lit/decorators.js'; +import type { UmbWorkspaceEntityElement } from '../../../shared/components/workspace/workspace-entity-element.interface'; import { UmbDocumentWorkspaceContext } from './document-workspace.context'; import { UmbLitElement } from '@umbraco-cms/element'; -import type { UmbWorkspaceEntityElement } from 'src/backoffice/shared/components/workspace/workspace-entity-element.interface'; @customElement('umb-document-workspace') export class UmbDocumentWorkspaceElement extends UmbLitElement implements UmbWorkspaceEntityElement { diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/media/media-types/media-type.detail.store.ts b/src/Umbraco.Web.UI.Client/src/backoffice/media/media-types/media-type.detail.store.ts index bc32dbd8f1..f27ba99859 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/media/media-types/media-type.detail.store.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/media/media-types/media-type.detail.store.ts @@ -1,7 +1,7 @@ -import type { DataTypeDetails } from '@umbraco-cms/models'; +import type { DataTypeDetails, MediaTypeDetails } from '@umbraco-cms/models'; import { UmbContextToken } from '@umbraco-cms/context-api'; import { ArrayState } from '@umbraco-cms/observable-api'; -import { UmbStoreBase } from '@umbraco-cms/store'; +import { UmbEntityDetailStore, UmbStoreBase } from '@umbraco-cms/store'; import { UmbControllerHostInterface } from '@umbraco-cms/controller'; @@ -14,16 +14,22 @@ export const UMB_MEDIA_TYPE_DETAIL_STORE_CONTEXT_TOKEN = new UmbContextToken { - private _data = new ArrayState([], (x) => x.key); + private _data = new ArrayState([], (x) => x.key); constructor(host: UmbControllerHostInterface) { super(host, UMB_MEDIA_TYPE_DETAIL_STORE_CONTEXT_TOKEN.toString()); } + + getScaffold(entityType: string, parentKey: string | null) { + return { + } as MediaTypeDetails; + } + /** * @description - Request a Data Type by key. The Data Type is added to the store and is returned as an Observable. * @param {string} key @@ -36,12 +42,12 @@ export class UmbMediaTypeDetailStore extends UmbStoreBase { // TODO: make sure UI somehow can follow the status of this action. /** - * @description - Save a Data Type. - * @param {Array} dataTypes + * @description - Save a Media Type. + * @param {Array} mediaTypes * @memberof UmbMediaTypesStore * @return {*} {Promise} */ - save(data: DataTypeDetails[]) { + save(data: MediaTypeDetails[]) { return null as any; } diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/media/media/workspace/media-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/backoffice/media/media/workspace/media-workspace.context.ts index c724271abb..991316b869 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/media/media/workspace/media-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/media/media/workspace/media-workspace.context.ts @@ -1,44 +1,28 @@ -import { UmbWorkspaceContentContext } from '../../../shared/components/workspace/workspace-content/workspace-content.context'; -import { - UmbMediaDetailStore, - UMB_MEDIA_DETAIL_STORE_CONTEXT_TOKEN, -} from 'src/backoffice/media/media/media.detail.store'; -import { UmbControllerHostInterface } from '@umbraco-cms/controller'; -import type { MediaDetails } from '@umbraco-cms/models'; +import { UMB_MEDIA_DETAIL_STORE_CONTEXT_TOKEN } from "../media.detail.store"; +import { UmbEntityWorkspaceManager } from "../../../shared/components/workspace/workspace-context/entity-manager-controller"; +import { UmbWorkspaceContext } from "../../../shared/components/workspace/workspace-context/workspace-context"; +import { UmbWorkspaceEntityContextInterface } from "../../../shared/components/workspace/workspace-context/workspace-entity-context.interface"; +import type { MediaDetails } from "@umbraco-cms/models"; -const DefaultMediaData = { - key: '', - name: '', - icon: '', - type: '', - hasChildren: false, - parentKey: '', - isTrashed: false, - properties: [ - { - alias: '', - label: '', - description: '', - dataTypeKey: '', - }, - ], - data: [ - { - alias: '', - value: '', - }, - ], - variants: [ - { - name: '', - }, - ], -} as MediaDetails; +export class UmbWorkspaceMediaContext extends UmbWorkspaceContext implements UmbWorkspaceEntityContextInterface { -export class UmbWorkspaceMediaContext extends UmbWorkspaceContentContext { - constructor(host: UmbControllerHostInterface) { - super(host, DefaultMediaData, UMB_MEDIA_DETAIL_STORE_CONTEXT_TOKEN.toString(), 'media'); + + #manager = new UmbEntityWorkspaceManager(this._host, 'media', UMB_MEDIA_DETAIL_STORE_CONTEXT_TOKEN); + + public readonly data = this.#manager.state.asObservable(); + public readonly name = this.#manager.state.getObservablePart((state) => state?.name); + + setName(name: string) { + this.#manager.state.update({name: name}) } + getEntityType = this.#manager.getEntityType; + getEntityKey = this.#manager.getEntityKey; + getStore = this.#manager.getStore; + getData = this.#manager.getData; + load = this.#manager.load; + create = this.#manager.create; + save = this.#manager.save; + destroy = this.#manager.destroy; public setPropertyValue(alias: string, value: unknown) { throw new Error('setPropertyValue is not implemented for UmbWorkspaceMediaContext'); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/members/member-groups/member-group.details.store.ts b/src/Umbraco.Web.UI.Client/src/backoffice/members/member-groups/member-group.details.store.ts index 8f2f06aec1..2189c2c274 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/members/member-groups/member-group.details.store.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/members/member-groups/member-group.details.store.ts @@ -3,7 +3,7 @@ import type { MemberGroupDetails } from '@umbraco-cms/models'; import { UmbContextToken } from '@umbraco-cms/context-api'; import { ArrayState } from '@umbraco-cms/observable-api'; import { UmbControllerHostInterface } from '@umbraco-cms/controller'; -import { UmbStoreBase } from '@umbraco-cms/store'; +import { UmbEntityDetailStore, UmbStoreBase } from '@umbraco-cms/store'; export const UMB_MEMBER_GROUP_STORE_CONTEXT_TOKEN = new UmbContextToken('UmbMemberGroupStore'); @@ -13,7 +13,7 @@ export const UMB_MEMBER_GROUP_STORE_CONTEXT_TOKEN = new UmbContextToken { #groups = new ArrayState([], x => x.key); @@ -24,11 +24,16 @@ export class UmbMemberGroupStore extends UmbStoreBase { super(host, UMB_MEMBER_GROUP_STORE_CONTEXT_TOKEN.toString()); } - getByKey(key: string): Observable { + getScaffold(entityType: string, parentKey: string | null) { + return { + } as MemberGroupDetails; + } + + getByKey(key: string): Observable { return null as any; } - async save(mediaTypes: Array): Promise { + async save(memberGroups: Array): Promise { return null as any; } } diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/members/member-types/member-type.detail.store.ts b/src/Umbraco.Web.UI.Client/src/backoffice/members/member-types/member-type.detail.store.ts index f74ea20e1c..25511b12e4 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/members/member-types/member-type.detail.store.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/members/member-types/member-type.detail.store.ts @@ -1,7 +1,7 @@ import type { MemberTypeDetails } from '@umbraco-cms/models'; import { UmbContextToken } from '@umbraco-cms/context-api'; import { ArrayState } from '@umbraco-cms/observable-api'; -import { UmbStoreBase } from '@umbraco-cms/store'; +import { UmbEntityDetailStore, UmbStoreBase } from '@umbraco-cms/store'; import { UmbControllerHostInterface } from '@umbraco-cms/controller'; @@ -14,7 +14,7 @@ export const UMB_MEMBER_TYPE_DETAIL_STORE_CONTEXT_TOKEN = new UmbContextToken { #data = new ArrayState([], (x) => x.key); @@ -24,6 +24,11 @@ export class UmbMemberTypeDetailStore extends UmbStoreBase { super(host, UMB_MEMBER_TYPE_DETAIL_STORE_CONTEXT_TOKEN.toString()); } + getScaffold(entityType: string, parentKey: string | null) { + return { + } as MemberTypeDetails; + } + /** * @description - Request a Data Type by key. The Data Type is added to the store and is returned as an Observable. * @param {string} key diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/data-type.detail.store.ts b/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/data-type.detail.store.ts index 0704f2b0b6..6ea6dc4fec 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/data-type.detail.store.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/data-type.detail.store.ts @@ -1,10 +1,8 @@ import type { DataTypeDetails } from '@umbraco-cms/models'; import { UmbContextToken } from '@umbraco-cms/context-api'; import { createObservablePart, ArrayState } from '@umbraco-cms/observable-api'; -import { UmbStoreBase } from '@umbraco-cms/store'; +import { UmbEntityDetailStore, UmbStoreBase } from '@umbraco-cms/store'; import { UmbControllerHostInterface } from '@umbraco-cms/controller'; -import { DataTypeResource } from '@umbraco-cms/backend-api'; -import { tryExecuteAndNotify } from '@umbraco-cms/resources'; export const UMB_DATA_TYPE_DETAIL_STORE_CONTEXT_TOKEN = new UmbContextToken('UmbDataTypeDetailStore'); @@ -16,7 +14,7 @@ export const UMB_DATA_TYPE_DETAIL_STORE_CONTEXT_TOKEN = new UmbContextToken { #data = new ArrayState([], (x) => x.key); @@ -26,6 +24,21 @@ export class UmbDataTypeDetailStore extends UmbStoreBase { super(host, UMB_DATA_TYPE_DETAIL_STORE_CONTEXT_TOKEN.toString()); } + + getScaffold(entityType: string, parentKey: string | null) { + return { + key: '', + name: '', + icon: '', + type: 'data-type', + hasChildren: false, + parentKey: '', + propertyEditorModelAlias: '', + propertyEditorUIAlias: '', + data: [], + } as DataTypeDetails; + } + /** * @description - Request a Data Type by key. The Data Type is added to the store and is returned as an Observable. * @param {string} key diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/workspace/data-type-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/workspace/data-type-workspace.context.ts index de3d6619fd..b751c89e57 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/workspace/data-type-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/workspace/data-type-workspace.context.ts @@ -1,40 +1,50 @@ -import { UmbWorkspaceContentContext } from '../../../shared/components/workspace/workspace-content/workspace-content.context'; -import { UMB_DATA_TYPE_DETAIL_STORE_CONTEXT_TOKEN} from '../../../settings/data-types/data-type.detail.store'; -import type { UmbDataTypeDetailStore} from '../../../settings/data-types/data-type.detail.store'; -import type { DataTypeDetails, DataTypePropertyData } from '@umbraco-cms/models'; -import { UmbControllerHostInterface } from '@umbraco-cms/controller'; +import { UmbWorkspaceContext } from '../../../shared/components/workspace/workspace-context/workspace-context'; +import { UmbWorkspaceEntityContextInterface } from '../../../shared/components/workspace/workspace-context/workspace-entity-context.interface'; +import { UmbEntityWorkspaceManager } from '../../../shared/components/workspace/workspace-context/entity-manager-controller'; +import { UMB_DATA_TYPE_DETAIL_STORE_CONTEXT_TOKEN } from '../data-type.detail.store'; +import type { DataTypeDetails } from '@umbraco-cms/models'; import { appendToFrozenArray } from '@umbraco-cms/observable-api'; -const DefaultDataTypeData = { - key: '', - name: '', - icon: '', - type: 'data-type', - hasChildren: false, - parentKey: '', - propertyEditorModelAlias: '', - propertyEditorUIAlias: '', - data: [], -} as DataTypeDetails; +export class UmbWorkspaceDataTypeContext extends UmbWorkspaceContext implements UmbWorkspaceEntityContextInterface { -export class UmbWorkspaceDataTypeContext extends UmbWorkspaceContentContext< - DataTypeDetails, - UmbDataTypeDetailStore -> { - constructor(host: UmbControllerHostInterface) { - super(host, DefaultDataTypeData, UMB_DATA_TYPE_DETAIL_STORE_CONTEXT_TOKEN.toString(), 'dataType'); + #manager = new UmbEntityWorkspaceManager(this._host, 'data-type', UMB_DATA_TYPE_DETAIL_STORE_CONTEXT_TOKEN); + + + + + + public readonly data = this.#manager.state.asObservable(); + public readonly name = this.#manager.state.getObservablePart((state) => state?.name); + + setName(name: string) { + this.#manager.state.update({name: name}); } + setPropertyEditorModelAlias(alias?: string) { + this.#manager.state.update({propertyEditorModelAlias: alias}); + } + setPropertyEditorUIAlias(alias?: string) { + this.#manager.state.update({propertyEditorUIAlias: alias}); + } + getEntityType = this.#manager.getEntityType; + getEntityKey = this.#manager.getEntityKey; + getStore = this.#manager.getStore; + getData = this.#manager.getData; + load = this.#manager.load; + create = this.#manager.create; + save = this.#manager.save; + destroy = this.#manager.destroy; - public setPropertyValue(alias: string, value: unknown) { - // TODO: make sure to check that we have a details model? otherwise fail? 8This can be relevant if we use the same context for tree actions? - const entry = { alias: alias, value: value }; - const newDataSet = appendToFrozenArray( - (this._data.getValue() as DataTypeDetails).data, - entry, - (x: DataTypePropertyData) => x.alias - ); + // This could eventually be moved out as well? + setPropertyValue(alias: string, value: unknown) { - this.update({ data: newDataSet }); + const entry = {alias: alias, value: value}; + + const currentData = this.#manager.getData(); + if (currentData) { + const newDataSet = appendToFrozenArray(currentData.data, entry, x => x.alias); + + this.#manager.state.update({data: newDataSet}); + } } } diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/workspace/data-type-workspace.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/workspace/data-type-workspace.element.ts index 51c2ecdd44..583f56c2e9 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/workspace/data-type-workspace.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/workspace/data-type-workspace.element.ts @@ -67,7 +67,7 @@ export class UmbDataTypeWorkspaceElement extends UmbLitElement { const target = event.composedPath()[0] as UUIInputElement; if (typeof target?.value === 'string') { - this._workspaceContext.update({ name: target.value }); + this._workspaceContext.setName(target.value); } } } diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/workspace/views/edit/data-type-workspace-view-edit.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/workspace/views/edit/data-type-workspace-view-edit.element.ts index 15555d9fee..7757f2c912 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/workspace/views/edit/data-type-workspace-view-edit.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/workspace/views/edit/data-type-workspace-view-edit.element.ts @@ -69,7 +69,7 @@ export class UmbDataTypeWorkspaceViewEditElement extends UmbLitElement { this._dataType = dataType as DataTypeDetails; if (this._dataType.propertyEditorUIAlias !== this._propertyEditorUIAlias) { - this._observePropertyEditorUI(this._dataType.propertyEditorUIAlias); + this._observePropertyEditorUI(this._dataType.propertyEditorUIAlias || undefined); } if (this._dataType.data !== this._data) { @@ -78,7 +78,7 @@ export class UmbDataTypeWorkspaceViewEditElement extends UmbLitElement { }); } - private _observePropertyEditorUI(propertyEditorUIAlias: string | null) { + private _observePropertyEditorUI(propertyEditorUIAlias?: string) { if (!propertyEditorUIAlias) return; this.observe( @@ -92,7 +92,7 @@ export class UmbDataTypeWorkspaceViewEditElement extends UmbLitElement { this._propertyEditorUIIcon = propertyEditorUI?.meta.icon ?? ''; this._propertyEditorModelAlias = propertyEditorUI?.meta.propertyEditorModel ?? ''; - this._workspaceContext?.update({ propertyEditorModelAlias: this._propertyEditorModelAlias }); + this._workspaceContext?.setPropertyEditorModelAlias(this._propertyEditorModelAlias); } ); } @@ -110,9 +110,9 @@ export class UmbDataTypeWorkspaceViewEditElement extends UmbLitElement { }); } - private _selectPropertyEditorUI(propertyEditorUIAlias: string | null) { + private _selectPropertyEditorUI(propertyEditorUIAlias: string | undefined) { if (!this._dataType || this._dataType.propertyEditorUIAlias === propertyEditorUIAlias) return; - this._workspaceContext?.update({ propertyEditorUIAlias }); + this._workspaceContext?.setPropertyEditorUIAlias(propertyEditorUIAlias); this._observePropertyEditorUI(propertyEditorUIAlias); } diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/variant-selector/variant-selector.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/variant-selector/variant-selector.element.ts index e86656b85c..6f3bc2826d 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/variant-selector/variant-selector.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/variant-selector/variant-selector.element.ts @@ -2,8 +2,7 @@ import { UUITextStyles } from '@umbraco-ui/uui-css/lib'; import { css, html, nothing } from 'lit'; import { customElement, property, state } from 'lit/decorators.js'; import { UUIInputElement, UUIInputEvent } from '@umbraco-ui/uui'; -import { distinctUntilChanged } from 'rxjs'; -import type { UmbWorkspaceContentContext } from '../workspace/workspace-content/workspace-content.context'; +import type { UmbWorkspaceEntityContextInterface } from '../workspace/workspace-context/workspace-entity-context.interface'; import { UmbLitElement } from '@umbraco-cms/element'; import type { ContentTreeItem } from '@umbraco-cms/backend-api'; @@ -45,13 +44,13 @@ export class UmbVariantSelectorElement extends UmbLitElement { @state() _content?: ContentTreeItem; - private _workspaceContext?: UmbWorkspaceContentContext; + private _workspaceContext?: UmbWorkspaceEntityContextInterface; constructor() { super(); // TODO: Figure out how to get the magic string for the workspace context. - this.consumeContext( + this.consumeContext>( 'umbWorkspaceContext', (instance) => { this._workspaceContext = instance; @@ -63,7 +62,7 @@ export class UmbVariantSelectorElement extends UmbLitElement { private async _observeWorkspace() { if (!this._workspaceContext) return; - this.observe(this._workspaceContext.data.pipe(distinctUntilChanged()), (data) => { + this.observe(this._workspaceContext.data, (data) => { this._content = data; }); } @@ -74,7 +73,8 @@ export class UmbVariantSelectorElement extends UmbLitElement { const target = event.composedPath()[0] as UUIInputElement; if (typeof target?.value === 'string') { - this._workspaceContext?.update({ name: target.value }); + // TODO: create a setName method on EntityWorkspace: + //this._workspaceContext?.update({ name: target.value }); } } } diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace-property/workspace-property.context.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace-property/workspace-property.context.ts index c5553a5d74..f3581b8247 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace-property/workspace-property.context.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace-property/workspace-property.context.ts @@ -1,4 +1,4 @@ -import { UmbWorkspaceContentContext } from '../workspace/workspace-content/workspace-content.context'; +import { UmbWorkspaceEntityContextInterface } from '../workspace/workspace-context/workspace-entity-context.interface'; import type { DataTypeDetails } from '@umbraco-cms/models'; import { UmbControllerHostInterface } from '@umbraco-cms/controller'; import { createObservablePart, ObjectState } from '@umbraco-cms/observable-api'; @@ -24,11 +24,11 @@ export class UmbWorkspacePropertyContext { public readonly value = createObservablePart(this._data, (data) => data.value); public readonly config = createObservablePart(this._data, (data) => data.config); - private _workspaceContext?: UmbWorkspaceContentContext; + private _workspaceContext?: UmbWorkspaceEntityContextInterface; constructor(host: UmbControllerHostInterface) { // TODO: Figure out how to get the magic string in a better way. - new UmbContextConsumerController(host, 'umbWorkspaceContext', (workspaceContext) => { + new UmbContextConsumerController(host, 'umbWorkspaceContext', (workspaceContext) => { this._workspaceContext = workspaceContext; }); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/actions/save/workspace-action-node-save.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/actions/save/workspace-action-node-save.element.ts index 733eb1d661..29fac1453f 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/actions/save/workspace-action-node-save.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/actions/save/workspace-action-node-save.element.ts @@ -2,7 +2,7 @@ import { css, html } from 'lit'; import { customElement, state } from 'lit/decorators.js'; import { UUITextStyles } from '@umbraco-ui/uui-css/lib'; import type { UUIButtonState } from '@umbraco-ui/uui'; -import { UmbWorkspaceContentContext } from '../../workspace-content/workspace-content.context'; +import { UmbWorkspaceEntityContextInterface } from '../../workspace-context/workspace-entity-context.interface'; import { UmbLitElement } from '@umbraco-cms/element'; import type { ManifestWorkspaceAction } from '@umbraco-cms/models'; @@ -13,7 +13,7 @@ export class UmbWorkspaceActionNodeSaveElement extends UmbLitElement { @state() private _saveButtonState?: UUIButtonState; - private _workspaceContext?: UmbWorkspaceContentContext; + private _workspaceContext?: UmbWorkspaceEntityContextInterface; public manifest?: ManifestWorkspaceAction; @@ -21,7 +21,7 @@ export class UmbWorkspaceActionNodeSaveElement extends UmbLitElement { super(); // TODO: Figure out how to get the magic string for the workspace context. - this.consumeContext('umbWorkspaceContext', (instance) => { + this.consumeContext('umbWorkspaceContext', (instance) => { this._workspaceContext = instance; }); } diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-content/views/collection/workspace-view-collection.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-content/views/collection/workspace-view-collection.element.ts index 8a09c6e2af..d4f1646864 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-content/views/collection/workspace-view-collection.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-content/views/collection/workspace-view-collection.element.ts @@ -2,7 +2,6 @@ import { css, html } from 'lit'; import { UUITextStyles } from '@umbraco-ui/uui-css/lib'; import { customElement } from 'lit/decorators.js'; import { ifDefined } from 'lit-html/directives/if-defined.js'; -import { UmbWorkspaceContentContext } from '../../workspace-content.context'; import { UmbMediaTreeStore } from '../../../../../../media/media/media.tree.store'; import { UmbCollectionContext, @@ -11,6 +10,7 @@ import { import '../../../../../../shared/components/content-property/content-property.element'; import '../../../../../../shared/collection/dashboards/dashboard-collection.element'; +import { UmbWorkspaceEntityContextInterface } from '../../../workspace-context/workspace-entity-context.interface'; import { UmbLitElement } from '@umbraco-cms/element'; import { FolderTreeItem } from '@umbraco-cms/backend-api'; import { ManifestWorkspaceViewCollection } from '@umbraco-cms/extensions-registry'; @@ -29,7 +29,7 @@ export class UmbWorkspaceViewCollectionElement extends UmbLitElement { public manifest!: ManifestWorkspaceViewCollection; - private _workspaceContext?: UmbWorkspaceContentContext; + private _workspaceContext?: UmbWorkspaceEntityContextInterface; private _collectionContext?: UmbCollectionContext; @@ -37,20 +37,21 @@ export class UmbWorkspaceViewCollectionElement extends UmbLitElement { super(); // TODO: Figure out how to get the magic string for the workspace context. - this.consumeContext('umbWorkspaceContext', (nodeContext) => { + this.consumeContext('umbWorkspaceContext', (nodeContext) => { this._workspaceContext = nodeContext; this._provideWorkspace(); }); } protected _provideWorkspace() { - if (this._workspaceContext?.entityKey != null) { + const entityKey = this._workspaceContext?.getEntityKey(); + if (entityKey != null) { const manifestMeta = this.manifest.meta; this._collectionContext = new UmbCollectionContext( this, - this._workspaceContext.entityKey, + entityKey, manifestMeta.storeAlias ); this.provideContext(UMB_COLLECTION_CONTEXT_TOKEN, this._collectionContext); @@ -58,7 +59,7 @@ export class UmbWorkspaceViewCollectionElement extends UmbLitElement { } render() { - return html``; + return html``; } } diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-content/views/edit/workspace-view-content-edit.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-content/views/edit/workspace-view-content-edit.element.ts index d10cf56215..12cc9731e9 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-content/views/edit/workspace-view-content-edit.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-content/views/edit/workspace-view-content-edit.element.ts @@ -3,7 +3,7 @@ import { UUITextStyles } from '@umbraco-ui/uui-css/lib'; import { customElement, state } from 'lit/decorators.js'; import { distinctUntilChanged } from 'rxjs'; import { repeat } from 'lit/directives/repeat.js'; -import { UmbWorkspaceContentContext } from '../../workspace-content.context'; +import type { UmbWorkspaceEntityContextInterface } from '../../../workspace-context/workspace-entity-context.interface'; import type { ContentProperty, ContentPropertyData, DocumentDetails, MediaDetails } from '@umbraco-cms/models'; import '../../../../content-property/content-property.element'; @@ -27,13 +27,13 @@ export class UmbWorkspaceViewContentEditElement extends UmbLitElement { @state() _data: ContentPropertyData[] = []; - private _workspaceContext?: UmbWorkspaceContentContext; + private _workspaceContext?: UmbWorkspaceEntityContextInterface; constructor() { super(); // TODO: Figure out how to get the magic string for the workspace context. - this.consumeContext>( + this.consumeContext>( 'umbWorkspaceContext', (workspaceContext) => { this._workspaceContext = workspaceContext; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-content/views/info/workspace-view-content-info.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-content/views/info/workspace-view-content-info.element.ts index ae148d8937..8db59d9664 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-content/views/info/workspace-view-content-info.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-content/views/info/workspace-view-content-info.element.ts @@ -1,7 +1,7 @@ import { css, html } from 'lit'; import { UUITextStyles } from '@umbraco-ui/uui-css/lib'; import { customElement, state } from 'lit/decorators.js'; -import type { UmbWorkspaceContentContext } from '../../workspace-content.context'; +import { UmbWorkspaceEntityContextInterface } from '../../../workspace-context/workspace-entity-context.interface'; import type { DocumentDetails, MediaDetails } from '@umbraco-cms/models'; import { UmbLitElement } from '@umbraco-cms/element'; @@ -20,13 +20,13 @@ export class UmbWorkspaceViewContentInfoElement extends UmbLitElement { @state() private _nodeName = ''; - private _workspaceContext?: UmbWorkspaceContentContext; + private _workspaceContext?: UmbWorkspaceEntityContextInterface; constructor() { super(); // TODO: Figure out how to get the magic string for the workspace context. - this.consumeContext>( + this.consumeContext>( 'umbWorkspaceContext', (nodeContext) => { this._workspaceContext = nodeContext; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-content/workspace-content.context.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-content/workspace-content.context.ts deleted file mode 100644 index b55c24255e..0000000000 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-content/workspace-content.context.ts +++ /dev/null @@ -1,125 +0,0 @@ -import { v4 as uuidv4 } from 'uuid'; -import { UmbNotificationService, UMB_NOTIFICATION_SERVICE_CONTEXT_TOKEN, UmbNotificationDefaultData } from '@umbraco-cms/notification'; -import { UmbControllerHostInterface } from '@umbraco-cms/controller'; -import { UmbContextConsumerController, UmbContextProviderController } from '@umbraco-cms/context-api'; -import { ObjectState, UmbObserverController, createObservablePart } from '@umbraco-cms/observable-api'; -import { UmbContentStore } from '@umbraco-cms/store'; -import type { ContentTreeItem } from '@umbraco-cms/backend-api'; - -// TODO: Consider if its right to have this many class-inheritance of WorkspaceContext -// TODO: Could we extract this code into a 'Manager' of its own, which will be instantiated by the concrete Workspace Context. This will be more transparent and 'reuseable' -export abstract class UmbWorkspaceContentContext< - ContentTypeType extends ContentTreeItem = ContentTreeItem, - StoreType extends UmbContentStore = UmbContentStore -> { - protected _host: UmbControllerHostInterface; - - // TODO: figure out how fine grained we want to make our observables. - // TODO: add interface - protected _data; - public readonly data; - public readonly name; - - protected _notificationService?: UmbNotificationService; - - protected _store: StoreType | null = null; - protected _storeSubscription?: UmbObserverController; - - #isNew = true; - - public entityKey?: string; - public entityType: string; - - constructor(host: UmbControllerHostInterface, defaultData: ContentTypeType, storeAlias: string, entityType: string) { - this._host = host; - - this._data = new ObjectState(defaultData); - this.data = this._data.asObservable(); - this.name = createObservablePart(this._data, (data) => data.name); - - this.entityType = entityType; - - new UmbContextConsumerController(host, UMB_NOTIFICATION_SERVICE_CONTEXT_TOKEN, (_instance) => { - this._notificationService = _instance; - }); - - new UmbContextConsumerController(host, storeAlias, (_instance: StoreType) => { - this._store = _instance; - if (!this._store) { - // TODO: make sure to break the application in a good way. - return; - } - this._observeStore(); - - // TODO: first provide when we have umbNotificationService as well. - new UmbContextProviderController(this._host, 'umbWorkspaceContext', this); - }); - } - - public getData() { - return this._data.getValue(); - } - public update(data: Partial) { - this._data.next({ ...this.getData(), ...data }); - } - - load(entityKey: string) { - this.#isNew = false; - this.entityKey = entityKey; - this._observeStore(); - } - - create(parentKey: string | null) { - this.#isNew = true; - this.entityKey = uuidv4(); - console.log("I'm new, and I will be created under ", parentKey); - } - - protected _observeStore(): void { - if (!this._store || !this.entityKey) { - return; - } - - if (!this.#isNew) { - this._storeSubscription?.destroy(); - this._storeSubscription = new UmbObserverController( - this._host, - this._store.getByKey(this.entityKey), - (content) => { - if (!content) return; // TODO: Handle nicely if there is no content data. - this.update(content as any); - } - ); - } - } - - public getStore() { - return this._store; - } - - abstract setPropertyValue(alias: string, value: unknown): void; - - // TODO: consider turning this into an abstract so each context implement this them selfs. - public save(): Promise { - if (!this._store) { - // TODO: add a more beautiful error: - console.error('Could not save cause workspace context has no store.'); - return Promise.resolve(); - } - return this._store - .save([this.getData()]) - .then(() => { - const data: UmbNotificationDefaultData = { message: 'Document Saved' }; - this._notificationService?.peek('positive', { data }); - }) - .catch(() => { - const data: UmbNotificationDefaultData = { message: 'Failed to save Document' }; - this._notificationService?.peek('danger', { data }); - }); - } - - // TODO: how can we make sure to call this, we might need to turn this thing into a ContextProvider(extending) for it to call destroy? - public destroy(): void { - this._data.unsubscribe(); - } -} diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-context/entity-manager-controller.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-context/entity-manager-controller.ts new file mode 100644 index 0000000000..12f1e41c9c --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-context/entity-manager-controller.ts @@ -0,0 +1,133 @@ +import { v4 as uuidv4 } from 'uuid'; +import { UmbContextConsumerController, UmbContextToken } from "@umbraco-cms/context-api"; +import { UmbControllerHostInterface } from "@umbraco-cms/controller"; +import { UmbNotificationDefaultData, UmbNotificationService, UMB_NOTIFICATION_SERVICE_CONTEXT_TOKEN } from "@umbraco-cms/notification"; +import { ObjectState, UmbObserverController } from "@umbraco-cms/observable-api"; +import { EntityTreeItem } from '@umbraco-cms/backend-api'; +import { UmbEntityDetailStore } from '@umbraco-cms/store'; + + +// Extend entityType base type?, so we are sure to have parentKey? +// TODO: switch to use EntityDetailItem ? if we can have such type? +export class UmbEntityWorkspaceManager, EntityDetailsType extends EntityTreeItem = ReturnType> { + + + private _host; + + state = new ObjectState(undefined); + + + + protected _storeSubscription?: UmbObserverController; + + private _notificationService?: UmbNotificationService; + private _store?: StoreType; + + + #isNew = false; + private _entityType; + private _entityKey?: string; + + private _createAtParentKey?: string | null; + + + constructor( + host: UmbControllerHostInterface, + entityType:string, + storeToken: UmbContextToken + ) { + this._host = host; + this._entityType = entityType; + + new UmbContextConsumerController(this._host, UMB_NOTIFICATION_SERVICE_CONTEXT_TOKEN, (_instance) => { + this._notificationService = _instance; + }); + + // Create controller holding Token? + new UmbContextConsumerController(this._host, storeToken, (_instance) => { + this._store = _instance; + this._observeStore(); + }); + } + + private _observeStore() { + if (!this._store || !this._entityKey) { + return; + } + + if (this.#isNew) { + const newData = this._store.getScaffold(this._entityType, this._createAtParentKey || null); + this.state.next(newData); + } else { + this._storeSubscription?.destroy(); + this._storeSubscription = new UmbObserverController( + this._host, + this._store.getByKey(this._entityKey), + (content) => { + if (!content) return; // TODO: Handle nicely if there is no content data. + this.state.next(content as any); + } + ); + } + } + + getEntityType() { + return this._entityType; + } + getEntityKey() { + return this._entityKey; + } + + getStore() { + return this._store; + } + + getData() { + return this.state.getValue(); + } + + load(entityKey: string) { + this.#isNew = false; + this._entityKey = entityKey; + this._observeStore(); + } + + create(parentKey: string | null) { + this.#isNew = true; + this._entityKey = uuidv4(); + this._createAtParentKey = parentKey; + } + + save(): Promise { + + if (!this._store) { + // TODO: add a more beautiful error: + console.error('Could not save cause workspace context has no store.'); + return Promise.resolve(); + } + + const documentData = this.getData(); + if(!documentData) { + console.error('Could not save cause workspace context has no data.'); + return Promise.resolve(); + } + + return this._store + .save([documentData]) + .then(() => { + const data: UmbNotificationDefaultData = { message: 'Document Saved' }; + this._notificationService?.peek('positive', { data }); + }) + .catch(() => { + const data: UmbNotificationDefaultData = { message: 'Failed to save Document' }; + this._notificationService?.peek('danger', { data }); + }); + } + + + public destroy(): void { + this.state.unsubscribe(); + } + + +} diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-context/workspace-entity-context.interface.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-context/workspace-entity-context.interface.ts index 6172536a23..206e433ea4 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-context/workspace-entity-context.interface.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-context/workspace-entity-context.interface.ts @@ -1,6 +1,7 @@ -import { Observable } from "rxjs"; +import type { Observable } from "rxjs"; +import { UmbEntityDetailStore } from "@umbraco-cms/store"; -export interface UmbWorkspaceEntityContextInterface { +export interface UmbWorkspaceEntityContextInterface { readonly data: Observable; @@ -9,6 +10,8 @@ export interface UmbWorkspaceEntityContextInterface { //entityKey?: string; //entityType: string; + getEntityKey(): string | undefined; + getEntityType(): string; getData(): T; @@ -16,7 +19,7 @@ export interface UmbWorkspaceEntityContextInterface { create(parentKey: string | null): void; - getStore(): unknown; + getStore(): UmbEntityDetailStore | undefined; setPropertyValue(alias: string, value: unknown): void; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/dictionary.detail.store.ts b/src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/dictionary.detail.store.ts index e22c529d0d..88ba424035 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/dictionary.detail.store.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/dictionary.detail.store.ts @@ -1,7 +1,7 @@ import type { DictionaryDetails } from '@umbraco-cms/models'; import { UmbContextToken } from '@umbraco-cms/context-api'; import { createObservablePart, ArrayState } from '@umbraco-cms/observable-api'; -import { UmbStoreBase } from '@umbraco-cms/store'; +import { UmbEntityDetailStore, UmbStoreBase } from '@umbraco-cms/store'; import { UmbControllerHostInterface } from '@umbraco-cms/controller'; import { EntityTreeItem } from '@umbraco-cms/backend-api'; @@ -15,7 +15,8 @@ export const UMB_DICTIONARY_DETAIL_STORE_CONTEXT_TOKEN = new UmbContextToken { // TODO: use the right type: @@ -26,6 +27,12 @@ export class UmbDictionaryDetailStore extends UmbStoreBase { super(host, UMB_DICTIONARY_DETAIL_STORE_CONTEXT_TOKEN.toString()); } + + getScaffold(entityType: string, parentKey: string | null) { + return { + } as EntityTreeItem; + } + /** * @description - Request a Data Type by key. The Data Type is added to the store and is returned as an Observable. * @param {string} key diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/users/user-groups/user-group.store.ts b/src/Umbraco.Web.UI.Client/src/backoffice/users/user-groups/user-group.store.ts index 31f3b99a22..b5ac06137a 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/users/user-groups/user-group.store.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/users/user-groups/user-group.store.ts @@ -2,10 +2,10 @@ import type { UserGroupDetails } from '@umbraco-cms/models'; import { UmbContextToken } from '@umbraco-cms/context-api'; import { UmbControllerHostInterface } from '@umbraco-cms/controller'; import { createObservablePart, ArrayState } from '@umbraco-cms/observable-api'; -import { UmbStoreBase } from '@umbraco-cms/store'; +import { UmbEntityDetailStore, UmbStoreBase } from '@umbraco-cms/store'; // TODO: get rid of this type addition & { ... }: -export type UmbUserGroupStoreItemType = UserGroupDetails & { users?: Array }; +//export type UmbUserGroupStoreItemType = UserGroupDetails & { users?: Array }; export const UMB_USER_GROUP_STORE_CONTEXT_TOKEN = new UmbContextToken('UmbUserGroupStore'); @@ -15,10 +15,10 @@ export const UMB_USER_GROUP_STORE_CONTEXT_TOKEN = new UmbContextToken { - #groups = new ArrayState([], x => x.key); + #groups = new ArrayState([], x => x.key); public groups = this.#groups.asObservable(); @@ -26,6 +26,22 @@ export class UmbUserGroupStore extends UmbStoreBase { super(host, UMB_USER_GROUP_STORE_CONTEXT_TOKEN.toString()); } + + + getScaffold(entityType: string, parentKey: string | null) { + return { + key: '', + name: '', + icon: '', + type: 'user-group', + hasChildren: false, + parentKey: '', + sections: [], + permissions: [], + users: [], + } as UserGroupDetails; + } + getAll() { // TODO: use Fetcher API. // TODO: only fetch if the data type is not in the store? @@ -61,10 +77,10 @@ export class UmbUserGroupStore extends UmbStoreBase { return createObservablePart(this.groups, (items) => items.filter(node => keys.includes(node.key))); } - async save(userGroups: Array) { + async save(userGroups: Array) { // TODO: use Fetcher API. - // TODO: implement so user group store updates the + // TODO: implement so user group store updates the users, but these needs to save as well..? /* if (this._userStore && userGroup.users) { await this._userStore.updateUserGroup(userGroup.users, userGroup.key); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/users/user-groups/workspace/user-group-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/backoffice/users/user-groups/workspace/user-group-workspace.context.ts index a7168973a9..9430d100b2 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/users/user-groups/workspace/user-group-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/users/user-groups/workspace/user-group-workspace.context.ts @@ -1,27 +1,30 @@ -import { UmbWorkspaceContentContext } from '../../../shared/components/workspace/workspace-content/workspace-content.context'; -import { UMB_USER_STORE_CONTEXT_TOKEN } from '../../users/user.store'; -import type { UmbUserGroupStore, UmbUserGroupStoreItemType } from 'src/backoffice/users/user-groups/user-group.store'; -import { UmbControllerHostInterface } from '@umbraco-cms/controller'; +import { UmbEntityWorkspaceManager } from '../../../shared/components/workspace/workspace-context/entity-manager-controller'; +import { UmbWorkspaceContext } from '../../../shared/components/workspace/workspace-context/workspace-context'; +import { UmbWorkspaceEntityContextInterface } from '../../../shared/components/workspace/workspace-context/workspace-entity-context.interface'; +import { UMB_USER_GROUP_STORE_CONTEXT_TOKEN } from '../user-group.store'; +import type { UserGroupDetails } from '@umbraco-cms/models'; -const DefaultDataTypeData = { - key: '', - name: '', - icon: '', - type: 'user-group', - hasChildren: false, - parentKey: '', - sections: [], - permissions: [], - users: [], -} as UmbUserGroupStoreItemType; -export class UmbWorkspaceUserGroupContext extends UmbWorkspaceContentContext< - UmbUserGroupStoreItemType, - UmbUserGroupStore -> { - constructor(host: UmbControllerHostInterface) { - super(host, DefaultDataTypeData, UMB_USER_STORE_CONTEXT_TOKEN.toString(), 'userGroup'); +export class UmbWorkspaceUserGroupContext extends UmbWorkspaceContext implements UmbWorkspaceEntityContextInterface { + + + + #manager = new UmbEntityWorkspaceManager(this._host, 'user-group', UMB_USER_GROUP_STORE_CONTEXT_TOKEN); + + public readonly data = this.#manager.state.asObservable(); + public readonly name = this.#manager.state.getObservablePart((state) => state?.name); + + setName(name: string) { + this.#manager.state.update({name: name}) } + getEntityType = this.#manager.getEntityType; + getEntityKey = this.#manager.getEntityKey; + getStore = this.#manager.getStore; + getData = this.#manager.getData; + load = this.#manager.load; + create = this.#manager.create; + save = this.#manager.save; + destroy = this.#manager.destroy; public setPropertyValue(alias: string, value: unknown) { throw new Error('setPropertyValue is not implemented for UmbWorkspaceUserGroupContext'); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/users/user-groups/workspace/user-group-workspace.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/users/user-groups/workspace/user-group-workspace.element.ts index 12af723b84..e0778511f9 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/users/user-groups/workspace/user-group-workspace.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/users/user-groups/workspace/user-group-workspace.element.ts @@ -256,39 +256,52 @@ export class UmbUserGroupWorkspaceElement extends UmbLitElement implements UmbWo // TODO: handle if there is no users. if (!this._userKeys && users.length > 0) { this._userKeys = users.filter((user) => user.userGroups.includes(this.entityKey)).map((user) => user.key); - this._updateProperty('users', this._userKeys); + //this._updateProperty('users', this._userKeys); + // TODO: make a method on the UmbWorkspaceUserGroupContext: + //this._workspaceContext.setUsers(); } }); } private _updateUserKeys(userKeys: Array) { this._userKeys = userKeys; - this._updateProperty('users', this._userKeys); + // TODO: make a method on the UmbWorkspaceUserGroupContext: + //this._workspaceContext.setUsers(); } - private _updateProperty(propertyName: string, value: unknown) { - this._workspaceContext?.update({ [propertyName]: value }); - } private _updatePermission(permission: { name: string; description: string; value: boolean }) { if (!this._workspaceContext) return; const checkValue = this._checkPermission(permission); - const selectedPermissions = this._workspaceContext.getData().permissions; + //const selectedPermissions = this._workspaceContext.getData().permissions; + // TODO: make a method on the UmbWorkspaceUserGroupContext: + //const selectedPermissions = this._workspaceContext.getPermissions(); + /* let newPermissions = []; if (checkValue === false) { newPermissions = [...selectedPermissions, permission.name]; } else { newPermissions = selectedPermissions.filter((p) => p !== permission.name); } - this._updateProperty('permissions', newPermissions); + */ + + //this._updateProperty('permissions', newPermissions); + // TODO: make a method on the UmbWorkspaceUserGroupContext: + //this._workspaceContext.setPermissions(); } private _checkPermission(permission: { name: string; description: string; value: boolean }) { if (!this._workspaceContext) return false; - return this._workspaceContext.getData().permissions.includes(permission.name); + //return this._workspaceContext.getPermissions().includes(permission.name); + return false; + } + + private _updateSections(value: string[]) { + console.log("To be done"); + //this._workspaceContext.setSections(value); } private renderLeftColumn() { @@ -300,7 +313,7 @@ export class UmbUserGroupWorkspaceElement extends UmbLitElement implements UmbWo this._updateProperty('sections', e.target.value)}> + @change=${(e: any) => this._updateSections(e.target.value)}> ('U * @extends {UmbStoreBase} * @description - Data Store for Users */ -export class UmbUserStore extends UmbStoreBase { +export class UmbUserStore extends UmbStoreBase implements UmbEntityDetailStore { #users = new ArrayState([], x => x.key); @@ -29,6 +29,27 @@ export class UmbUserStore extends UmbStoreBase { } + getScaffold(entityType: string, parentKey: string | null) { + return { + key: '', + name: '', + icon: '', + type: 'user', + hasChildren: false, + parentKey: '', + email: '', + language: '', + status: 'enabled', + updateDate: '8/27/2022', + createDate: '9/19/2022', + failedLoginAttempts: 0, + userGroups: [], + contentStartNodes: [], + mediaStartNodes: [], + } as UserDetails; + } + + getAll() { // TODO: use Fetcher API. // TODO: only fetch if the data type is not in the store? diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/users/users/workspace/user-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/backoffice/users/users/workspace/user-workspace.context.ts index 1393c00767..414e6d76b0 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/users/users/workspace/user-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/users/users/workspace/user-workspace.context.ts @@ -1,33 +1,30 @@ -import { UmbWorkspaceContentContext } from '../../../shared/components/workspace/workspace-content/workspace-content.context'; -import { - UmbUserStore, - UmbUserStoreItemType, - UMB_USER_STORE_CONTEXT_TOKEN, -} from '../../users/user.store'; -import { UmbControllerHostInterface } from '@umbraco-cms/controller'; +import { UMB_USER_STORE_CONTEXT_TOKEN } from '../../users/user.store'; +import { UmbWorkspaceContext } from '../../../shared/components/workspace/workspace-context/workspace-context'; +import { UmbWorkspaceEntityContextInterface } from '../../../shared/components/workspace/workspace-context/workspace-entity-context.interface'; +import { UmbEntityWorkspaceManager } from '../../../shared/components/workspace/workspace-context/entity-manager-controller'; +import type { UserDetails } from '@umbraco-cms/models'; -const DefaultDataTypeData = { - key: '', - name: '', - icon: '', - type: 'user', - hasChildren: false, - parentKey: '', - email: '', - language: '', - status: 'enabled', - updateDate: '8/27/2022', - createDate: '9/19/2022', - failedLoginAttempts: 0, - userGroups: [], - contentStartNodes: [], - mediaStartNodes: [], -} as UmbUserStoreItemType; +export class UmbWorkspaceUserContext extends UmbWorkspaceContext implements UmbWorkspaceEntityContextInterface { -export class UmbWorkspaceUserContext extends UmbWorkspaceContentContext { - constructor(host: UmbControllerHostInterface) { - super(host, DefaultDataTypeData, UMB_USER_STORE_CONTEXT_TOKEN.toString(), 'user'); + #manager = new UmbEntityWorkspaceManager(this._host, 'user', UMB_USER_STORE_CONTEXT_TOKEN); + + public readonly data = this.#manager.state.asObservable(); + public readonly name = this.#manager.state.getObservablePart((state) => state?.name); + + // TODO: remove this magic connection, instead create the necessary methods to update parts. + update = this.#manager.state.update; + + setName(name: string) { + this.#manager.state.update({name: name}) } + getEntityType = this.#manager.getEntityType; + getEntityKey = this.#manager.getEntityKey; + getStore = this.#manager.getStore; + getData = this.#manager.getData; + load = this.#manager.load; + create = this.#manager.create; + save = this.#manager.save; + destroy = this.#manager.destroy; public setPropertyValue(alias: string, value: unknown) { throw new Error('setPropertyValue is not implemented for UmbWorkspaceUserContext'); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/users/users/workspace/user-workspace.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/users/users/workspace/user-workspace.element.ts index cb7f0f9d33..b5f87f4816 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/users/users/workspace/user-workspace.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/users/users/workspace/user-workspace.element.ts @@ -123,7 +123,7 @@ export class UmbUserWorkspaceElement extends UmbLitElement implements UmbWorkspa this.observe(this._workspaceContext.data.pipe(distinctUntilChanged()), (user) => { this._user = user; - if (user.name !== this._userName) { + if (user && user.name !== this._userName) { this._userName = user.name; } }); From d57e8344295732c2b33a691e28fc621fce91c09d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Fri, 27 Jan 2023 09:50:27 +0100 Subject: [PATCH 21/35] better comment --- src/Umbraco.Web.UI.Client/libs/store/store.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/libs/store/store.ts b/src/Umbraco.Web.UI.Client/libs/store/store.ts index dbaf6e17a2..65b063342a 100644 --- a/src/Umbraco.Web.UI.Client/libs/store/store.ts +++ b/src/Umbraco.Web.UI.Client/libs/store/store.ts @@ -44,7 +44,7 @@ export interface UmbEntityDetailStore extends UmbDataStore { export interface UmbContentStore extends UmbEntityDetailStore { - // TODO: make something that is specific for UmbContentStore + // TODO: make something that is specific for UmbContentStore, or then we should get rid of it. But for now i kept it as we might want this for rollback or other things specific to Content types. save(data: T[]): Promise; } From 3457fca8f0a1a4fc408db4ae7d1bf776193fb559 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Fri, 27 Jan 2023 09:51:54 +0100 Subject: [PATCH 22/35] remove logs --- .../shared/components/section/section.element.ts | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section.element.ts index c60b1e147d..b49f765ba7 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section.element.ts @@ -1,13 +1,12 @@ import { UUITextStyles } from '@umbraco-ui/uui-css/lib'; import { css, html, nothing } from 'lit'; import { customElement, state } from 'lit/decorators.js'; -import { map, switchMap, EMPTY, of } from 'rxjs'; +import { map } from 'rxjs'; import { IRoutingInfo } from 'router-slot'; import type { UmbWorkspaceEntityElement } from '../workspace/workspace-entity-element.interface'; import { UmbSectionContext, UMB_SECTION_CONTEXT_TOKEN } from './section.context'; -import { createExtensionElement } from '@umbraco-cms/extensions-api'; import type { ManifestSectionView, ManifestWorkspace, ManifestSidebarMenuItem } from '@umbraco-cms/models'; -import { umbExtensionsRegistry } from '@umbraco-cms/extensions-api'; +import { umbExtensionsRegistry, createExtensionElement } from '@umbraco-cms/extensions-api'; import { UmbLitElement } from '@umbraco-cms/element'; import './section-sidebar-menu/section-sidebar-menu.element.ts'; @@ -90,7 +89,6 @@ export class UmbSectionElement extends UmbLitElement { private _createMenuRoutes() { - console.log("_createMenuRoutes") // TODO: find a way to make this reuseable across: const workspaceRoutes = this._workspaces?.map((workspace: ManifestWorkspace) => { return [ @@ -170,8 +168,6 @@ export class UmbSectionElement extends UmbLitElement { private _createViewRoutes() { - console.log("_createViewRoutes") - this._routes = this._views?.map((view) => { return { From 188a754f7f081d30a98d11361ed50550246a1f7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Fri, 27 Jan 2023 10:06:10 +0100 Subject: [PATCH 23/35] instead of properties its now methods --- .../document-type-workspace.context.ts | 3 +- .../document-type-workspace.element.ts | 24 ++++++---------- .../workspace/document-workspace.context.ts | 3 +- .../workspace/document-workspace.element.ts | 19 ++++--------- .../workspace/media-workspace.context.ts | 1 + .../workspace/data-type-workspace.context.ts | 1 + .../components/section/section.element.ts | 6 ++-- .../variant-selector.element.ts | 4 ++- .../entity-manager-controller.ts | 4 +-- .../workspace-context.interface.ts | 16 +++++++++++ .../workspace-entity-context.interface.ts | 15 ++-------- .../workspace-entity-element.interface.ts | 4 +-- .../workspace/user-group-workspace.context.ts | 1 + .../workspace/user-group-workspace.element.ts | 28 ++++++++----------- .../users/workspace/user-workspace.context.ts | 1 + .../users/workspace/user-workspace.element.ts | 24 ++++++---------- 16 files changed, 69 insertions(+), 85 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-context/workspace-context.interface.ts diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/workspace/document-type-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/workspace/document-type-workspace.context.ts index 537335a1be..949cb7f5e4 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/workspace/document-type-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/workspace/document-type-workspace.context.ts @@ -1,7 +1,7 @@ import { UmbEntityWorkspaceManager } from '../../../shared/components/workspace/workspace-context/entity-manager-controller'; import { UmbWorkspaceContext } from '../../../shared/components/workspace/workspace-context/workspace-context'; import { UmbWorkspaceEntityContextInterface } from '../../../shared/components/workspace/workspace-context/workspace-entity-context.interface'; -import { UmbDocumentTypeDetailStore, UMB_DOCUMENT_TYPE_DETAIL_STORE_CONTEXT_TOKEN } from '../document-type.detail.store'; +import { UMB_DOCUMENT_TYPE_DETAIL_STORE_CONTEXT_TOKEN } from '../document-type.detail.store'; import type { DocumentTypeDetails } from '@umbraco-cms/models'; export class UmbWorkspaceDocumentTypeContext extends UmbWorkspaceContext implements UmbWorkspaceEntityContextInterface { @@ -19,6 +19,7 @@ export class UmbWorkspaceDocumentTypeContext extends UmbWorkspaceContext impleme this.#manager.state.update({icon: icon}) } getEntityType = this.#manager.getEntityType; + getUnique = this.#manager.getEntityKey; getEntityKey = this.#manager.getEntityKey; getStore = this.#manager.getStore; getData = this.#manager.getData; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/workspace/document-type-workspace.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/workspace/document-type-workspace.element.ts index 1bf3b63e90..f2aabc22ff 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/workspace/document-type-workspace.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/workspace/document-type-workspace.element.ts @@ -46,22 +46,6 @@ export class UmbDocumentTypeWorkspaceElement extends UmbLitElement implements Um name: 'umb:document-dashed-line', }; - private _entityKey!: string; - @property() - public get entityKey(): string { - return this._entityKey; - } - public set entityKey(value: string) { - this._entityKey = value; - if (this._entityKey) { - this._workspaceContext?.load(this._entityKey); - } - } - - @property() - public set create(parentKey: string | null) { - this._workspaceContext?.create(parentKey); - } private _workspaceContext: UmbWorkspaceDocumentTypeContext = new UmbWorkspaceDocumentTypeContext(this); @@ -83,6 +67,14 @@ export class UmbDocumentTypeWorkspaceElement extends UmbLitElement implements Um }); } + public load(entityKey: string) { + this._workspaceContext.load(entityKey); + } + + public create(parentKey: string | null) { + this._workspaceContext.create(parentKey); + } + // TODO. find a way where we don't have to do this for all workspaces. private _handleInput(event: UUIInputEvent) { if (event instanceof UUIInputEvent) { diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/document-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/document-workspace.context.ts index 3618663545..b6f9dd6516 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/document-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/document-workspace.context.ts @@ -1,4 +1,4 @@ -import { UmbDocumentDetailStore, UMB_DOCUMENT_DETAIL_STORE_CONTEXT_TOKEN } from '../document.detail.store'; +import { UMB_DOCUMENT_DETAIL_STORE_CONTEXT_TOKEN } from '../document.detail.store'; import type { UmbWorkspaceEntityContextInterface } from '../../../shared/components/workspace/workspace-context/workspace-entity-context.interface'; import { UmbWorkspaceContext } from '../../../shared/components/workspace/workspace-context/workspace-context'; import { UmbEntityWorkspaceManager } from '../../../shared/components/workspace/workspace-context/entity-manager-controller'; @@ -17,6 +17,7 @@ export class UmbDocumentWorkspaceContext extends UmbWorkspaceContext implements this.#manager.state.update({name: name}) } getEntityType = this.#manager.getEntityType; + getUnique = this.#manager.getEntityKey; getEntityKey = this.#manager.getEntityKey; getStore = this.#manager.getStore; getData = this.#manager.getData; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/document-workspace.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/document-workspace.element.ts index 0a2301f756..55ddd6a39d 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/document-workspace.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/document-workspace.element.ts @@ -18,25 +18,16 @@ export class UmbDocumentWorkspaceElement extends UmbLitElement implements UmbWor `, ]; - private _entityKey!: string; - @property() - public get entityKey(): string { - return this._entityKey; - } - public set entityKey(value: string) { - this._entityKey = value; - if (this._entityKey) { - this._workspaceContext.load(this._entityKey); - } + private _workspaceContext: UmbDocumentWorkspaceContext = new UmbDocumentWorkspaceContext(this); + + public load(entityKey: string) { + this._workspaceContext.load(entityKey); } - @property() - public set create(parentKey: string | null) { + public create(parentKey: string | null) { this._workspaceContext.create(parentKey); } - private _workspaceContext: UmbDocumentWorkspaceContext = new UmbDocumentWorkspaceContext(this); - render() { return html``; } diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/media/media/workspace/media-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/backoffice/media/media/workspace/media-workspace.context.ts index 991316b869..d4f4cd51e5 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/media/media/workspace/media-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/media/media/workspace/media-workspace.context.ts @@ -16,6 +16,7 @@ export class UmbWorkspaceMediaContext extends UmbWorkspaceContext implements Umb this.#manager.state.update({name: name}) } getEntityType = this.#manager.getEntityType; + getUnique = this.#manager.getEntityKey; getEntityKey = this.#manager.getEntityKey; getStore = this.#manager.getStore; getData = this.#manager.getData; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/workspace/data-type-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/workspace/data-type-workspace.context.ts index b751c89e57..fe4c51aabd 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/workspace/data-type-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/workspace/data-type-workspace.context.ts @@ -26,6 +26,7 @@ export class UmbWorkspaceDataTypeContext extends UmbWorkspaceContext implements this.#manager.state.update({propertyEditorUIAlias: alias}); } getEntityType = this.#manager.getEntityType; + getUnique = this.#manager.getEntityKey; getEntityKey = this.#manager.getEntityKey; getStore = this.#manager.getStore; getData = this.#manager.getData; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section.element.ts index b49f765ba7..b4565ccee2 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section.element.ts @@ -97,7 +97,7 @@ export class UmbSectionElement extends UmbLitElement { component: () => createExtensionElement(workspace), setup: (component: Promise, info: IRoutingInfo) => { component.then((el) => { - el.entityKey = info.match.params.key; + el.load(info.match.params.key); }); }, }, @@ -106,7 +106,7 @@ export class UmbSectionElement extends UmbLitElement { component: () => createExtensionElement(workspace), setup: (component: Promise) => { component.then((el) => { - el.create = null; + el.create(null); }); }, }, @@ -115,7 +115,7 @@ export class UmbSectionElement extends UmbLitElement { component: () => createExtensionElement(workspace), setup: (component: Promise, info: IRoutingInfo) => { component.then((el) => { - el.create = info.match.params.parentKey; + el.create(info.match.params.parentKey); }); }, }, diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/variant-selector/variant-selector.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/variant-selector/variant-selector.element.ts index 6f3bc2826d..3d2d66f0d0 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/variant-selector/variant-selector.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/variant-selector/variant-selector.element.ts @@ -63,7 +63,9 @@ export class UmbVariantSelectorElement extends UmbLitElement { if (!this._workspaceContext) return; this.observe(this._workspaceContext.data, (data) => { - this._content = data; + if(data) { + this._content = data; + } }); } diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-context/entity-manager-controller.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-context/entity-manager-controller.ts index 12f1e41c9c..6259693f7b 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-context/entity-manager-controller.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-context/entity-manager-controller.ts @@ -26,7 +26,7 @@ export class UmbEntityWorkspaceManager { + + readonly data: Observable; + + getUnique(): string | undefined; + + getData(): T; + + load(unique: string): void; + + create(parentUnique: string | null): void; + + destroy(): void; +} diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-context/workspace-entity-context.interface.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-context/workspace-entity-context.interface.ts index 206e433ea4..f513ee751f 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-context/workspace-entity-context.interface.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-context/workspace-entity-context.interface.ts @@ -1,29 +1,20 @@ import type { Observable } from "rxjs"; +import { UmbWorkspaceContextInterface } from "./workspace-context.interface"; import { UmbEntityDetailStore } from "@umbraco-cms/store"; -export interface UmbWorkspaceEntityContextInterface { +export interface UmbWorkspaceEntityContextInterface extends UmbWorkspaceContextInterface { - - readonly data: Observable; readonly name: Observable; - //entityKey?: string; - //entityType: string; - - getEntityKey(): string | undefined; + getEntityKey(): string | undefined;// COnsider if this should go away now that we have getUnique() getEntityType(): string; getData(): T; - load(entityKey: string): void; - - create(parentKey: string | null): void; - getStore(): UmbEntityDetailStore | undefined; setPropertyValue(alias: string, value: unknown): void; save(): Promise; - destroy(): void; } diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-entity-element.interface.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-entity-element.interface.ts index 5b56617df4..f701853c32 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-entity-element.interface.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-entity-element.interface.ts @@ -1,4 +1,4 @@ export interface UmbWorkspaceEntityElement { - set entityKey(key: string); - set create(parentKey: string | null); + load(key: string): void; + create(parentKey: string | null): void; } diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/users/user-groups/workspace/user-group-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/backoffice/users/user-groups/workspace/user-group-workspace.context.ts index 9430d100b2..470163bc3c 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/users/user-groups/workspace/user-group-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/users/user-groups/workspace/user-group-workspace.context.ts @@ -18,6 +18,7 @@ export class UmbWorkspaceUserGroupContext extends UmbWorkspaceContext implements this.#manager.state.update({name: name}) } getEntityType = this.#manager.getEntityType; + getUnique = this.#manager.getEntityKey; getEntityKey = this.#manager.getEntityKey; getStore = this.#manager.getStore; getData = this.#manager.getData; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/users/user-groups/workspace/user-group-workspace.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/users/user-groups/workspace/user-group-workspace.element.ts index e0778511f9..fea60878ce 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/users/user-groups/workspace/user-group-workspace.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/users/user-groups/workspace/user-group-workspace.element.ts @@ -1,7 +1,7 @@ import { UUIInputElement, UUIInputEvent } from '@umbraco-ui/uui'; import { UUITextStyles } from '@umbraco-ui/uui-css'; import { css, html, nothing } from 'lit'; -import { customElement, property, state } from 'lit/decorators.js'; +import { customElement, state } from 'lit/decorators.js'; import { repeat } from 'lit/directives/repeat.js'; import { distinctUntilChanged } from 'rxjs'; import { UmbUserStore, UMB_USER_STORE_CONTEXT_TOKEN } from '../../../users/users/user.store'; @@ -186,22 +186,7 @@ export class UmbUserGroupWorkspaceElement extends UmbLitElement implements UmbWo private _userStore?: UmbUserStore; - private _entityKey!: string; - @property() - public get entityKey(): string { - return this._entityKey; - } - public set entityKey(value: string) { - this._entityKey = value; - if (this._entityKey) { - this._workspaceContext.load(this._entityKey); - } - } - @property() - public set create(parentKey: string | null) { - this._workspaceContext.create(parentKey); - } private _workspaceContext: UmbWorkspaceUserGroupContext = new UmbWorkspaceUserGroupContext(this); @@ -226,6 +211,14 @@ export class UmbUserGroupWorkspaceElement extends UmbLitElement implements UmbWo }); } + public load(entityKey: string) { + this._workspaceContext.load(entityKey); + } + + public create(parentKey: string | null) { + this._workspaceContext.create(parentKey); + } + private _registerWorkspaceActions() { const manifests: Array = [ { @@ -255,7 +248,8 @@ export class UmbUserGroupWorkspaceElement extends UmbLitElement implements UmbWo this.observe(this._userStore.getAll(), (users) => { // TODO: handle if there is no users. if (!this._userKeys && users.length > 0) { - this._userKeys = users.filter((user) => user.userGroups.includes(this.entityKey)).map((user) => user.key); + const entityKey = this._workspaceContext.getEntityKey(); + this._userKeys = users.filter((user) => user.userGroups.includes(entityKey)).map((user) => user.key); //this._updateProperty('users', this._userKeys); // TODO: make a method on the UmbWorkspaceUserGroupContext: //this._workspaceContext.setUsers(); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/users/users/workspace/user-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/backoffice/users/users/workspace/user-workspace.context.ts index 414e6d76b0..eb22cff826 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/users/users/workspace/user-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/users/users/workspace/user-workspace.context.ts @@ -18,6 +18,7 @@ export class UmbWorkspaceUserContext extends UmbWorkspaceContext implements UmbW this.#manager.state.update({name: name}) } getEntityType = this.#manager.getEntityType; + getUnique = this.#manager.getEntityKey; getEntityKey = this.#manager.getEntityKey; getStore = this.#manager.getStore; getData = this.#manager.getData; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/users/users/workspace/user-workspace.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/users/users/workspace/user-workspace.element.ts index b5f87f4816..36f55637ad 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/users/users/workspace/user-workspace.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/users/users/workspace/user-workspace.element.ts @@ -88,22 +88,6 @@ export class UmbUserWorkspaceElement extends UmbLitElement implements UmbWorkspa private _languages = []; //TODO Add languages - private _entityKey!: string; - @property() - public get entityKey(): string { - return this._entityKey; - } - public set entityKey(value: string) { - this._entityKey = value; - if (this._entityKey) { - this._workspaceContext.load(this._entityKey); - } - } - - @property() - public set create(parentKey: string | null) { - this._workspaceContext.create(parentKey); - } private _workspaceContext: UmbWorkspaceUserContext = new UmbWorkspaceUserContext(this); @@ -129,6 +113,14 @@ export class UmbUserWorkspaceElement extends UmbLitElement implements UmbWorkspa }); } + public load(entityKey: string) { + this._workspaceContext.load(entityKey); + } + + public create(parentKey: string | null) { + this._workspaceContext.create(parentKey); + } + private async _observeCurrentUser() { if (!this._currentUserStore) return; From 2407ade7d8790569efd90a55c9cb37ce1e69c690 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Fri, 27 Jan 2023 10:26:20 +0100 Subject: [PATCH 24/35] correct imports --- .../libs/controller/controller-host.mixin.ts | 2 +- .../libs/notification/notification.service.ts | 2 +- .../libs/notification/notification.stories.ts | 2 +- .../libs/resources/tryExecuteAndNotify.method.ts | 2 +- .../views/created/created-packages-section-view.element.ts | 3 +-- .../installed/installed-packages-section-view.element.ts | 3 +-- .../views/installed/packages-installed-item.element.ts | 3 +-- .../extensions/workspace/extension-root-workspace.element.ts | 3 +-- .../src/backoffice/shared/collection/collection.element.ts | 3 +-- .../collection/views/collection-view-media-table.element.ts | 4 ++-- .../components/extension-slot/extension-slot.element.ts | 3 +-- .../section/section-dashboards/section-dashboards.element.ts | 3 +-- .../workspace-property/workspace-property.element.ts | 3 +-- .../workspace/workspace-layout/workspace-layout.element.ts | 3 +-- .../uis/textarea/property-editor-ui-textarea.element.ts | 2 +- .../user-section/views/users/section-view-users.element.ts | 3 +-- 16 files changed, 17 insertions(+), 27 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/libs/controller/controller-host.mixin.ts b/src/Umbraco.Web.UI.Client/libs/controller/controller-host.mixin.ts index bce6f4815c..6c0342846b 100644 --- a/src/Umbraco.Web.UI.Client/libs/controller/controller-host.mixin.ts +++ b/src/Umbraco.Web.UI.Client/libs/controller/controller-host.mixin.ts @@ -1,5 +1,5 @@ -import type { HTMLElementConstructor } from '@umbraco-cms/models'; import { UmbControllerInterface } from './controller.interface'; +import type { HTMLElementConstructor } from '@umbraco-cms/models'; export declare class UmbControllerHostInterface extends HTMLElement { //#controllers:UmbController[]; diff --git a/src/Umbraco.Web.UI.Client/libs/notification/notification.service.ts b/src/Umbraco.Web.UI.Client/libs/notification/notification.service.ts index 1427ecb223..f240b1ec3b 100644 --- a/src/Umbraco.Web.UI.Client/libs/notification/notification.service.ts +++ b/src/Umbraco.Web.UI.Client/libs/notification/notification.service.ts @@ -1,6 +1,6 @@ import { BehaviorSubject } from 'rxjs'; -import { UmbContextToken } from '@umbraco-cms/context-api'; import { UmbNotificationHandler } from './notification-handler'; +import { UmbContextToken } from '@umbraco-cms/context-api'; export type UmbNotificationData = any; diff --git a/src/Umbraco.Web.UI.Client/libs/notification/notification.stories.ts b/src/Umbraco.Web.UI.Client/libs/notification/notification.stories.ts index a3f6a63c6a..c890834387 100644 --- a/src/Umbraco.Web.UI.Client/libs/notification/notification.stories.ts +++ b/src/Umbraco.Web.UI.Client/libs/notification/notification.stories.ts @@ -4,7 +4,6 @@ import { Meta, Story } from '@storybook/web-components'; import { html } from 'lit'; import { customElement } from 'lit/decorators.js'; -import { UmbLitElement } from '@umbraco-cms/element'; import type { UmbNotificationDefaultData } from './layouts/default'; import { UmbNotificationColor, @@ -12,6 +11,7 @@ import { UmbNotificationService, UMB_NOTIFICATION_SERVICE_CONTEXT_TOKEN, } from '.'; +import { UmbLitElement } from '@umbraco-cms/element'; export default { title: 'API/Notifications/Overview', diff --git a/src/Umbraco.Web.UI.Client/libs/resources/tryExecuteAndNotify.method.ts b/src/Umbraco.Web.UI.Client/libs/resources/tryExecuteAndNotify.method.ts index 2bd9c26d61..8a280ec876 100644 --- a/src/Umbraco.Web.UI.Client/libs/resources/tryExecuteAndNotify.method.ts +++ b/src/Umbraco.Web.UI.Client/libs/resources/tryExecuteAndNotify.method.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ +import { UmbResourceController } from './resource.controller'; import { UmbControllerHostInterface } from '@umbraco-cms/controller'; import type { UmbNotificationOptions } from '@umbraco-cms/notification'; -import { UmbResourceController } from './resource.controller'; export function tryExecuteAndNotify( host: UmbControllerHostInterface, diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/packages/package-section/views/created/created-packages-section-view.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/packages/package-section/views/created/created-packages-section-view.element.ts index 04925967b7..be18117d20 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/packages/package-section/views/created/created-packages-section-view.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/packages/package-section/views/created/created-packages-section-view.element.ts @@ -2,8 +2,7 @@ import { html } from 'lit'; import { customElement, state } from 'lit/decorators.js'; import { IRoute, IRoutingInfo } from 'router-slot'; import type { ManifestWorkspace } from '@umbraco-cms/models'; -import { createExtensionElement } from '@umbraco-cms/extensions-api'; -import { umbExtensionsRegistry } from '@umbraco-cms/extensions-api'; +import { createExtensionElement , umbExtensionsRegistry } from '@umbraco-cms/extensions-api'; import { UmbLitElement } from '@umbraco-cms/element'; @customElement('umb-created-packages-section-view') diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/packages/package-section/views/installed/installed-packages-section-view.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/packages/package-section/views/installed/installed-packages-section-view.element.ts index d5e7bfd180..f1b8d80ddc 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/packages/package-section/views/installed/installed-packages-section-view.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/packages/package-section/views/installed/installed-packages-section-view.element.ts @@ -2,8 +2,7 @@ import { html } from 'lit'; import { customElement, state } from 'lit/decorators.js'; import { IRoute, IRoutingInfo } from 'router-slot'; import type { ManifestWorkspace } from '@umbraco-cms/models'; -import { createExtensionElement } from '@umbraco-cms/extensions-api'; -import { umbExtensionsRegistry } from '@umbraco-cms/extensions-api'; +import { createExtensionElement , umbExtensionsRegistry } from '@umbraco-cms/extensions-api'; import { UmbLitElement } from '@umbraco-cms/element'; @customElement('umb-installed-packages-section-view') diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/packages/package-section/views/installed/packages-installed-item.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/packages/package-section/views/installed/packages-installed-item.element.ts index 0e80b2b45f..0ef2ee1f54 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/packages/package-section/views/installed/packages-installed-item.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/packages/package-section/views/installed/packages-installed-item.element.ts @@ -3,9 +3,8 @@ import { customElement, property, state } from 'lit/decorators.js'; import { firstValueFrom, map } from 'rxjs'; import { UmbModalService, UMB_MODAL_SERVICE_CONTEXT_TOKEN } from '../../../../../core/modal'; -import { createExtensionElement } from '@umbraco-cms/extensions-api'; +import { createExtensionElement , umbExtensionsRegistry } from '@umbraco-cms/extensions-api'; -import { umbExtensionsRegistry } from '@umbraco-cms/extensions-api'; import type { ManifestPackageView } from '@umbraco-cms/models'; import { UmbLitElement } from '@umbraco-cms/element'; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/settings/extensions/workspace/extension-root-workspace.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/settings/extensions/workspace/extension-root-workspace.element.ts index 4fa338ebae..26bcf43f2a 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/settings/extensions/workspace/extension-root-workspace.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/settings/extensions/workspace/extension-root-workspace.element.ts @@ -1,7 +1,6 @@ import { html } from 'lit'; import { customElement, state } from 'lit/decorators.js'; -import { isManifestElementNameType } from '@umbraco-cms/extensions-api'; -import { umbExtensionsRegistry } from '@umbraco-cms/extensions-api'; +import { isManifestElementNameType , umbExtensionsRegistry } from '@umbraco-cms/extensions-api'; import type { ManifestBase } from '@umbraco-cms/models'; import { UmbLitElement } from '@umbraco-cms/element'; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/collection/collection.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/collection/collection.element.ts index 54d24df995..d9a52420ca 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/collection/collection.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/collection/collection.element.ts @@ -5,9 +5,8 @@ import { map } from 'rxjs'; import './collection-selection-actions.element'; import './collection-toolbar.element'; import { UmbCollectionContext, UMB_COLLECTION_CONTEXT_TOKEN } from './collection.context'; -import { createExtensionElement } from '@umbraco-cms/extensions-api'; +import { createExtensionElement , umbExtensionsRegistry } from '@umbraco-cms/extensions-api'; import type { ManifestCollectionView, MediaDetails } from '@umbraco-cms/models'; -import { umbExtensionsRegistry } from '@umbraco-cms/extensions-api'; import { UmbLitElement } from '@umbraco-cms/element'; import type { UmbObserverController } from '@umbraco-cms/observable-api'; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/collection/views/collection-view-media-table.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/collection/views/collection-view-media-table.element.ts index 258a0a7465..86d53d6a1f 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/collection/views/collection-view-media-table.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/collection/views/collection-view-media-table.element.ts @@ -2,8 +2,6 @@ import { UUITextStyles } from '@umbraco-ui/uui-css'; import { css, html } from 'lit'; import { customElement, state } from 'lit/decorators.js'; import { UmbCollectionContext, UMB_COLLECTION_CONTEXT_TOKEN } from '../collection.context'; -import type { MediaDetails } from '@umbraco-cms/models'; -import { UmbLitElement } from '@umbraco-cms/element'; import { UmbTableColumn, UmbTableConfig, @@ -13,6 +11,8 @@ import { UmbTableOrderedEvent, UmbTableSelectedEvent, } from '../../components/table'; +import type { MediaDetails } from '@umbraco-cms/models'; +import { UmbLitElement } from '@umbraco-cms/element'; @customElement('umb-collection-view-media-table') export class UmbCollectionViewMediaTableElement extends UmbLitElement { diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/extension-slot/extension-slot.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/extension-slot/extension-slot.element.ts index 79af586bee..083a6a5181 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/extension-slot/extension-slot.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/extension-slot/extension-slot.element.ts @@ -3,8 +3,7 @@ import type { TemplateResult } from 'lit'; import { customElement, property, state } from 'lit/decorators.js'; import { map } from 'rxjs'; import { repeat } from 'lit/directives/repeat.js'; -import { umbExtensionsRegistry } from '@umbraco-cms/extensions-api'; -import { createExtensionElement, isManifestElementableType } from '@umbraco-cms/extensions-api'; +import { umbExtensionsRegistry , createExtensionElement, isManifestElementableType } from '@umbraco-cms/extensions-api'; import { UmbLitElement } from '@umbraco-cms/element'; export type InitializedExtension = { alias: string; weight: number; component: HTMLElement | null }; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section-dashboards/section-dashboards.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section-dashboards/section-dashboards.element.ts index 3f6ae8fc09..2bd975f3c7 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section-dashboards/section-dashboards.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section-dashboards/section-dashboards.element.ts @@ -4,13 +4,12 @@ import { customElement, state } from 'lit/decorators.js'; import { IRoutingInfo } from 'router-slot'; import { first, map } from 'rxjs'; import { UmbSectionContext, UMB_SECTION_CONTEXT_TOKEN } from '../section.context'; -import { createExtensionElement } from '@umbraco-cms/extensions-api'; +import { createExtensionElement , umbExtensionsRegistry } from '@umbraco-cms/extensions-api'; import type { ManifestDashboard, ManifestDashboardCollection, ManifestWithMeta, } from '@umbraco-cms/models'; -import { umbExtensionsRegistry } from '@umbraco-cms/extensions-api'; import { UmbLitElement } from '@umbraco-cms/element'; @customElement('umb-section-dashboards') diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace-property/workspace-property.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace-property/workspace-property.element.ts index f1059664b9..7e4562b0e7 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace-property/workspace-property.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace-property/workspace-property.element.ts @@ -3,8 +3,7 @@ import { css, html } from 'lit'; import { customElement, property, state } from 'lit/decorators.js'; import { ifDefined } from 'lit-html/directives/if-defined.js'; import { UmbWorkspacePropertyContext } from './workspace-property.context'; -import { createExtensionElement } from '@umbraco-cms/extensions-api'; -import { umbExtensionsRegistry } from '@umbraco-cms/extensions-api'; +import { createExtensionElement , umbExtensionsRegistry } from '@umbraco-cms/extensions-api'; import type { DataTypePropertyData, ManifestPropertyEditorUI, ManifestTypes } from '@umbraco-cms/models'; import '../../property-actions/shared/property-action-menu/property-action-menu.element'; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-layout/workspace-layout.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-layout/workspace-layout.element.ts index 2f2bce50dc..5ca1c0dbcb 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-layout/workspace-layout.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-layout/workspace-layout.element.ts @@ -4,8 +4,7 @@ import { customElement, property, state } from 'lit/decorators.js'; import { IRoutingInfo, RouterSlot } from 'router-slot'; import { map } from 'rxjs'; -import { createExtensionElement } from '@umbraco-cms/extensions-api'; -import { umbExtensionsRegistry } from '@umbraco-cms/extensions-api'; +import { createExtensionElement , umbExtensionsRegistry } from '@umbraco-cms/extensions-api'; import type { ManifestWorkspaceAction, ManifestWorkspaceView, diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/uis/textarea/property-editor-ui-textarea.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/uis/textarea/property-editor-ui-textarea.element.ts index df1980458f..ba7c1245cf 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/uis/textarea/property-editor-ui-textarea.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/uis/textarea/property-editor-ui-textarea.element.ts @@ -1,9 +1,9 @@ import { css, html } from 'lit'; import { UUITextStyles } from '@umbraco-ui/uui-css/lib'; import { customElement, property } from 'lit/decorators.js'; +import { UUITextareaElement } from '@umbraco-ui/uui'; import type { UmbWorkspacePropertyContext } from 'src/backoffice/shared/components/workspace-property/workspace-property.context'; import { UmbLitElement } from '@umbraco-cms/element'; -import { UUITextareaElement } from '@umbraco-ui/uui'; @customElement('umb-property-editor-ui-textarea') export class UmbPropertyEditorUITextareaElement extends UmbLitElement { diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/users/user-section/views/users/section-view-users.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/users/user-section/views/users/section-view-users.element.ts index 1aaff75e64..c383ee2e05 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/users/user-section/views/users/section-view-users.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/users/user-section/views/users/section-view-users.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 type { IRoute, IRoutingInfo } from 'router-slot'; -import { umbExtensionsRegistry } from '@umbraco-cms/extensions-api'; +import { umbExtensionsRegistry , createExtensionElement } from '@umbraco-cms/extensions-api'; import './list-view-layouts/table/workspace-view-users-table.element'; import './list-view-layouts/grid/workspace-view-users-grid.element'; @@ -10,7 +10,6 @@ import './workspace-view-users-selection.element'; import './workspace-view-users-invite.element'; import type { ManifestWorkspace, UserDetails } from '@umbraco-cms/models'; import { UmbUserStore, UMB_USER_STORE_CONTEXT_TOKEN } from 'src/backoffice/users/users/user.store'; -import { createExtensionElement } from '@umbraco-cms/extensions-api'; import { UmbLitElement } from '@umbraco-cms/element'; import { DeepState } from '@umbraco-cms/observable-api'; From 7f8e2cc432172a8486d801989d98a4dba10f0894 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Fri, 27 Jan 2023 10:28:40 +0100 Subject: [PATCH 25/35] correct import --- .../modal/layouts/picker-user/picker-layout-user.element.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/core/modal/layouts/picker-user/picker-layout-user.element.ts b/src/Umbraco.Web.UI.Client/src/core/modal/layouts/picker-user/picker-layout-user.element.ts index 126a25fd99..cdbe21b290 100644 --- a/src/Umbraco.Web.UI.Client/src/core/modal/layouts/picker-user/picker-layout-user.element.ts +++ b/src/Umbraco.Web.UI.Client/src/core/modal/layouts/picker-user/picker-layout-user.element.ts @@ -2,8 +2,8 @@ import { UUITextStyles } from '@umbraco-ui/uui-css'; import { css, html } from 'lit'; import { customElement, state } from 'lit/decorators.js'; import { UmbModalLayoutPickerBase } from '../modal-layout-picker-base'; +import { UmbUserStore, UMB_USER_STORE_CONTEXT_TOKEN } from '../../../../backoffice/users/users/user.store'; import type { UserDetails } from '@umbraco-cms/models'; -import { UmbUserStore, UMB_USER_STORE_CONTEXT_TOKEN } from 'src/backoffice/users/users/user.store'; @customElement('umb-picker-layout-user') export class UmbPickerLayoutUserElement extends UmbModalLayoutPickerBase { From 06f579930d520a8365767bdc4547d8bf1687ff58 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Fri, 27 Jan 2023 11:15:12 +0100 Subject: [PATCH 26/35] Update README.md --- src/Umbraco.Web.UI.Client/.github/README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Umbraco.Web.UI.Client/.github/README.md b/src/Umbraco.Web.UI.Client/.github/README.md index 68201b62c3..9cc0f21d62 100644 --- a/src/Umbraco.Web.UI.Client/.github/README.md +++ b/src/Umbraco.Web.UI.Client/.github/README.md @@ -39,3 +39,7 @@ See the Main branch in action here as an [Azure Static Web App](https://ashy-bay ### Storybook Storybook is also being built and deployed automatically on the Main branch, including a preview URL on each pull request. See it in action on this [Azure Static Web App](https://ambitious-stone-0033b3603.1.azurestaticapps.net/). + +## Contributing + +We accept contributions to this project. However be aware that we are mainly working on a private backlog, so not everyone will be immediately obvious. If you want to get started on contributing, please read the [contribute space](https://github.com/umbraco/Umbraco.CMS.Backoffice/contribute) where you will be able to find the guidelines on how to contribute as well as a list of good first issues. From 5a05226ef9d7a36817b9840589543a2674ae4790 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Fri, 27 Jan 2023 11:22:07 +0100 Subject: [PATCH 27/35] Update CONTRIBUTING.md --- src/Umbraco.Web.UI.Client/.github/CONTRIBUTING.md | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/.github/CONTRIBUTING.md b/src/Umbraco.Web.UI.Client/.github/CONTRIBUTING.md index 24741b1823..e38509ec18 100644 --- a/src/Umbraco.Web.UI.Client/.github/CONTRIBUTING.md +++ b/src/Umbraco.Web.UI.Client/.github/CONTRIBUTING.md @@ -13,15 +13,12 @@ Here is the LIT documentation and playground: [https://lit.dev](https://lit.dev) ### What is the process of contribution? -* Umbraco HQ owns the Management API on the backend -* Features can be worked on in the frontend when there is an API, or otherwise, if no API is required -* A contribution should be made in a fork of the backoffice repository -* Once a contribution is ready, a pull request should be made towards the backoffice repository, where HQ will assign a reviewer -* A pull request should always indicate what part of a feature it tries to solve, i.e. which labels can be removed from the issue on the projects board - -### How to work with Github Projects - -TBD +- Read the [README](README.md) to learn how to get the project up and running +- Find an issue marked as [community/up-for-grabs](https://github.com/umbraco/Umbraco.CMS.Backoffice/issues?q=is%3Aissue+is%3Aopen+label%3Acommunity%2Fup-for-grabs) - note that some are also marked [good first issue](https://github.com/umbraco/Umbraco.CMS.Backoffice/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) which indicates they are simple to get started on +- Umbraco HQ owns the Management API on the backend, so features can be worked on in the frontend only when there is an API, or otherwise, if no API is required +- A contribution should be made in a fork of the repository +- Once a contribution is ready, a pull request should be made towards this repository and HQ will assign a reviewer +- A pull request should always indicate what part of a feature it tries to solve, i.e. does it close the targeted issue (if any) or does the developer expect Umbraco HQ to take over ## Contributing in general terms From 3b7e0856eb8367faad2d2e250f0337cd54082226 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Fri, 27 Jan 2023 11:34:06 +0100 Subject: [PATCH 28/35] adding repository notes --- .../workspace/document-workspace.context.ts | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/document-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/document-workspace.context.ts index b6f9dd6516..6a6d0838f8 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/document-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/document-workspace.context.ts @@ -7,7 +7,15 @@ import { appendToFrozenArray } from '@umbraco-cms/observable-api'; export class UmbDocumentWorkspaceContext extends UmbWorkspaceContext implements UmbWorkspaceEntityContextInterface { + // Repository notes: + /* + #draft = new ObjectState(undefined); + + + */ + + // Manager will be removed when we get the Repository: #manager = new UmbEntityWorkspaceManager(this._host, 'document', UMB_DOCUMENT_DETAIL_STORE_CONTEXT_TOKEN); public readonly data = this.#manager.state.asObservable(); @@ -26,6 +34,23 @@ export class UmbDocumentWorkspaceContext extends UmbWorkspaceContext implements save = this.#manager.save; destroy = this.#manager.destroy; + /** + * Concept for Repository impl.: + + load(entityKey: string) { + this.#repository.load(entityKey).then((data) => { + this.#draft.next(data) + }) + } + + create(parentKey: string | undefined) { + this.#repository.create(parentKey).then((data) => { + this.#draft.next(data) + }) + } + + */ + // This could eventually be moved out as well? setPropertyValue(alias: string, value: unknown) { From 03e3f90bec47a3f8f6b177a688c43f33081dddeb Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Fri, 27 Jan 2023 11:35:53 +0100 Subject: [PATCH 29/35] Create LICENSE --- src/Umbraco.Web.UI.Client/LICENSE | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 src/Umbraco.Web.UI.Client/LICENSE diff --git a/src/Umbraco.Web.UI.Client/LICENSE b/src/Umbraco.Web.UI.Client/LICENSE new file mode 100644 index 0000000000..f88d3c2e3c --- /dev/null +++ b/src/Umbraco.Web.UI.Client/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 Umbraco HQ + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. From ba3f2761f4943832db9af41022fd2301e976e33f Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Fri, 27 Jan 2023 11:38:03 +0100 Subject: [PATCH 30/35] Create pull_request_template --- .../.github/pull_request_template | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 src/Umbraco.Web.UI.Client/.github/pull_request_template diff --git a/src/Umbraco.Web.UI.Client/.github/pull_request_template b/src/Umbraco.Web.UI.Client/.github/pull_request_template new file mode 100644 index 0000000000..01c2c0fe8a --- /dev/null +++ b/src/Umbraco.Web.UI.Client/.github/pull_request_template @@ -0,0 +1,30 @@ + + +## Description + + + +## Types of changes + + + +- [ ] Bug fix (non-breaking change which fixes an issue) +- [ ] New feature (non-breaking change which adds functionality) +- [ ] Breaking change (fix or feature that would cause existing functionality to change) +- [ ] Chore (minor updates related to the tooling or maintenance of the repository, does not impact compiled assets) + +## Motivation and context + + + +## How to test? + +## Screenshots (if appropriate) + +## Checklist + + + +- [ ] If my change requires a change to the documentation, I have updated the documentation in this pull request. +- [ ] I have read the **[CONTRIBUTING](<(https://github.com/umbraco/Umbraco.CMS.Backoffice/blob/main/.github/CONTRIBUTING.md)>)** document. +- [ ] I have added tests to cover my changes. From 246777d46408eef9ad313afae6f6dbf2e0478263 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Fri, 27 Jan 2023 11:39:01 +0100 Subject: [PATCH 31/35] Update issue templates --- .../.github/ISSUE_TEMPLATE/feature_request.md | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 src/Umbraco.Web.UI.Client/.github/ISSUE_TEMPLATE/feature_request.md diff --git a/src/Umbraco.Web.UI.Client/.github/ISSUE_TEMPLATE/feature_request.md b/src/Umbraco.Web.UI.Client/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000000..bbcbbe7d61 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: '' +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. From ed1c7db2a80db29556a3c1c10c002d1c73ea18c4 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Fri, 27 Jan 2023 11:41:37 +0100 Subject: [PATCH 32/35] Create config.yml --- .../.github/ISSUE_TEMPLATE/config.yml | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 src/Umbraco.Web.UI.Client/.github/ISSUE_TEMPLATE/config.yml diff --git a/src/Umbraco.Web.UI.Client/.github/ISSUE_TEMPLATE/config.yml b/src/Umbraco.Web.UI.Client/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000000..999d82b31b --- /dev/null +++ b/src/Umbraco.Web.UI.Client/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,8 @@ +blank_issues_enabled: false +contact_links: + - name: 💡 Features and ideas + url: https://github.com/umbraco/Umbraco.CMS.Backoffice/discussions/new?category=ideas + about: Start a new discussion when you have ideas or feature requests, eventually discussions can turn into plans. + - name: ❓ Support Question + url: https://our.umbraco.com + about: This issue tracker is NOT meant for support questions. If you have a question, please join us on the discussions board or Discord. From 0f093527e4ecc6c33dc7e86e3299370f2a433e7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Fri, 27 Jan 2023 11:44:01 +0100 Subject: [PATCH 33/35] todos --- .../src/backoffice/themes/theme.service.ts | 9 ++++++--- .../users/current-user/user-dashboard-themes.element.ts | 1 + 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/themes/theme.service.ts b/src/Umbraco.Web.UI.Client/src/backoffice/themes/theme.service.ts index 851fe1ee6d..d56b4d57a7 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/themes/theme.service.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/themes/theme.service.ts @@ -1,6 +1,6 @@ -import { BehaviorSubject } from 'rxjs'; import { dark, highContrast } from './themes'; import { UmbContextToken } from '@umbraco-cms/context-api'; +import { ArrayState, StringState } from '@umbraco-cms/observable-api'; export interface UmbTheme { name: string; @@ -8,7 +8,9 @@ export interface UmbTheme { } export class UmbThemeService { - #themes = new BehaviorSubject(>[ + + // TODO: Turn this into a extension type, get rid of the #themes subject and #themes observable + #themes = new ArrayState(>[ { name: 'Light', css: '', @@ -16,7 +18,7 @@ export class UmbThemeService { ]); public readonly themes = this.#themes.asObservable(); - #theme = new BehaviorSubject('Light'); + #theme = new StringState('Light'); public readonly theme = this.#theme.asObservable(); #styleElement: HTMLStyleElement; @@ -37,6 +39,7 @@ export class UmbThemeService { this.#theme.next(theme); localStorage.setItem('umb-theme', theme); + // TODO: This should come from the extension API: const themeCss = this.#themes.value.find((t) => t.name === theme)?.css; if (themeCss !== undefined) { 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 f036ce0456..ffe608c698 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 @@ -38,6 +38,7 @@ export class UmbUserDashboardTestElement extends UmbLitElement { instance.theme.subscribe((theme) => { this._theme = 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); }); From 6e5025725a9f65bdc75ec8336cd6706cfedbe328 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Fri, 27 Jan 2023 11:49:27 +0100 Subject: [PATCH 34/35] more TODOs --- .../src/backoffice/themes/themes/dark.theme.ts | 1 + .../src/backoffice/themes/themes/high-contrast.theme.ts | 1 + 2 files changed, 2 insertions(+) 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 c4a593c652..205d01ec14 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,6 +1,7 @@ import { css } from 'lit'; import { UmbTheme } from '../theme.service'; +// TODO: We should get this from UUI, and it should be served through an extension. const name = 'Dark'; const cssResult = css` :root { 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 a32b903c35..d7b1090c91 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,6 +1,7 @@ import { css } from 'lit'; import { UmbTheme } from '../theme.service'; +// TODO: We should get this from UUI, and it should be served through an extension. const name = 'High Contrast'; const cssResult = css` :root { From 32729f98adc66a6ad0feb6929dbdd7e5a0363a31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Fri, 27 Jan 2023 11:51:12 +0100 Subject: [PATCH 35/35] test for load/create methods --- .../documents/workspace/document-workspace.test.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/document-workspace.test.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/document-workspace.test.ts index e15d51b5da..3849929e42 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/document-workspace.test.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/document-workspace.test.ts @@ -18,12 +18,12 @@ describe('UmbDocumentWorkspaceElement', () => { await expect(element).to.be.accessible(defaultA11yConfig); }); - describe('properties', () => { - it('has a entityKey property', () => { - expect(element).to.have.property('entityKey'); + describe('methods', () => { + it('has a load method', () => { + expect(element).to.have.property('load').that.is.a('function'); }); - it('has a create property', () => { - expect(element).to.have.property('create'); + it('has a create method', () => { + expect(element).to.have.property('create').that.is.a('function'); }); }); });