From 0453bd293bf9c886d6c8d412fc8139f504472842 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Fri, 6 Jan 2023 20:14:54 +0100 Subject: [PATCH] refactoring of property -> context communication --- .../content-property.element.ts | 2 +- .../components/property/property.element.ts | 17 +++--- .../property/workspace-property.context.ts | 6 +++ .../workspace-content.context.ts | 53 ++++++++++++++++--- .../workspace-content.element.ts | 48 ----------------- .../clear/property-action-clear.element.ts | 2 +- .../property-action-menu.context.ts | 12 +++-- .../property-action-menu.element.ts | 49 +++++++++-------- ...operty-editor-ui-content-picker.element.ts | 2 +- .../property-editor-ui-number.element.ts | 2 +- .../property-editor-ui-text-box.element.ts | 2 +- .../property-editor-ui-textarea.element.ts | 2 +- .../src/core/models/index.ts | 10 +++- 13 files changed, 111 insertions(+), 96 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/content-property/content-property.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/content-property/content-property.element.ts index 9f3bfca1ae..90f55497ca 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/content-property/content-property.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/content-property/content-property.element.ts @@ -31,7 +31,7 @@ export class UmbContentPropertyElement extends UmbLitElement { } @property() - value?: object; + value?: object | string; @state() private _propertyEditorUIAlias?: string; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/property/property.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/property/property.element.ts index 53de152dc0..45ba7d01ec 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/property/property.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/property/property.element.ts @@ -84,6 +84,9 @@ export class UmbPropertyElement extends UmbLitElement { * @default '' */ @property({ type: String }) + public get alias(): string { + return this._propertyContext.getAlias() || ''; + } public set alias(alias: string) { this._propertyContext.setAlias(alias); } @@ -108,11 +111,14 @@ export class UmbPropertyElement extends UmbLitElement { /** * Property Editor UI Alias. Render the Property Editor UI registered for this alias. * @public - * @type {object} + * @type {object | string} * @attr * @default undefined */ - @property({ type: Object, attribute: false }) + @property({ attribute: false }) + public get value() { + return this._propertyContext.getValue() as any; + } public set value(value: object | string) { this._propertyContext.setValue(value); } @@ -144,7 +150,6 @@ export class UmbPropertyElement extends UmbLitElement { this.observe(this._propertyContext.label, (label) => { - console.log("_propertyContext replied with label", label) this._label = label; }); this.observe(this._propertyContext.label, (description) => { @@ -152,7 +157,7 @@ export class UmbPropertyElement extends UmbLitElement { }); // TODO: move event to context. maybe rename to property-editor-value-change. - this.addEventListener('property-editor-change', this._onPropertyEditorChange as any as EventListener); + this.addEventListener('property-editor-value-change', this._onPropertyEditorChange as any as EventListener); } @@ -200,9 +205,9 @@ export class UmbPropertyElement extends UmbLitElement { const target = e.composedPath()[0] as any; this.value = target.value; - // TODO: update context. //TODO: Property-Context: Figure out the requirements for this. Cause currently the alias-prop(getter) is required, but its not obvious. - + + // TODO: Confusing with the little detail of the event name that changed here.. this.dispatchEvent(new CustomEvent('property-value-change', { bubbles: true, composed: true })); e.stopPropagation(); }; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/property/workspace-property.context.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/property/workspace-property.context.ts index 7d9fdac5dd..836c8941dd 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/property/workspace-property.context.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/property/workspace-property.context.ts @@ -81,6 +81,9 @@ export class UmbWorkspacePropertyContext { public setAlias(alias: WorkspacePropertyData['alias']) { this.update({alias: alias}); } + public getAlias() { + return this._data.getValue().alias; + } public setLabel(label: WorkspacePropertyData['label']) { this.update({label: label}); } @@ -90,6 +93,9 @@ export class UmbWorkspacePropertyContext { public setValue(value: WorkspacePropertyData['value']) { this.update({value: value}); } + public getValue() { + return this._data.getValue().value; + } public setConfig(config: WorkspacePropertyData['config']) { this.update({config: config}); } 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 index 9d491762c9..8586e736c5 100644 --- 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 @@ -3,16 +3,16 @@ import { UmbNotificationService } from '../../../../../core/notification'; import { UmbNotificationDefaultData } from '../../../../../core/notification/layouts/default'; import { UmbWorkspaceContext } from '../workspace-context/workspace.context'; import { UmbNodeStoreBase } from '@umbraco-cms/stores/store'; -import { ContentTreeItem } from '@umbraco-cms/backend-api'; import { UmbControllerHostInterface } from 'src/core/controller/controller-host.mixin'; import { UmbContextConsumerController } from 'src/core/context-api/consume/context-consumer.controller'; import { UmbObserverController } from '@umbraco-cms/observable-api'; import { UmbContextProviderController } from 'src/core/context-api/provide/context-provider.controller'; +import type { ContentDetails } from '@umbraco-cms/models'; // 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 class UmbWorkspaceContentContext< - ContentTypeType extends ContentTreeItem = ContentTreeItem, + ContentTypeType extends ContentDetails = ContentDetails, StoreType extends UmbNodeStoreBase = UmbNodeStoreBase > extends UmbWorkspaceContext { @@ -34,6 +34,10 @@ export class UmbWorkspaceContentContext< ) { super(host, defaultData); + this.entityType = entityType; + + host.addEventListener('property-value-change', this._onPropertyValueChange); + new UmbContextConsumerController( host, 'umbNotificationService', @@ -42,15 +46,13 @@ export class UmbWorkspaceContentContext< } ); - this.entityType = entityType; - 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._readyToLoad(); + this._observeStore(); // TODO: first provide when we have umbNotificationService as well. new UmbContextProviderController(this._host, 'umbWorkspaceContext', this); @@ -60,7 +62,7 @@ export class UmbWorkspaceContentContext< load(entityKey: string) { this.#isNew = false; this.entityKey = entityKey; - this._readyToLoad(); + this._observeStore(); } create(parentKey: string | null) { @@ -69,7 +71,7 @@ export class UmbWorkspaceContentContext< console.log("I'm new, and I will be created under ", parentKey) } - protected _readyToLoad(): void { + protected _observeStore(): void { if(!this._store || !this.entityKey) { return; } @@ -88,6 +90,43 @@ export class UmbWorkspaceContentContext< return this._store; } + + + + + //TODO: Property-Context: I would like ot investigate how this would work as methods. That do require that a property-context gets the workspace context. But this connection would be more safe. + private _onPropertyValueChange = (e: Event) => { + const target = e.composedPath()[0] as any; + + console.log("_onPropertyValueChange context", target.alias, target); + + const property = this.getData().data.find((x) => x.alias === target.alias); + if (property) { + this._setPropertyValue(property.alias, target.value); + } else { + console.error('property was not found', target.alias); + } + + // We need to stop the event, so it does not bubble up to parent workspace contexts. + e.stopPropagation(); + }; + + private _setPropertyValue(alias: string, value: unknown) { + + console.log("about to change prop", this.getData()); + const newDataSet = this.getData().data.map((entry) => { + if (entry.alias === alias) { + return {alias: alias, value: value}; + } + return entry; + }); + + + const part = {data: newDataSet}; + console.log("result", part) + this.update(part as Partial); + } + public save(): Promise { if(!this._store) { // TODO: more beautiful error: diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-content/workspace-content.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-content/workspace-content.element.ts index 3c17210782..4e2ae00635 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-content/workspace-content.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-content/workspace-content.element.ts @@ -49,54 +49,6 @@ export class UmbWorkspaceContentElement extends UmbLitElement { @property() alias!: string; - // TODO: use a NodeDetails type here: - @state() - _content?: ContentTypeTypes; - - private _workspaceContext?: UmbWorkspaceContentContext>; - - constructor() { - super(); - - this.consumeContext('umbWorkspaceContext', (instance) => { - this._workspaceContext = instance; - this._observeWorkspace(); - }); - - this.addEventListener('property-value-change', this._onPropertyValueChange); - } - - private async _observeWorkspace() { - if (!this._workspaceContext) return; - - this.observe(this._workspaceContext.data.pipe(distinctUntilChanged()), (data) => { - this._content = data; - }); - } - - private _onPropertyValueChange = (e: Event) => { - const target = e.composedPath()[0] as any; - - console.log("_onPropertyValueChange", target.alias, target); - - // TODO: Set value. - const property = this._content?.properties.find((x) => x.alias === target.alias); - if (property) { - this._setPropertyValue(property.alias, target.value); - } else { - console.error('property was not found', target.alias); - } - }; - - // TODO: How do we ensure this is a change of this document and not nested documents? Should the event be stopped at this spot at avoid such. - private _setPropertyValue(alias: string, value: unknown) { - this._content?.data.forEach((data) => { - if (data.alias === alias) { - data.value = value; - } - }); - } - render() { return html` diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-actions/clear/property-action-clear.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-actions/clear/property-action-clear.element.ts index 5371832894..11a2fe8fbe 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-actions/clear/property-action-clear.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-actions/clear/property-action-clear.element.ts @@ -29,7 +29,7 @@ export class UmbPropertyActionClearElement extends UmbLitElement implements UmbP private _clearValue() { // TODO: how do we want to update the value? Testing an event based approach. We need to test an api based approach too. this.value = ''; - this.dispatchEvent(new CustomEvent('property-editor-change', { bubbles: true, composed: true })); + this.dispatchEvent(new CustomEvent('property-editor-value-change', { bubbles: true, composed: true })); } render() { diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-actions/shared/property-action-menu/property-action-menu.context.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-actions/shared/property-action-menu/property-action-menu.context.ts index 1cfbbac557..4cd8caa24a 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-actions/shared/property-action-menu/property-action-menu.context.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-actions/shared/property-action-menu/property-action-menu.context.ts @@ -1,20 +1,22 @@ -import { Observable, ReplaySubject } from 'rxjs'; +import { BehaviorSubject } from 'rxjs'; import { UmbContextProviderController } from 'src/core/context-api/provide/context-provider.controller'; -import { UmbControllerHostInterface } from 'src/core/controller/controller-host.mixin'; +import type { UmbControllerHostInterface } from 'src/core/controller/controller-host.mixin'; export class UmbPropertyActionMenuContext { - private _isOpen: ReplaySubject = new ReplaySubject(1); - public readonly isOpen: Observable = this._isOpen.asObservable(); + private _isOpen = new BehaviorSubject(false); + public readonly isOpen = this._isOpen.asObservable(); constructor(host: UmbControllerHostInterface) { new UmbContextProviderController(host, 'umbPropertyActionMenu', this); } + toggle() { + this._isOpen.next(!this._isOpen.getValue()); + } open() { this._isOpen.next(true); } - close() { this._isOpen.next(false); } diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-actions/shared/property-action-menu/property-action-menu.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-actions/shared/property-action-menu/property-action-menu.element.ts index 5510e8c53f..ca15dbce7d 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-actions/shared/property-action-menu/property-action-menu.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-actions/shared/property-action-menu/property-action-menu.element.ts @@ -8,6 +8,7 @@ import { umbExtensionsRegistry } from '@umbraco-cms/extensions-registry'; import '../property-action/property-action.element'; import { UmbLitElement } from '@umbraco-cms/element'; +import { UmbObserverController } from '@umbraco-cms/observable-api'; @customElement('umb-property-action-menu') export class UmbPropertyActionMenuElement extends UmbLitElement { @@ -40,13 +41,19 @@ export class UmbPropertyActionMenuElement extends UmbLitElement { `, ]; - @property() - public propertyEditorUIAlias = ''; + // TODO: we need to investigate context api vs values props and events @property() public value?: string; + @property() + set propertyEditorUIAlias(alias: string) { + this._observeActions(alias); + } + + private _actionsObserver?: UmbObserverController; + @state() private _actions: Array = []; @@ -55,22 +62,25 @@ export class UmbPropertyActionMenuElement extends UmbLitElement { private _propertyActionMenuContext = new UmbPropertyActionMenuContext(this); - connectedCallback(): void { - super.connectedCallback(); + constructor() { + super(); - this._observePropertyActions(); - this._observePropertyActionMenuOpenState(); + this.observe(this._propertyActionMenuContext.isOpen, (value) => { + this._open = value; + }); } - private _observePropertyActions() { - this.observe( + private _observeActions(alias: string) { + this._actionsObserver?.destroy(); + this._actionsObserver = this.observe( umbExtensionsRegistry .extensionsOfType('propertyAction') .pipe( - map((propertyActions) => - propertyActions.filter((propertyAction) => - propertyAction.meta.propertyEditors.includes(this.propertyEditorUIAlias) + map((propertyActions) => { + return propertyActions.filter((propertyAction) => + propertyAction.meta.propertyEditors.includes(alias) ) + } ) ), (manifests) => { @@ -79,23 +89,17 @@ export class UmbPropertyActionMenuElement extends UmbLitElement { ); } - private _observePropertyActionMenuOpenState() { - this.observe(this._propertyActionMenuContext.isOpen, (value) => { - this._open = value; - }); - } - private _toggleMenu() { - this._open ? this._propertyActionMenuContext.close() : this._propertyActionMenuContext.open(); + this._propertyActionMenuContext.toggle(); } private _handleClose(event: CustomEvent) { - this._open = false; + this._propertyActionMenuContext.close(); event.stopPropagation(); } render() { - if (this._actions.length > 0) { + return (this._actions.length > 0) ? html` - `; - } - return ''; + ` + : ''; } } diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/uis/content-picker/property-editor-ui-content-picker.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/uis/content-picker/property-editor-ui-content-picker.element.ts index 7e0f41cf04..4d2b95ebfd 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/uis/content-picker/property-editor-ui-content-picker.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/uis/content-picker/property-editor-ui-content-picker.element.ts @@ -90,7 +90,7 @@ export class UmbPropertyEditorUIContentPickerElement extends UmbLitElement { private _setValue(newValue: Array) { this.value = newValue; this._observePickedDocuments(); - this.dispatchEvent(new CustomEvent('property-editor-change', { bubbles: true, composed: true })); + this.dispatchEvent(new CustomEvent('property-editor-value-change', { bubbles: true, composed: true })); } private _renderItem(item: FolderTreeItem) { diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/uis/number/property-editor-ui-number.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/uis/number/property-editor-ui-number.element.ts index 8461d2c513..969495f270 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/uis/number/property-editor-ui-number.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/uis/number/property-editor-ui-number.element.ts @@ -21,7 +21,7 @@ export class UmbPropertyEditorUINumberElement extends LitElement { private onInput(e: InputEvent) { this.value = (e.target as HTMLInputElement).value; - this.dispatchEvent(new CustomEvent('property-editor-change', { bubbles: true, composed: true })); + this.dispatchEvent(new CustomEvent('property-editor-value-change', { bubbles: true, composed: true })); } render() { diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/uis/text-box/property-editor-ui-text-box.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/uis/text-box/property-editor-ui-text-box.element.ts index 4ea729ad37..c59f03d66f 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/uis/text-box/property-editor-ui-text-box.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-editors/uis/text-box/property-editor-ui-text-box.element.ts @@ -21,7 +21,7 @@ export class UmbPropertyEditorUITextBoxElement extends LitElement { private onInput(e: InputEvent) { this.value = (e.target as HTMLInputElement).value; - this.dispatchEvent(new CustomEvent('property-editor-change', { bubbles: true, composed: true })); + this.dispatchEvent(new CustomEvent('property-editor-value-change', { bubbles: true, composed: true })); } render() { 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 098c8a750e..48101e960b 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 @@ -36,7 +36,7 @@ export class UmbPropertyEditorUITextareaElement extends UmbLitElement { private onInput(e: InputEvent) { this.value = (e.target as HTMLInputElement).value; - this.dispatchEvent(new CustomEvent('property-editor-change', { bubbles: true, composed: true })); + this.dispatchEvent(new CustomEvent('property-editor-value-change', { bubbles: true, composed: true })); } render() { diff --git a/src/Umbraco.Web.UI.Client/src/core/models/index.ts b/src/Umbraco.Web.UI.Client/src/core/models/index.ts index cd8f53325d..e8fb169d3f 100644 --- a/src/Umbraco.Web.UI.Client/src/core/models/index.ts +++ b/src/Umbraco.Web.UI.Client/src/core/models/index.ts @@ -24,6 +24,14 @@ export interface Entity { parentKey: string; } +export interface ContentDetails { + key: string; // TODO: Remove this when the backend is fixed + isTrashed: boolean; // TODO: remove only temp part of refactor + properties: Array; + data: Array; + //layout?: any; // TODO: define layout type - make it non-optional +} + export interface UserEntity extends Entity { type: 'user'; } @@ -95,7 +103,7 @@ export interface ContentPropertyData { } // Documents -export interface DocumentDetails extends DocumentTreeItem { +export interface DocumentDetails extends DocumentTreeItem, ContentDetails { key: string; // TODO: Remove this when the backend is fixed isTrashed: boolean; // TODO: remove only temp part of refactor properties: Array;