From 240d14c6ec408e9df6afa2365d7921c0caf0d046 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Mon, 2 Jan 2023 10:20:05 +0100 Subject: [PATCH 01/13] initial work on controllers --- .../collection/collection.context.ts | 4 +- .../entity-property.element.ts | 20 +++++- .../workspace-property.context.ts | 64 +++++++++++++++++++ .../property-editor-ui-textarea.element.ts | 21 +++++- .../workspace-node.context.ts | 4 +- .../workspace-with-store.context.ts | 6 +- .../consume/context-consumer.controller.ts | 14 ++++ .../consume/context-consumer.mixin.ts | 6 +- .../consume/context-consumer.test.ts | 8 +-- .../context-api/consume/context-consumer.ts | 8 +-- .../provide/context-provide.event.ts | 2 +- .../provide/context-provider.controller.ts | 13 ++++ .../provide/context-provider.mixin.ts | 8 +-- .../provide/context-provider.test.ts | 8 +-- .../context-api/provide/context-provider.ts | 4 +- .../core/controller/controller-host.mixin.ts | 56 ++++++++++++++++ .../core/controller/controller.interface.ts | 4 ++ 17 files changed, 217 insertions(+), 33 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/backoffice/components/entity-property/workspace-property.context.ts create mode 100644 src/Umbraco.Web.UI.Client/src/core/context-api/consume/context-consumer.controller.ts create mode 100644 src/Umbraco.Web.UI.Client/src/core/context-api/provide/context-provider.controller.ts create mode 100644 src/Umbraco.Web.UI.Client/src/core/controller/controller-host.mixin.ts create mode 100644 src/Umbraco.Web.UI.Client/src/core/controller/controller.interface.ts diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/components/collection/collection.context.ts b/src/Umbraco.Web.UI.Client/src/backoffice/components/collection/collection.context.ts index 4f86960fde..2ef52bb1ca 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/components/collection/collection.context.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/components/collection/collection.context.ts @@ -42,11 +42,11 @@ export class UmbCollectionContext(""); + + constructor() { + super(); + + // TODO: make it easier to create a provider, unless a context should just extends a provider-controller? + new UmbContextProviderController(this, 'umbPropertyContext', this._propertyContext); + } + + + connectedCallback(): void { super.connectedCallback(); this._observePropertyEditorUI(); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/components/entity-property/workspace-property.context.ts b/src/Umbraco.Web.UI.Client/src/backoffice/components/entity-property/workspace-property.context.ts new file mode 100644 index 0000000000..d325c3a06a --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/backoffice/components/entity-property/workspace-property.context.ts @@ -0,0 +1,64 @@ +import { BehaviorSubject, Observable } from "rxjs"; + +export type WorkspacePropertyData = { + alias?: string | null; + label?: string | null; + value?: ValueType | null; +}; + + + +export class UmbWorkspacePropertyContext { + + + private _data: BehaviorSubject>; + public readonly data: Observable> + + + #defaultValue!: ValueType | null; + + + constructor(defaultValue: ValueType | null) { + + this.#defaultValue = defaultValue; + + // TODO: How do we connect this value with parent context? + // Ensuring the property editor value-property is updated... + + this._data = new BehaviorSubject({value: defaultValue} as WorkspacePropertyData); + this.data = this._data.asObservable(); + } + + /* + hostConnected() { + + } + + hostDisconnected() { + + } + */ + + public getData() { + return this._data.getValue(); + } + + + public update(data: Partial>) { + this._data.next({ ...this.getData(), ...data }); + } + + public resetValue() { + console.log("property context reset") + + this.update({value: this.#defaultValue}) + } + + + // TODO: how can we make sure to call this. + public destroy(): void { + this._data.unsubscribe(); + } + +} + diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/property-editors/uis/textarea/property-editor-ui-textarea.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/property-editors/uis/textarea/property-editor-ui-textarea.element.ts index 595ccadce4..3a4585044a 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/property-editors/uis/textarea/property-editor-ui-textarea.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/property-editors/uis/textarea/property-editor-ui-textarea.element.ts @@ -1,9 +1,12 @@ import { css, html, LitElement } from 'lit'; import { UUITextStyles } from '@umbraco-ui/uui-css/lib'; import { customElement, property } from 'lit/decorators.js'; +import { UmbControllerHostMixin } from 'src/core/controller/controller-host.mixin'; +import { UmbContextConsumerController } from 'src/core/context-api/consume/context-consumer.controller'; +import { UmbWorkspacePropertyContext } from '@umbraco-cms/components/entity-property/workspace-property.context'; @customElement('umb-property-editor-ui-textarea') -export class UmbPropertyEditorUITextareaElement extends LitElement { +export class UmbPropertyEditorUITextareaElement extends UmbControllerHostMixin(LitElement) { static styles = [ UUITextStyles, css` @@ -19,14 +22,26 @@ export class UmbPropertyEditorUITextareaElement extends LitElement { @property({ type: Array, attribute: false }) config = []; + private propertyContext?: UmbWorkspacePropertyContext; + + constructor() { + super(); + + new UmbContextConsumerController(this, 'umbPropertyContext', (instance) => { + this.propertyContext = instance; + }); + } + private onInput(e: InputEvent) { this.value = (e.target as HTMLInputElement).value; this.dispatchEvent(new CustomEvent('property-editor-change', { bubbles: true, composed: true })); } render() { - return html` - ${this.config?.map((property: any) => html`
${property.alias}: ${property.value}
`)} `; + return html` + + ${this.config?.map((property: any) => html`
${property.alias}: ${property.value}
`)} + `; } } diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/workspaces/shared/workspace-context/workspace-node.context.ts b/src/Umbraco.Web.UI.Client/src/backoffice/workspaces/shared/workspace-context/workspace-node.context.ts index c491de9bb2..a48863ea8c 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/workspaces/shared/workspace-context/workspace-node.context.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/workspaces/shared/workspace-context/workspace-node.context.ts @@ -28,12 +28,12 @@ export class UmbWorkspaceNodeContext(superC } if (this._attached) { - consumer.attach(); + consumer.hostConnected(); } }); } @@ -84,13 +84,13 @@ export const UmbContextConsumerMixin = (superC connectedCallback() { super.connectedCallback?.(); this._attached = true; - this._consumers.forEach((consumers) => consumers.forEach((consumer) => consumer.attach())); + this._consumers.forEach((consumers) => consumers.forEach((consumer) => consumer.hostConnected())); } disconnectedCallback() { super.disconnectedCallback?.(); this._attached = false; - this._consumers.forEach((consumers) => consumers.forEach((consumer) => consumer.detach())); + this._consumers.forEach((consumers) => consumers.forEach((consumer) => consumer.hostDisconnected())); this._resolved.clear(); } diff --git a/src/Umbraco.Web.UI.Client/src/core/context-api/consume/context-consumer.test.ts b/src/Umbraco.Web.UI.Client/src/core/context-api/consume/context-consumer.test.ts index 03da974bdf..07d32cf870 100644 --- a/src/Umbraco.Web.UI.Client/src/core/context-api/consume/context-consumer.test.ts +++ b/src/Umbraco.Web.UI.Client/src/core/context-api/consume/context-consumer.test.ts @@ -28,7 +28,7 @@ describe('UmbContextConsumer', () => { it('dispatches context request event when constructed', async () => { const listener = oneEvent(window, umbContextRequestEventType); - consumer.attach(); + consumer.hostConnected(); const event = (await listener) as unknown as UmbContextRequestEventImplementation; expect(event).to.exist; @@ -40,7 +40,7 @@ describe('UmbContextConsumer', () => { it('works with UmbContextProvider', (done) => { const provider = new UmbContextProvider(document.body, testContextAlias, new MyClass()); - provider.attach(); + provider.hostConnected(); const element = document.createElement('div'); document.body.appendChild(element); @@ -49,8 +49,8 @@ describe('UmbContextConsumer', () => { expect(_instance.prop).to.eq('value from provider'); done(); }); - localConsumer.attach(); + localConsumer.hostConnected(); - provider.detach(); + provider.hostDisconnected(); }); }); diff --git a/src/Umbraco.Web.UI.Client/src/core/context-api/consume/context-consumer.ts b/src/Umbraco.Web.UI.Client/src/core/context-api/consume/context-consumer.ts index 35205ccc58..45ace142f1 100644 --- a/src/Umbraco.Web.UI.Client/src/core/context-api/consume/context-consumer.ts +++ b/src/Umbraco.Web.UI.Client/src/core/context-api/consume/context-consumer.ts @@ -1,4 +1,4 @@ -import { isUmbContextProvideEvent, umbContextProvideEventType } from '../provide/context-provide.event'; +import { isUmbContextProvideEventType, umbContextProvideEventType } from '../provide/context-provide.event'; import { UmbContextRequestEventImplementation, UmbContextCallback } from './context-request.event'; /** @@ -23,19 +23,19 @@ export class UmbContextConsumer { this.target.dispatchEvent(event); } - public attach() { + public hostConnected() { // TODO: We need to use closets application element. We need this in order to have separate Backoffice running within or next to each other. window.addEventListener(umbContextProvideEventType, this._handleNewProvider); this.request(); } - public detach() { + public hostDisconnected() { // TODO: We need to use closets application element. We need this in order to have separate Backoffice running within or next to each other. window.removeEventListener(umbContextProvideEventType, this._handleNewProvider); } private _handleNewProvider = (event: Event) => { - if (!isUmbContextProvideEvent(event)) return; + if (!isUmbContextProvideEventType(event)) return; if (this._contextAlias === event.contextAlias) { this.request(); diff --git a/src/Umbraco.Web.UI.Client/src/core/context-api/provide/context-provide.event.ts b/src/Umbraco.Web.UI.Client/src/core/context-api/provide/context-provide.event.ts index 7478d4f7d0..c6c38a80e7 100644 --- a/src/Umbraco.Web.UI.Client/src/core/context-api/provide/context-provide.event.ts +++ b/src/Umbraco.Web.UI.Client/src/core/context-api/provide/context-provide.event.ts @@ -20,6 +20,6 @@ export class UmbContextProvideEventImplementation extends Event implements UmbCo } } -export const isUmbContextProvideEvent = (event: Event): event is UmbContextProvideEventImplementation => { +export const isUmbContextProvideEventType = (event: Event): event is UmbContextProvideEventImplementation => { return event.type === umbContextProvideEventType; }; diff --git a/src/Umbraco.Web.UI.Client/src/core/context-api/provide/context-provider.controller.ts b/src/Umbraco.Web.UI.Client/src/core/context-api/provide/context-provider.controller.ts new file mode 100644 index 0000000000..24f397c1c7 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/core/context-api/provide/context-provider.controller.ts @@ -0,0 +1,13 @@ +import { UmbContextProvider } from './context-provider'; +import type { UmbController } from 'src/core/controller/controller.interface'; +import { UmbControllerHostInterface } from 'src/core/controller/controller-host.mixin'; + + +export class UmbContextProviderController extends UmbContextProvider implements UmbController { + + constructor(host:UmbControllerHostInterface, contextAlias: string, instance: unknown) { + super(host, contextAlias, instance); + host.addController(this); + } + +} \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/core/context-api/provide/context-provider.mixin.ts b/src/Umbraco.Web.UI.Client/src/core/context-api/provide/context-provider.mixin.ts index a2c34575e4..8647039eb0 100644 --- a/src/Umbraco.Web.UI.Client/src/core/context-api/provide/context-provider.mixin.ts +++ b/src/Umbraco.Web.UI.Client/src/core/context-api/provide/context-provider.mixin.ts @@ -15,14 +15,14 @@ export const UmbContextProviderMixin = (superC // TODO: Consider if its right to re-publish the context? const existingProvider = this._providers.get(alias); if (existingProvider) { - existingProvider.detach(); + existingProvider.hostDisconnected(); } const provider = new UmbContextProvider(this, alias, instance); this._providers.set(alias, provider); // TODO: if already connected then attach the new one. if (this._attached) { - provider.attach(); + provider.hostConnected(); } } @@ -31,13 +31,13 @@ export const UmbContextProviderMixin = (superC connectedCallback() { super.connectedCallback?.(); this._attached = true; - this._providers.forEach((provider) => provider.attach()); + this._providers.forEach((provider) => provider.hostConnected()); } disconnectedCallback() { super.disconnectedCallback?.(); this._attached = false; - this._providers.forEach((provider) => provider.detach()); + this._providers.forEach((provider) => provider.hostDisconnected()); } } diff --git a/src/Umbraco.Web.UI.Client/src/core/context-api/provide/context-provider.test.ts b/src/Umbraco.Web.UI.Client/src/core/context-api/provide/context-provider.test.ts index 65c72c67fb..6909114ceb 100644 --- a/src/Umbraco.Web.UI.Client/src/core/context-api/provide/context-provider.test.ts +++ b/src/Umbraco.Web.UI.Client/src/core/context-api/provide/context-provider.test.ts @@ -12,11 +12,11 @@ describe('UmbContextProvider', () => { beforeEach(() => { provider = new UmbContextProvider(document.body, 'my-test-context', new MyClass()); - provider.attach(); + provider.hostConnected(); }); afterEach(async () => { - provider.detach(); + provider.hostDisconnected(); }); describe('Public API', () => { @@ -53,8 +53,8 @@ describe('UmbContextProvider', () => { const localConsumer = new UmbContextConsumer(element, 'my-test-context', (_instance: MyClass) => { expect(_instance.prop).to.eq('value from provider'); done(); - localConsumer.detach(); + localConsumer.hostDisconnected(); }); - localConsumer.attach(); + localConsumer.hostConnected(); }); }); diff --git a/src/Umbraco.Web.UI.Client/src/core/context-api/provide/context-provider.ts b/src/Umbraco.Web.UI.Client/src/core/context-api/provide/context-provider.ts index ba105eb2f9..f07c2c3551 100644 --- a/src/Umbraco.Web.UI.Client/src/core/context-api/provide/context-provider.ts +++ b/src/Umbraco.Web.UI.Client/src/core/context-api/provide/context-provider.ts @@ -26,7 +26,7 @@ export class UmbContextProvider { /** * @memberof UmbContextProvider */ - public attach() { + public hostConnected() { this.host.addEventListener(umbContextRequestEventType, this._handleContextRequest); this.host.dispatchEvent(new UmbContextProvideEventImplementation(this._contextAlias)); } @@ -34,7 +34,7 @@ export class UmbContextProvider { /** * @memberof UmbContextProvider */ - public detach() { + public hostDisconnected() { this.host.removeEventListener(umbContextRequestEventType, this._handleContextRequest); // TODO: fire unprovided event. } diff --git a/src/Umbraco.Web.UI.Client/src/core/controller/controller-host.mixin.ts b/src/Umbraco.Web.UI.Client/src/core/controller/controller-host.mixin.ts new file mode 100644 index 0000000000..a5ced89098 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/core/controller/controller-host.mixin.ts @@ -0,0 +1,56 @@ +import type { HTMLElementConstructor } from '../models'; +import { UmbController } from './controller.interface'; + +export declare class UmbControllerHostInterface extends HTMLElement { + //#controllers:UmbController[]; + //#attached:boolean; + addController(controller:UmbController): void; +} + +/** + * This mixin enables the component to host controllers. + * This is done by calling the `consumeContext` method. + * + * @param {Object} superClass - superclass to be extended. + * @mixin + */ +export const UmbControllerHostMixin = (superClass: T) => { + class UmbContextConsumerClass extends superClass { + + #controllers: UmbController[] = []; + + #attached = false; + + /** + * Append a controller to this element. + * @param {UmbController} ctrl + */ + addController(ctrl: UmbController): void { + this.#controllers.push(ctrl); + if(this.#attached) { + ctrl.hostConnected(); + } + } + + connectedCallback() { + super.connectedCallback?.(); + this.#attached = true; + this.#controllers.forEach((ctrl: UmbController) => ctrl.hostConnected()); + } + + disconnectedCallback() { + super.disconnectedCallback?.(); + this.#attached = false; + this.#controllers.forEach((ctrl: UmbController) => ctrl.hostDisconnected()); + } + } + + return UmbContextConsumerClass as unknown as HTMLElementConstructor & T; +}; + +declare global { + interface HTMLElement { + connectedCallback(): void; + disconnectedCallback(): void; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/core/controller/controller.interface.ts b/src/Umbraco.Web.UI.Client/src/core/controller/controller.interface.ts new file mode 100644 index 0000000000..655eb0ff59 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/core/controller/controller.interface.ts @@ -0,0 +1,4 @@ +export interface UmbController { + hostConnected(): void; + hostDisconnected(): void; +} \ No newline at end of file From e8e4e7207eea7c48f55d78216f5b7541698f7911 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Mon, 2 Jan 2023 10:20:53 +0100 Subject: [PATCH 02/13] remove consumer mixing from property --- .../components/entity-property/entity-property.element.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/components/entity-property/entity-property.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/components/entity-property/entity-property.element.ts index 421bd1b795..475e3d596b 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/components/entity-property/entity-property.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/components/entity-property/entity-property.element.ts @@ -5,7 +5,6 @@ import { UmbWorkspacePropertyContext } from './workspace-property.context'; import { UmbObserverMixin } from '@umbraco-cms/observable-api'; import { createExtensionElement } from '@umbraco-cms/extensions-api'; import { umbExtensionsRegistry } from '@umbraco-cms/extensions-registry'; -import { UmbContextConsumerMixin } from '@umbraco-cms/context-api'; import type { ManifestPropertyEditorUI, ManifestTypes } from '@umbraco-cms/models'; import '../../property-actions/shared/property-action-menu/property-action-menu.element'; @@ -21,7 +20,7 @@ import { UmbControllerHostMixin } from 'src/core/controller/controller-host.mixi // TODO: get rid of the other mixins: @customElement('umb-entity-property') -export class UmbEntityPropertyElement extends UmbControllerHostMixin(UmbContextConsumerMixin(UmbObserverMixin(LitElement))) { +export class UmbEntityPropertyElement extends UmbControllerHostMixin(UmbObserverMixin(LitElement)) { static styles = [ UUITextStyles, css` From ac3d9b52a35ce5834d071c8ee790bddc16853650 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Mon, 2 Jan 2023 11:09:47 +0100 Subject: [PATCH 03/13] observer as a class --- .../observable-api/observer.controller.ts | 18 +++++++++++++++ .../src/core/observable-api/observer.mixin.ts | 4 ++++ .../src/core/observable-api/observer.ts | 23 +++++++++++++++++++ 3 files changed, 45 insertions(+) create mode 100644 src/Umbraco.Web.UI.Client/src/core/observable-api/observer.controller.ts create mode 100644 src/Umbraco.Web.UI.Client/src/core/observable-api/observer.ts diff --git a/src/Umbraco.Web.UI.Client/src/core/observable-api/observer.controller.ts b/src/Umbraco.Web.UI.Client/src/core/observable-api/observer.controller.ts new file mode 100644 index 0000000000..7b9094cd9a --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/core/observable-api/observer.controller.ts @@ -0,0 +1,18 @@ +import { Observable } from 'rxjs'; +import { UmbObserver } from './observer'; +import type { UmbController } from 'src/core/controller/controller.interface'; +import { UmbControllerHostInterface } from 'src/core/controller/controller-host.mixin'; + + +export class UmbObserverController extends UmbObserver implements UmbController { + + constructor(host:UmbControllerHostInterface, source: Observable, callback: (_value: Y) => void) { + super(source, callback); + host.addController(this); + } + + hostConnected() { + return; + } + +} \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/core/observable-api/observer.mixin.ts b/src/Umbraco.Web.UI.Client/src/core/observable-api/observer.mixin.ts index 490c40c335..3e2be5bfed 100644 --- a/src/Umbraco.Web.UI.Client/src/core/observable-api/observer.mixin.ts +++ b/src/Umbraco.Web.UI.Client/src/core/observable-api/observer.mixin.ts @@ -10,10 +10,14 @@ export const UmbObserverMixin = (superClass: T _subscriptions: Map, Subscription> = new Map(); observe(source: Observable, callback: (_value: Y) => void): ()=>void { + + // TODO: can be transferred to something using alias? + /* if (this._subscriptions.has(source)) { const subscription = this._subscriptions.get(source); subscription?.unsubscribe(); } + */ const subscription = source.subscribe((value) => callback(value)); this._subscriptions.set(source, subscription); diff --git a/src/Umbraco.Web.UI.Client/src/core/observable-api/observer.ts b/src/Umbraco.Web.UI.Client/src/core/observable-api/observer.ts new file mode 100644 index 0000000000..48be81682b --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/core/observable-api/observer.ts @@ -0,0 +1,23 @@ +import { Observable, Subscription } from 'rxjs'; + +export class UmbObserver { + + #subscription!: Subscription; + + constructor(source: Observable, callback: (_value: Y) => void) { + + // TODO: can be transferred to something using alias? + /* + if (this._subscriptions.has(source)) { + const subscription = this._subscriptions.get(source); + subscription?.unsubscribe(); + } + */ + + this.#subscription = source.subscribe((value) => callback(value)); + } + + hostDisconnected() { + this.#subscription.unsubscribe(); + } +}; \ No newline at end of file From de341069dfa27a7c8718683c9db76dd69772e937 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Mon, 2 Jan 2023 11:19:09 +0100 Subject: [PATCH 04/13] note --- src/Umbraco.Web.UI.Client/src/core/observable-api/observer.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Umbraco.Web.UI.Client/src/core/observable-api/observer.ts b/src/Umbraco.Web.UI.Client/src/core/observable-api/observer.ts index 48be81682b..4f701a0e49 100644 --- a/src/Umbraco.Web.UI.Client/src/core/observable-api/observer.ts +++ b/src/Umbraco.Web.UI.Client/src/core/observable-api/observer.ts @@ -17,6 +17,8 @@ export class UmbObserver { this.#subscription = source.subscribe((value) => callback(value)); } + // Notice controller class implements empty hostConnected(). + hostDisconnected() { this.#subscription.unsubscribe(); } From 8d25b19bfa29dfc7db22698f2d52a014d6da4871 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Mon, 2 Jan 2023 11:21:54 +0100 Subject: [PATCH 05/13] move to constructor --- .../entity-property/entity-property.element.ts | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/components/entity-property/entity-property.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/components/entity-property/entity-property.element.ts index 475e3d596b..962476dea7 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/components/entity-property/entity-property.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/components/entity-property/entity-property.element.ts @@ -127,24 +127,16 @@ export class UmbEntityPropertyElement extends UmbControllerHostMixin(UmbObserver // TODO: make it easier to create a provider, unless a context should just extends a provider-controller? new UmbContextProviderController(this, 'umbPropertyContext', this._propertyContext); - } - - - connectedCallback(): void { - super.connectedCallback(); - this._observePropertyEditorUI(); - this.addEventListener('property-editor-change', this._onPropertyEditorChange as any as EventListener); - } - - private _observePropertyEditorUI() { this.observe(umbExtensionsRegistry.getByAlias(this.propertyEditorUIAlias), (manifest) => { if (manifest?.type === 'propertyEditorUI') { this._gotData(manifest); } }); + this.addEventListener('property-editor-change', this._onPropertyEditorChange as any as EventListener); } + private _gotData(propertyEditorUIManifest?: ManifestPropertyEditorUI) { if (!propertyEditorUIManifest) { // TODO: if dataTypeKey didn't exist in store, we should do some nice UI. From 399f5702383487d0033670b3e2753bd04656d0ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Mon, 2 Jan 2023 11:24:54 +0100 Subject: [PATCH 06/13] next step --- .../entity-property/entity-property.element.ts | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/components/entity-property/entity-property.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/components/entity-property/entity-property.element.ts index 962476dea7..fd9ace9eac 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/components/entity-property/entity-property.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/components/entity-property/entity-property.element.ts @@ -11,6 +11,7 @@ import '../../property-actions/shared/property-action-menu/property-action-menu. import '../../workspaces/shared/workspace-property-layout/workspace-property-layout.element'; import { UmbContextProviderController } from 'src/core/context-api/provide/context-provider.controller'; import { UmbControllerHostMixin } from 'src/core/controller/controller-host.mixin'; +import { UmbObserverController } from 'src/core/observable-api/observer.controller'; /** * @element umb-entity-property @@ -122,21 +123,30 @@ export class UmbEntityPropertyElement extends UmbControllerHostMixin(UmbObserver private _propertyContext = new UmbWorkspacePropertyContext(""); + private propertyEditorUIObserver?: UmbObserverController; + + constructor() { super(); // TODO: make it easier to create a provider, unless a context should just extends a provider-controller? new UmbContextProviderController(this, 'umbPropertyContext', this._propertyContext); + + this._observePropertyEditorUI(); + this.addEventListener('property-editor-change', this._onPropertyEditorChange as any as EventListener); + } - this.observe(umbExtensionsRegistry.getByAlias(this.propertyEditorUIAlias), (manifest) => { + private _observePropertyEditorUI() { + if(this.propertyEditorUIObserver) { + //this.propertyEditorUIObserver.destroy(); + } + this.propertyEditorUIObserver = new UmbObserverController(this, umbExtensionsRegistry.getByAlias(this.propertyEditorUIAlias), (manifest) => { if (manifest?.type === 'propertyEditorUI') { this._gotData(manifest); } }); - this.addEventListener('property-editor-change', this._onPropertyEditorChange as any as EventListener); } - private _gotData(propertyEditorUIManifest?: ManifestPropertyEditorUI) { if (!propertyEditorUIManifest) { // TODO: if dataTypeKey didn't exist in store, we should do some nice UI. From b2ac040e1a4033e2d853a0a88758b77b759032cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Mon, 2 Jan 2023 11:33:39 +0100 Subject: [PATCH 07/13] observe impl --- .../components/entity-property/entity-property.element.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/components/entity-property/entity-property.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/components/entity-property/entity-property.element.ts index fd9ace9eac..614268c951 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/components/entity-property/entity-property.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/components/entity-property/entity-property.element.ts @@ -2,7 +2,6 @@ import { UUITextStyles } from '@umbraco-ui/uui-css/lib'; import { css, html, LitElement, PropertyValueMap } from 'lit'; import { customElement, property, state } from 'lit/decorators.js'; import { UmbWorkspacePropertyContext } from './workspace-property.context'; -import { UmbObserverMixin } from '@umbraco-cms/observable-api'; import { createExtensionElement } from '@umbraco-cms/extensions-api'; import { umbExtensionsRegistry } from '@umbraco-cms/extensions-registry'; import type { ManifestPropertyEditorUI, ManifestTypes } from '@umbraco-cms/models'; @@ -21,7 +20,7 @@ import { UmbObserverController } from 'src/core/observable-api/observer.controll // TODO: get rid of the other mixins: @customElement('umb-entity-property') -export class UmbEntityPropertyElement extends UmbControllerHostMixin(UmbObserverMixin(LitElement)) { +export class UmbEntityPropertyElement extends UmbControllerHostMixin(LitElement) { static styles = [ UUITextStyles, css` From ade6be34fa87582683e103602ffc8df4d6458c13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Mon, 2 Jan 2023 11:34:57 +0100 Subject: [PATCH 08/13] todo --- .../components/entity-property/entity-property.element.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/components/entity-property/entity-property.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/components/entity-property/entity-property.element.ts index 614268c951..6652cd06f8 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/components/entity-property/entity-property.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/components/entity-property/entity-property.element.ts @@ -119,7 +119,7 @@ export class UmbEntityPropertyElement extends UmbControllerHostMixin(LitElement) private _element?: { value?: any; config?: any } & HTMLElement; // TODO: invent interface for propertyEditorUI. - + // TODO: How to get proper default value? private _propertyContext = new UmbWorkspacePropertyContext(""); private propertyEditorUIObserver?: UmbObserverController; From d9d70fc35c275f69fb5b059f998e20dffbf1b88c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Mon, 2 Jan 2023 11:38:01 +0100 Subject: [PATCH 09/13] destroy --- .../components/entity-property/entity-property.element.ts | 2 +- .../src/core/observable-api/observer.ts | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/components/entity-property/entity-property.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/components/entity-property/entity-property.element.ts index 6652cd06f8..f572dc6efd 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/components/entity-property/entity-property.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/components/entity-property/entity-property.element.ts @@ -137,7 +137,7 @@ export class UmbEntityPropertyElement extends UmbControllerHostMixin(LitElement) private _observePropertyEditorUI() { if(this.propertyEditorUIObserver) { - //this.propertyEditorUIObserver.destroy(); + this.propertyEditorUIObserver.destroy(); } this.propertyEditorUIObserver = new UmbObserverController(this, umbExtensionsRegistry.getByAlias(this.propertyEditorUIAlias), (manifest) => { if (manifest?.type === 'propertyEditorUI') { diff --git a/src/Umbraco.Web.UI.Client/src/core/observable-api/observer.ts b/src/Umbraco.Web.UI.Client/src/core/observable-api/observer.ts index 4f701a0e49..b577a2b6d3 100644 --- a/src/Umbraco.Web.UI.Client/src/core/observable-api/observer.ts +++ b/src/Umbraco.Web.UI.Client/src/core/observable-api/observer.ts @@ -22,4 +22,9 @@ export class UmbObserver { hostDisconnected() { this.#subscription.unsubscribe(); } + + destroy(): void { + this.#subscription.unsubscribe(); + } + }; \ No newline at end of file From 59a44f8eee47ff5b99b4a8c77143d703f3b5db44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Mon, 2 Jan 2023 11:43:41 +0100 Subject: [PATCH 10/13] turn into a one liner --- .../components/entity-property/entity-property.element.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/components/entity-property/entity-property.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/components/entity-property/entity-property.element.ts index f572dc6efd..95eab9ef83 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/components/entity-property/entity-property.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/components/entity-property/entity-property.element.ts @@ -136,9 +136,7 @@ export class UmbEntityPropertyElement extends UmbControllerHostMixin(LitElement) } private _observePropertyEditorUI() { - if(this.propertyEditorUIObserver) { - this.propertyEditorUIObserver.destroy(); - } + this.propertyEditorUIObserver?.destroy(); this.propertyEditorUIObserver = new UmbObserverController(this, umbExtensionsRegistry.getByAlias(this.propertyEditorUIAlias), (manifest) => { if (manifest?.type === 'propertyEditorUI') { this._gotData(manifest); From 8cb8af976ca8b87bfd893f5c6bb16dde6205c960 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Mon, 2 Jan 2023 11:52:22 +0100 Subject: [PATCH 11/13] move resource controller into resource folder --- ...dashboard-performance-profiling.element.ts | 2 +- .../core/{controllers => resources}/index.ts | 0 .../resource.controller.ts | 22 ++++++++----------- src/Umbraco.Web.UI.Client/tsconfig.json | 2 +- .../web-test-runner.config.mjs | 2 +- 5 files changed, 12 insertions(+), 16 deletions(-) rename src/Umbraco.Web.UI.Client/src/core/{controllers => resources}/index.ts (100%) rename src/Umbraco.Web.UI.Client/src/core/{controllers => resources}/resource.controller.ts (84%) diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/dashboards/performance-profiling/dashboard-performance-profiling.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/dashboards/performance-profiling/dashboard-performance-profiling.element.ts index fa4835f52b..6cc0ce425d 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/dashboards/performance-profiling/dashboard-performance-profiling.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/dashboards/performance-profiling/dashboard-performance-profiling.element.ts @@ -2,7 +2,7 @@ import { UUITextStyles } from '@umbraco-ui/uui-css/lib'; import { css, html, LitElement } from 'lit'; import { customElement, state } from 'lit/decorators.js'; -import { UmbResourceController } from '@umbraco-cms/controllers'; +import { UmbResourceController } from 'src/core/resources'; import { ProfilingResource } from '@umbraco-cms/backend-api'; @customElement('umb-dashboard-performance-profiling') diff --git a/src/Umbraco.Web.UI.Client/src/core/controllers/index.ts b/src/Umbraco.Web.UI.Client/src/core/resources/index.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/core/controllers/index.ts rename to src/Umbraco.Web.UI.Client/src/core/resources/index.ts diff --git a/src/Umbraco.Web.UI.Client/src/core/controllers/resource.controller.ts b/src/Umbraco.Web.UI.Client/src/core/resources/resource.controller.ts similarity index 84% rename from src/Umbraco.Web.UI.Client/src/core/controllers/resource.controller.ts rename to src/Umbraco.Web.UI.Client/src/core/resources/resource.controller.ts index bff3e44b7b..fa819e7249 100644 --- a/src/Umbraco.Web.UI.Client/src/core/controllers/resource.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/core/resources/resource.controller.ts @@ -1,24 +1,22 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { ReactiveController, ReactiveControllerHost } from 'lit'; +import { UmbController } from '../controller/controller.interface'; +import { UmbControllerHostInterface } from '../controller/controller-host.mixin'; +import { UmbContextConsumerController } from '../context-api/consume/context-consumer.controller'; import { ApiError, CancelablePromise, ProblemDetails } from '@umbraco-cms/backend-api'; import { UmbNotificationOptions, UmbNotificationDefaultData, UmbNotificationService } from '@umbraco-cms/services'; -import { UmbContextConsumer } from '@umbraco-cms/context-api'; -export class UmbResourceController implements ReactiveController { - host: ReactiveControllerHost; +export class UmbResourceController implements UmbController { + #promises: Promise[] = []; - #notificationConsumer: UmbContextConsumer; - #notificationService?: UmbNotificationService; - constructor(host: ReactiveControllerHost) { - (this.host = host).addController(this); - this.#notificationConsumer = new UmbContextConsumer( - host as unknown as EventTarget, - 'umbNotificationService', + constructor(host: UmbControllerHostInterface) { + host.addController(this); + + new UmbContextConsumerController(host, 'umbNotificationService', (_instance: UmbNotificationService) => { this.#notificationService = _instance; } @@ -27,12 +25,10 @@ export class UmbResourceController implements ReactiveController { hostConnected() { this.#promises.length = 0; - this.#notificationConsumer.attach(); } hostDisconnected() { this.cancelAllResources(); - this.#notificationConsumer.detach(); } addResource(promise: Promise): void { diff --git a/src/Umbraco.Web.UI.Client/tsconfig.json b/src/Umbraco.Web.UI.Client/tsconfig.json index 6763dcdfee..6f278ee1d3 100644 --- a/src/Umbraco.Web.UI.Client/tsconfig.json +++ b/src/Umbraco.Web.UI.Client/tsconfig.json @@ -28,7 +28,7 @@ "@umbraco-cms/observable-api": ["src/core/observable-api"], "@umbraco-cms/utils": ["src/core/utils"], "@umbraco-cms/test-utils": ["src/core/test-utils"], - "@umbraco-cms/controllers": ["src/core/controllers"], + "@umbraco-cms/resources": ["src/core/resources"], "@umbraco-cms/services": ["src/core/services"], "@umbraco-cms/components/*": ["src/backoffice/components/*"], "@umbraco-cms/stores/*": ["src/core/stores/*"], diff --git a/src/Umbraco.Web.UI.Client/web-test-runner.config.mjs b/src/Umbraco.Web.UI.Client/web-test-runner.config.mjs index 2167c55a73..bf55d97a2d 100644 --- a/src/Umbraco.Web.UI.Client/web-test-runner.config.mjs +++ b/src/Umbraco.Web.UI.Client/web-test-runner.config.mjs @@ -19,7 +19,7 @@ export default { '@umbraco-cms/resource-api': './src/core/resource-api', '@umbraco-cms/utils': './src/core/utils/index.ts', '@umbraco-cms/test-utils': './src/core/test-utils/index.ts', - '@umbraco-cms/controllers': './src/core/controllers', + '@umbraco-cms/resources': './src/core/resources', '@umbraco-cms/services': './src/core/services', '@umbraco-cms/extensions-registry': './src/core/extensions-registry/index.ts', }, From 1b294bd01d8b7b1d86584891a835dd7c65f72dd2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Mon, 2 Jan 2023 11:52:51 +0100 Subject: [PATCH 12/13] note --- .../src/core/resources/resource.controller.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/core/resources/resource.controller.ts b/src/Umbraco.Web.UI.Client/src/core/resources/resource.controller.ts index fa819e7249..f2de6515fd 100644 --- a/src/Umbraco.Web.UI.Client/src/core/resources/resource.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/core/resources/resource.controller.ts @@ -24,7 +24,8 @@ export class UmbResourceController implements UmbController { } hostConnected() { - this.#promises.length = 0; + // TODO: Make sure we do the right thing here, as connected can be called multiple times without disconnected invoked. + //this.#promises.length = 0; } hostDisconnected() { From 102329c4d48452e38803e4100a12b4fda9705bed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Mon, 2 Jan 2023 13:00:13 +0100 Subject: [PATCH 13/13] correct test --- .../src/core/context-api/provide/context-provider.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/core/context-api/provide/context-provider.test.ts b/src/Umbraco.Web.UI.Client/src/core/context-api/provide/context-provider.test.ts index 6909114ceb..0d864cd608 100644 --- a/src/Umbraco.Web.UI.Client/src/core/context-api/provide/context-provider.test.ts +++ b/src/Umbraco.Web.UI.Client/src/core/context-api/provide/context-provider.test.ts @@ -28,11 +28,11 @@ describe('UmbContextProvider', () => { describe('methods', () => { it('has an attach method', () => { - expect(provider).to.have.property('attach').that.is.a('function'); + expect(provider).to.have.property('hostConnected').that.is.a('function'); }); it('has a detach method', () => { - expect(provider).to.have.property('detach').that.is.a('function'); + expect(provider).to.have.property('hostDisconnected').that.is.a('function'); }); }); });