diff --git a/src/Umbraco.Web.UI.Client/src/app.ts b/src/Umbraco.Web.UI.Client/src/app.ts index 4ef3a5db44..a8ebaaabed 100644 --- a/src/Umbraco.Web.UI.Client/src/app.ts +++ b/src/Umbraco.Web.UI.Client/src/app.ts @@ -25,6 +25,8 @@ import { umbRouterBeforeEnterEventType, } from './core/router'; import { UmbSectionContext } from './section.context'; +import { UmbNodeStore } from './core/stores/node.store'; +import { UmbDataTypeStore } from './core/stores/data-type.store'; // TODO: lazy load these const routes: Array = [ @@ -84,6 +86,10 @@ export class UmbApp extends UmbContextProviderMixin(LitElement) { this.provideContext('umbExtensionRegistry', window.Umbraco.extensionRegistry); this.provideContext('umbSectionContext', new UmbSectionContext(extensionRegistry)); + + // TODO: consider providing somethings for install/login and some only for 'backoffice'. + this.provideContext('umbNodeStore', new UmbNodeStore()); + this.provideContext('umbDataTypeStore', new UmbDataTypeStore()); } private _onBeforeEnter = (event: Event) => { diff --git a/src/Umbraco.Web.UI.Client/src/content/content-editor.element.ts b/src/Umbraco.Web.UI.Client/src/content/content-editor.element.ts index 387b6ca4d0..9bd38fdc79 100644 --- a/src/Umbraco.Web.UI.Client/src/content/content-editor.element.ts +++ b/src/Umbraco.Web.UI.Client/src/content/content-editor.element.ts @@ -2,7 +2,7 @@ import { css, html, LitElement } from 'lit'; import { UUITextStyles } from '@umbraco-ui/uui-css/lib'; import { customElement, property, state } from 'lit/decorators.js'; import { UmbContextConsumerMixin } from '../core/context'; -import { UmbNodesStore } from '../core/stores/nodes.store'; +import { UmbNodeStore } from '../core/stores/node.store'; import { Subscription } from 'rxjs'; import { DocumentNode } from '../mocks/data/content.data'; @@ -51,13 +51,13 @@ class UmbContentEditor extends UmbContextConsumerMixin(LitElement) { @state() _node?: DocumentNode; - private _contentService?: UmbNodesStore; + private _contentService?: UmbNodeStore; private _nodeSubscription?: Subscription; constructor () { super(); - this.consumeContext('umbContentService', (contentService: UmbNodesStore) => { + this.consumeContext('umbNodeStore', (contentService: UmbNodeStore) => { this._contentService = contentService; this._useNode(); }); diff --git a/src/Umbraco.Web.UI.Client/src/content/content-section.element.ts b/src/Umbraco.Web.UI.Client/src/content/content-section.element.ts index f07f64d2b3..54f7128bf1 100644 --- a/src/Umbraco.Web.UI.Client/src/content/content-section.element.ts +++ b/src/Umbraco.Web.UI.Client/src/content/content-section.element.ts @@ -3,7 +3,7 @@ import { css, html, LitElement } from 'lit'; import { customElement } from 'lit/decorators.js'; import { UmbContextConsumerMixin, UmbContextProviderMixin } from '../core/context'; import { UmbRouteLocation, UmbRouter } from '../core/router'; -import { UmbNodesStore } from '../core/stores/nodes.store'; +import { UmbNodeStore as UmbNodeStore } from '../core/stores/node.store'; import { Subscription } from 'rxjs'; import './content-tree.element'; @@ -30,8 +30,6 @@ export class UmbContentSection extends UmbContextProviderMixin(UmbContextConsume constructor () { super(); - this.provideContext('umbContentService', new UmbNodesStore()); - this.consumeContext('umbRouter', (_instance: UmbRouter) => { this._router = _instance; this._useLocation(); diff --git a/src/Umbraco.Web.UI.Client/src/core/extension/extension.registry.ts b/src/Umbraco.Web.UI.Client/src/core/extension/extension.registry.ts index c07c10598e..4898cbaffc 100644 --- a/src/Umbraco.Web.UI.Client/src/core/extension/extension.registry.ts +++ b/src/Umbraco.Web.UI.Client/src/core/extension/extension.registry.ts @@ -1,4 +1,4 @@ -import { BehaviorSubject, Observable } from 'rxjs'; +import { BehaviorSubject, map, Observable } from 'rxjs'; // TODO: how do we want to type extensions? export type UmbExtensionType = 'startUp' | 'section' | 'propertyEditorUI' | 'dashboard'; @@ -48,7 +48,9 @@ export interface UmbManifestDashboardMeta { weight: number; } +// TODO: consider making a UmbStore base class for... export class UmbExtensionRegistry { + private _extensions: BehaviorSubject> = new BehaviorSubject(>[]); public readonly extensions: Observable> = this._extensions.asObservable(); @@ -64,5 +66,12 @@ export class UmbExtensionRegistry { this._extensions.next([...extensionsValues, manifest]); } + getByAlias (alias: string): Observable { + // TODO: make pipes prettier/simpler/reuseable + return this.extensions.pipe(map(((dataTypes: Array) => dataTypes.find((extension: UmbExtensionManifest) => extension.alias === alias) || null))); + } + + + // TODO: implement unregister of extension } \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/core/stores/data-type.store.ts b/src/Umbraco.Web.UI.Client/src/core/stores/data-type.store.ts new file mode 100644 index 0000000000..2b2175d821 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/core/stores/data-type.store.ts @@ -0,0 +1,41 @@ +import { BehaviorSubject, map, Observable } from 'rxjs'; +import { DataTypeEntity } from '../../mocks/data/content.data'; + +export class UmbDataTypeStore { + + private _dataTypes: BehaviorSubject> = new BehaviorSubject(>[]); + public readonly dataTypes: Observable> = this._dataTypes.asObservable(); + + constructor() { + this._dataTypes.next([ + { + id: 1245, + key: 'dt-1', + name: 'TextString (DataType)', + propertyEditorUIAlias: 'Umb.PropertyEditorUI.Text' + }, + { + id: 1244, + key: 'dt-2', + name: 'Textarea (DataType)', + propertyEditorUIAlias: 'Umb.PropertyEditorUI.Textarea' + } + ]) + } + + getById (id: number): Observable { + + // no fetch.. + + // TODO: make pipes prettier/simpler/reuseable + return this.dataTypes.pipe(map(((dataTypes: Array) => dataTypes.find((node: DataTypeEntity) => node.id === id) || null))); + } + + getByKey (key: string): Observable { + + // no fetch.. + + // TODO: make pipes prettier/simpler/reuseable + return this.dataTypes.pipe(map(((dataTypes: Array) => dataTypes.find((node: DataTypeEntity) => node.key === key) || null))); + } +} \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/core/stores/nodes.store.ts b/src/Umbraco.Web.UI.Client/src/core/stores/node.store.ts similarity index 97% rename from src/Umbraco.Web.UI.Client/src/core/stores/nodes.store.ts rename to src/Umbraco.Web.UI.Client/src/core/stores/node.store.ts index 6c733470eb..14dbebccc6 100644 --- a/src/Umbraco.Web.UI.Client/src/core/stores/nodes.store.ts +++ b/src/Umbraco.Web.UI.Client/src/core/stores/node.store.ts @@ -1,7 +1,7 @@ import { BehaviorSubject, map, Observable } from 'rxjs'; import { DocumentNode } from '../../mocks/data/content.data'; -export class UmbNodesStore { +export class UmbNodeStore { private _nodes: BehaviorSubject> = new BehaviorSubject(>[]); public readonly nodes: Observable> = this._nodes.asObservable(); diff --git a/src/Umbraco.Web.UI.Client/src/mocks/data/content.data.ts b/src/Umbraco.Web.UI.Client/src/mocks/data/content.data.ts index 34f2b84e0b..30078f0a73 100644 --- a/src/Umbraco.Web.UI.Client/src/mocks/data/content.data.ts +++ b/src/Umbraco.Web.UI.Client/src/mocks/data/content.data.ts @@ -9,11 +9,21 @@ export interface DocumentNode { layout?: any; // TODO: define layout type - make it non-optional } +export interface DataTypeEntity { + id: number; + key: string; + name: string; + //icon: string; // TODO: should come from the doc type? + //configUI: any; // this is the prevalues... + propertyEditorUIAlias: string; +} + + export interface NodeProperty { alias: string; label: string; description: string; - dataTypeAlias: string; + dataTypeKey: string; tempValue: string; // TODO: remove this - only used for testing } @@ -29,14 +39,14 @@ export const data = [ alias: 'myHeadline', label: 'Textarea label', description: 'this is a textarea property', - dataTypeAlias: 'myTextStringEditor', + dataTypeKey: 'dt-1', tempValue: 'hello world 1' }, { alias: 'myDescription', label: 'Text string label', description: 'This is the a text string property', - dataTypeAlias: 'myTextAreaEditor', + dataTypeKey: 'dt-2', tempValue: 'Tex areaaaa 1' }, ], @@ -79,14 +89,14 @@ export const data = [ alias: 'myHeadline', label: 'Textarea label', description: 'this is a textarea property', - dataTypeAlias: 'myTextStringEditor', + dataTypeKey: 'dt-1', tempValue: 'hello world 2' }, { alias: 'myDescription', label: 'Text string label', description: 'This is the a text string property', - dataTypeAlias: 'myTextAreaEditor', + dataTypeKey: 'dt-2', tempValue: 'Tex areaaaa 2' }, ], diff --git a/src/Umbraco.Web.UI.Client/src/node-editor/node-property-data-type.element.ts b/src/Umbraco.Web.UI.Client/src/node-editor/node-property-data-type.element.ts index 144bb665af..7e68e134a1 100644 --- a/src/Umbraco.Web.UI.Client/src/node-editor/node-property-data-type.element.ts +++ b/src/Umbraco.Web.UI.Client/src/node-editor/node-property-data-type.element.ts @@ -1,26 +1,14 @@ import { css, html, LitElement, PropertyValueMap } from 'lit'; import { UUITextStyles } from '@umbraco-ui/uui-css/lib'; import { customElement, property, state } from 'lit/decorators.js'; - -// TODO: get from Data Type Service? -// TODO: do not have elementName, instead use extension-alias(property editor UI alias) to retrieve element Name, and ensure loaded JS resource. -const DataTypeInGlobalService = [ - { - alias: 'myTextStringEditor', - /* - TODO: use this instead, look up extension API. - propertyEditorUIAlias: 'Umb.PropertyEditorUI.TextString', - */ - elementName: 'umb-property-editor-text' - }, - { - alias: 'myTextAreaEditor', - elementName: 'umb-property-editor-textarea' - } -]; +import { UmbContextConsumerMixin } from '../core/context'; +import { UmbDataTypeStore } from '../core/stores/data-type.store'; +import { mergeMap, Subscription, map } from 'rxjs'; +import { DataTypeEntity } from '../mocks/data/content.data'; +import { UmbExtensionRegistry } from '../core/extension'; @customElement('umb-node-property-data-type') -class UmbNodePropertyDataType extends LitElement { +class UmbNodePropertyDataType extends UmbContextConsumerMixin(LitElement) { static styles = [ UUITextStyles, css` @@ -33,26 +21,14 @@ class UmbNodePropertyDataType extends LitElement { ]; @property() - private _dataTypeAlias?: string | undefined; - public get dataTypeAlias(): string | undefined { - return this._dataTypeAlias; + private _dataTypeKey?: string | undefined; + public get dataTypeKey(): string | undefined { + return this._dataTypeKey; } - public set dataTypeAlias(alias: string | undefined) { - const oldValue = this._dataTypeAlias - this._dataTypeAlias = alias; - - const found = DataTypeInGlobalService.find(x => x.alias === alias); - if(!found) { - // TODO: did not find data-type.. - // TODO: Consider error if undefined, showing a error-data-type, if super duper admin we might show a good error message(as always) and a editable textarea with the value, so there is some debug option available? - return; - } - this._element = document.createElement(found.elementName); - // TODO: Set/Parse Data-Type-UI-configuration - this._element.addEventListener('property-editor-change', this._onPropertyEditorChange); - - this._element.value = this.value;// Be aware its duplicated code - this.requestUpdate('element', oldValue); + public set dataTypeKey(key: string | undefined) { + const oldValue = this._dataTypeKey + this._dataTypeKey = key; + this._useDataType(); } @@ -63,6 +39,61 @@ class UmbNodePropertyDataType extends LitElement { @property() value?:string; + private _extensionRegistry?: UmbExtensionRegistry; + private _dataTypeStore?: UmbDataTypeStore; + private _dataTypeSubscription?: Subscription; + + constructor() { + super(); + this.consumeContext('umbDataTypeStore', (_instance: UmbDataTypeStore) => { + this._dataTypeStore = _instance; + this._useDataType(); + }) + this.consumeContext('umbExtensionRegistry', (_instance: UmbExtensionRegistry) => { + this._extensionRegistry = _instance; + this._useDataType(); + }) + + // TODO: solution to know when both contexts are available + + } + + // TODO: use subscribtion, rename to _useDataType: + private _useDataType() { + this._dataTypeSubscription?.unsubscribe(); + if(this._dataTypeKey && this._extensionRegistry && this._dataTypeStore) { + //this._dataTypeSubscription = this._dataTypeStore.getByKey(this._dataTypeKey).subscribe(this._gotDataType); + + this._dataTypeSubscription = this._dataTypeStore.getByKey(this._dataTypeKey) + .pipe( + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + map((dataTypeEntity: DataTypeEntity) => dataTypeEntity.propertyEditorUIAlias), + mergeMap((alias: string) => this._extensionRegistry?.getByAlias(alias) as any) + ) + .subscribe((dataType) => { + console.log('dataType:', dataType); + }); + } + } + + private _gotDataType(_data: DataTypeEntity | null) { + + if(_data === null) { + // TODO: if dataTypeKey didn't exist in store, we should do some nice UI. + return; + } + + const oldValue = this._element; + this._element = document.createElement(_data.elementName); + + // TODO: Set/Parse Data-Type-UI-configuration + this._element.addEventListener('property-editor-change', this._onPropertyEditorChange); + + this._element.value = this.value;// Be aware its duplicated code + this.requestUpdate('element', oldValue); + } + private _onPropertyEditorChange = ( e:CustomEvent) => { if(e.currentTarget === this._element) { this.value = this._element.value; @@ -85,6 +116,11 @@ class UmbNodePropertyDataType extends LitElement { } } + disconnectedCallback(): void { + super.disconnectedCallback(); + this._dataTypeSubscription?.unsubscribe(); + } + render() { return html`${this._element}`; diff --git a/src/Umbraco.Web.UI.Client/src/node-editor/node-property.element.ts b/src/Umbraco.Web.UI.Client/src/node-editor/node-property.element.ts index 4c99963516..19a9e876f9 100644 --- a/src/Umbraco.Web.UI.Client/src/node-editor/node-property.element.ts +++ b/src/Umbraco.Web.UI.Client/src/node-editor/node-property.element.ts @@ -44,7 +44,7 @@ class UmbNodeProperty extends LitElement {

${this.property.description}

- +
`;