From 134069851fe1312436bfec23602e29170cbe743f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 10 Jan 2023 14:24:09 +0100 Subject: [PATCH 1/7] added comment --- .../observable-api/unique-behavior-subject.ts | 52 ++++++++++--------- 1 file changed, 27 insertions(+), 25 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/core/observable-api/unique-behavior-subject.ts b/src/Umbraco.Web.UI.Client/src/core/observable-api/unique-behavior-subject.ts index 15e755eee4..d6e7d58fdc 100644 --- a/src/Umbraco.Web.UI.Client/src/core/observable-api/unique-behavior-subject.ts +++ b/src/Umbraco.Web.UI.Client/src/core/observable-api/unique-behavior-subject.ts @@ -28,38 +28,40 @@ type MappingFunction = (mappable: T) => R; type MemoizationFunction = (previousResult: R, currentResult: R) => boolean; function defaultMemoization(previousValue: any, currentValue: any): boolean { - if (typeof previousValue === 'object' && typeof currentValue === 'object') { - return naiveObjectComparison(previousValue, currentValue); - } - return previousValue === currentValue; +if (typeof previousValue === 'object' && typeof currentValue === 'object') { + return naiveObjectComparison(previousValue, currentValue); +} +return previousValue === currentValue; } export function CreateObservablePart ( - source$: Observable, - mappingFunction: MappingFunction, - memoizationFunction?: MemoizationFunction +source$: Observable, +mappingFunction: MappingFunction, +memoizationFunction?: MemoizationFunction ): Observable { - return source$.pipe( - map(mappingFunction), - distinctUntilChanged(memoizationFunction || defaultMemoization), - shareReplay(1) - ) +return source$.pipe( +map(mappingFunction), +distinctUntilChanged(memoizationFunction || defaultMemoization), +shareReplay(1) +) } - +// TODO: Write JSDocs +// TODO: Write comments about what it is and how it works. export class UniqueBehaviorSubject extends BehaviorSubject { - constructor(initialData: T) { - super(deepFreeze(initialData)); - } +constructor(initialData: T) { - next(newData: T): void { - const frozenData = deepFreeze(newData); - if (!naiveObjectComparison(frozenData, this.getValue())) { - super.next(frozenData); - } - } +super(deepFreeze(initialData)); +} - update(data: Partial) { - this.next({ ...this.getValue(), ...data }); - } +next(newData: T): void { +const frozenData = deepFreeze(newData); +if (!naiveObjectComparison(frozenData, this.getValue())) { +super.next(frozenData); +} +} + +update(data: Partial) { +this.next({ ...this.getValue(), ...data }); +} } \ No newline at end of file From b1baa70c4730b02dde402aa29db6a1ee09a51573 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 10 Jan 2023 14:30:40 +0100 Subject: [PATCH 2/7] add Alias to Import --- .../documents/workspace/document-workspace.context.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 da1b8b5d3d..45c863497f 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,5 +1,5 @@ import { UmbWorkspaceContentContext } from '../../../shared/components/workspace/workspace-content/workspace-content.context'; -import { isDocumentDetails, STORE_ALIAS } from 'src/backoffice/documents/documents/document.store'; +import { isDocumentDetails, STORE_ALIAS as DOCUMENT_STORE_ALIAS } from 'src/backoffice/documents/documents/document.store'; import type { UmbDocumentStore, UmbDocumentStoreItemType } from 'src/backoffice/documents/documents/document.store'; import { UmbControllerHostInterface } from 'src/core/controller/controller-host.mixin'; import type { DocumentDetails } from '@umbraco-cms/models'; @@ -35,7 +35,7 @@ const DefaultDocumentData = { export class UmbWorkspaceDocumentContext extends UmbWorkspaceContentContext { constructor(host: UmbControllerHostInterface) { - super(host, DefaultDocumentData, STORE_ALIAS, 'document'); + super(host, DefaultDocumentData, DOCUMENT_STORE_ALIAS, 'document'); } public setPropertyValue(alias: string, value: unknown) { From 8bdc14f818cfae35d4785ce5d57e0a94951735a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 10 Jan 2023 15:34:05 +0100 Subject: [PATCH 3/7] refactor property editor chang event --- .../input-user-group.element.ts | 2 +- .../input-user/input-user.element.ts | 2 +- .../input-section/input-section.element.ts | 2 +- .../workspace-property.element.ts | 41 ++++++++++++------- .../clear/property-action-clear.element.ts | 4 +- ...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 | 10 ++--- 9 files changed, 38 insertions(+), 29 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/auth/components/input-user-group/input-user-group.element.ts b/src/Umbraco.Web.UI.Client/src/auth/components/input-user-group/input-user-group.element.ts index f1133821f4..7f92e52413 100644 --- a/src/Umbraco.Web.UI.Client/src/auth/components/input-user-group/input-user-group.element.ts +++ b/src/Umbraco.Web.UI.Client/src/auth/components/input-user-group/input-user-group.element.ts @@ -63,7 +63,7 @@ export class UmbInputPickerUserGroupElement extends UmbInputListBase { selectionUpdated() { this._observeUserGroups(); - this.dispatchEvent(new CustomEvent('change', { bubbles: true, composed: true })); + this.dispatchEvent(new CustomEvent('property-value-change', { bubbles: true, composed: true })); } private _renderUserGroupList() { diff --git a/src/Umbraco.Web.UI.Client/src/auth/components/input-user/input-user.element.ts b/src/Umbraco.Web.UI.Client/src/auth/components/input-user/input-user.element.ts index dd99d8520d..16d035b2f4 100644 --- a/src/Umbraco.Web.UI.Client/src/auth/components/input-user/input-user.element.ts +++ b/src/Umbraco.Web.UI.Client/src/auth/components/input-user/input-user.element.ts @@ -63,7 +63,7 @@ export class UmbPickerUserElement extends UmbInputListBase { selectionUpdated() { this._observeUser(); - this.dispatchEvent(new CustomEvent('change', { bubbles: true, composed: true })); + this.dispatchEvent(new CustomEvent('property-value-change', { bubbles: true, composed: true })); } private _renderUserList() { diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/input-section/input-section.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/input-section/input-section.element.ts index 8d26ad21e2..26e3295df2 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/input-section/input-section.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/input-section/input-section.element.ts @@ -58,7 +58,7 @@ export class UmbInputPickerSectionElement extends UmbInputListBase { selectionUpdated() { this._observeSections(); // TODO: Use proper event class: - this.dispatchEvent(new CustomEvent('change', { bubbles: true, composed: true })); + this.dispatchEvent(new CustomEvent('property-value-change', { bubbles: true, composed: true })); } renderContent() { 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 a0047bf386..16cfea9da7 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 @@ -136,6 +136,9 @@ export class UmbWorkspacePropertyElement extends UmbLitElement { private propertyEditorUIObserver?: UmbObserverController; + private _valueObserver?: UmbObserverController; + private _configObserver?: UmbObserverController; + constructor() { super(); @@ -147,15 +150,13 @@ export class UmbWorkspacePropertyElement extends UmbLitElement { this._description = description; }); - // TODO: maybe this would be called change. - this.addEventListener('change', this._onPropertyEditorChange as any as EventListener); - } private _onPropertyEditorChange = (e: CustomEvent) => { const target = e.composedPath()[0] as any; this.value = target.value;// Sets value in context. + e.stopPropagation(); }; private _observePropertyEditorUI() { @@ -174,27 +175,37 @@ export class UmbWorkspacePropertyElement extends UmbLitElement { createExtensionElement(manifest) .then((el) => { const oldValue = this._element; + + oldValue?.removeEventListener('change', this._onPropertyEditorChange as any as EventListener); + this._element = el; - this.observe(this._propertyContext.value, (value) => { - if(this._element) { - this._element.value = value; - } - }); - this.observe(this._propertyContext.config, (config) => { - if(this._element) { - this._element.config = config; - } - }); + this._valueObserver?.destroy(); + this._configObserver?.destroy(); + + if(this._element) { + this._element.addEventListener('change', this._onPropertyEditorChange as any as EventListener); + + this._valueObserver = this.observe(this._propertyContext.value, (value) => { + if(this._element) { + this._element.value = value; + } + }); + this._configObserver = this.observe(this._propertyContext.config, (config) => { + if(this._element) { + this._element.config = config; + } + }); + } this.requestUpdate('element', oldValue); - + }) .catch(() => { // TODO: loading JS failed so we should do some nice UI. (This does only happen if extension has a js prop, otherwise we concluded that no source was needed resolved the load.) }); } - + 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 bfa19bce97..b5e81487e1 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 @@ -7,7 +7,7 @@ import { UmbLitElement } from '@umbraco-cms/element'; @customElement('umb-property-action-clear') export class UmbPropertyActionClearElement extends UmbLitElement implements UmbPropertyAction { - + @property() value = ''; @@ -38,7 +38,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 is though bad as it assumes we are dealing with a string. So wouldn't work as a generalized element. - //this.dispatchEvent(new CustomEvent('change', { bubbles: true, composed: true })); + //this.dispatchEvent(new CustomEvent('property-value-change', { bubbles: true, composed: true })); // Or you can do this: this._propertyContext?.resetValue();// This resets value to what the property wants. } 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 e8030b3049..9ce4bfc0d5 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('change', { bubbles: true, composed: true })); + this.dispatchEvent(new CustomEvent('property-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 42d35c2c8e..fb1e450df1 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('change', { bubbles: true, composed: true })); + this.dispatchEvent(new CustomEvent('property-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 104f87accb..8845fd53e5 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('change', { bubbles: true, composed: true })); + this.dispatchEvent(new CustomEvent('property-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 36537cdb84..df1980458f 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 @@ -3,6 +3,7 @@ import { UUITextStyles } from '@umbraco-ui/uui-css/lib'; import { customElement, property } from 'lit/decorators.js'; 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 { @@ -32,16 +33,13 @@ export class UmbPropertyEditorUITextareaElement extends UmbLitElement { } private onInput(e: InputEvent) { - this.value = (e.target as HTMLInputElement).value; - this.dispatchEvent(new CustomEvent('change', { bubbles: true, composed: true })); + this.value = (e.target as UUITextareaElement).value as string; + this.dispatchEvent(new CustomEvent('property-value-change')); } render() { return html` - - ${this.config?.map((property: any) => html`
${property.alias}: ${property.value}
`)} - - `; + `; } } From 9a02b4026de317b328cdc2d9b44eae5d1986a213 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 10 Jan 2023 15:47:03 +0100 Subject: [PATCH 4/7] simpler initialization of Subject --- .../workspace-property/workspace-property.context.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) 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 31cbb8f77e..9dcbc36281 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 @@ -15,6 +15,7 @@ import { UmbContextConsumerController } from "src/core/context-api/consume/conte +// If we get this from the server then we can consider using TypeScripts Partial<> around the model from the Management-API. export type WorkspacePropertyData = { alias?: string; label?: string; @@ -28,7 +29,7 @@ export class UmbWorkspacePropertyContext { private _providerController: UmbContextProviderController; - private _data: UniqueBehaviorSubject> = new UniqueBehaviorSubject({} as WorkspacePropertyData); + private _data = new UniqueBehaviorSubject>({}); public readonly alias = CreateObservablePart(this._data, data => data.alias); public readonly label = CreateObservablePart(this._data, data => data.label); @@ -47,9 +48,9 @@ export class UmbWorkspacePropertyContext { this._providerController = new UmbContextProviderController(host, 'umbPropertyContext', this); - + } - + public setAlias(alias: WorkspacePropertyData['alias']) { this._data.update({alias: alias}); } From af83aabe6dcb5343be5d2805b0748c19c6f51dad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 10 Jan 2023 15:51:27 +0100 Subject: [PATCH 5/7] JSDocs --- .../workspace-content/workspace-content.context.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) 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 0aad0a639b..8febc888ab 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 @@ -8,11 +8,12 @@ import { UmbContextConsumerController } from 'src/core/context-api/consume/conte import { UmbObserverController } from '@umbraco-cms/observable-api'; import { UmbContextProviderController } from 'src/core/context-api/provide/context-provider.controller'; import { EntityTreeItem } from '@umbraco-cms/backend-api'; +import { 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 abstract class UmbWorkspaceContentContext< - ContentTypeType extends EntityTreeItem = EntityTreeItem, + ContentTypeType extends ContentDetails = ContentDetails, StoreType extends UmbNodeStoreBase = UmbNodeStoreBase > { @@ -96,7 +97,7 @@ export abstract class UmbWorkspaceContentContext< if(!this.#isNew) { this._storeSubscription?.destroy(); - this._storeSubscription = new UmbObserverController(this._host, this._store.getByKey(this.entityKey), + 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); @@ -134,4 +135,4 @@ export abstract class UmbWorkspaceContentContext< public destroy(): void { this._data.unsubscribe(); } -} \ No newline at end of file +} From 25fd0bc0754835964c021db657ca2d9d64b03726 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 10 Jan 2023 15:51:52 +0100 Subject: [PATCH 6/7] revert --- .../workspace/workspace-content/workspace-content.context.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) 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 8febc888ab..0eac7ff837 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 @@ -8,12 +8,11 @@ import { UmbContextConsumerController } from 'src/core/context-api/consume/conte import { UmbObserverController } from '@umbraco-cms/observable-api'; import { UmbContextProviderController } from 'src/core/context-api/provide/context-provider.controller'; import { EntityTreeItem } from '@umbraco-cms/backend-api'; -import { 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 abstract class UmbWorkspaceContentContext< - ContentTypeType extends ContentDetails = ContentDetails, + ContentTypeType extends EntityTreeItem = EntityTreeItem, StoreType extends UmbNodeStoreBase = UmbNodeStoreBase > { From 19d83cc7304ba2244736028caac63c5a04f92d75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 10 Jan 2023 15:52:00 +0100 Subject: [PATCH 7/7] JSDocs --- .../observable-api/unique-behavior-subject.ts | 79 ++++++++++++------- 1 file changed, 50 insertions(+), 29 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/core/observable-api/unique-behavior-subject.ts b/src/Umbraco.Web.UI.Client/src/core/observable-api/unique-behavior-subject.ts index d6e7d58fdc..c3a763b325 100644 --- a/src/Umbraco.Web.UI.Client/src/core/observable-api/unique-behavior-subject.ts +++ b/src/Umbraco.Web.UI.Client/src/core/observable-api/unique-behavior-subject.ts @@ -3,7 +3,7 @@ import { BehaviorSubject, distinctUntilChanged, map, Observable, shareReplay } f function deepFreeze(inObj: T): T { Object.freeze(inObj); - + Object.getOwnPropertyNames(inObj).forEach(function (prop) { // eslint-disable-next-line no-prototype-builtins if ((inObj as any).hasOwnProperty(prop) @@ -28,40 +28,61 @@ type MappingFunction = (mappable: T) => R; type MemoizationFunction = (previousResult: R, currentResult: R) => boolean; function defaultMemoization(previousValue: any, currentValue: any): boolean { -if (typeof previousValue === 'object' && typeof currentValue === 'object') { - return naiveObjectComparison(previousValue, currentValue); -} -return previousValue === currentValue; + if (typeof previousValue === 'object' && typeof currentValue === 'object') { + return naiveObjectComparison(previousValue, currentValue); + } + return previousValue === currentValue; } + +/** + * @export + * @method CreateObservablePart + * @param {Observable} source - RxJS Subject to use for this Observable. + * @param {(mappable: T) => R} mappingFunction - Method to return the part for this Observable to return. + * @param {(previousResult: R, currentResult: R) => boolean} [memoizationFunction] - Method to Compare if the data has changed. Should return true when data is different. + * @description - Creates a RxJS Observable from RxJS Subject. + * @example Example create a Observable for part of the data Subject. + * public readonly myPart = CreateObservablePart(this._data, (data) => data.myPart); + */ export function CreateObservablePart ( -source$: Observable, -mappingFunction: MappingFunction, -memoizationFunction?: MemoizationFunction -): Observable { -return source$.pipe( -map(mappingFunction), -distinctUntilChanged(memoizationFunction || defaultMemoization), -shareReplay(1) -) + source$: Observable, + mappingFunction: MappingFunction, + memoizationFunction?: MemoizationFunction + ): Observable { + return source$.pipe( + map(mappingFunction), + distinctUntilChanged(memoizationFunction || defaultMemoization), + shareReplay(1) + ) } -// TODO: Write JSDocs -// TODO: Write comments about what it is and how it works. +/** + * @export + * @class UniqueBehaviorSubject + * @extends {BehaviorSubject} + * @description - A RxJS BehaviorSubject which deepFreezes the data to ensure its not manipulated from any implementations. + * Additionally the Subject ensures the data is unique, not updating any Observes unless there is an actual change of the content. + */ export class UniqueBehaviorSubject extends BehaviorSubject { -constructor(initialData: T) { + constructor(initialData: T) { + super(deepFreeze(initialData)); + } -super(deepFreeze(initialData)); -} + next(newData: T): void { + const frozenData = deepFreeze(newData); + // Only update data if its different than current data. + if (!naiveObjectComparison(frozenData, this.getValue())) { + super.next(frozenData); + } + } -next(newData: T): void { -const frozenData = deepFreeze(newData); -if (!naiveObjectComparison(frozenData, this.getValue())) { -super.next(frozenData); + /** + * Partial update data set, only works for Objects. + * TODO: consider moving this into a specific class for Objects? + * Consider doing similar for Array? + */ + update(data: Partial) { + this.next({ ...this.getValue(), ...data }); + } } -} - -update(data: Partial) { -this.next({ ...this.getValue(), ...data }); -} -} \ No newline at end of file