stores + RxJS
This commit is contained in:
@@ -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<UmbRoute> = [
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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<Array<UmbExtensionManifest>> = new BehaviorSubject(<Array<UmbExtensionManifest>>[]);
|
||||
public readonly extensions: Observable<Array<UmbExtensionManifest>> = this._extensions.asObservable();
|
||||
|
||||
@@ -64,5 +66,12 @@ export class UmbExtensionRegistry {
|
||||
this._extensions.next([...extensionsValues, manifest]);
|
||||
}
|
||||
|
||||
getByAlias (alias: string): Observable<UmbExtensionManifest | null> {
|
||||
// TODO: make pipes prettier/simpler/reuseable
|
||||
return this.extensions.pipe(map(((dataTypes: Array<UmbExtensionManifest>) => dataTypes.find((extension: UmbExtensionManifest) => extension.alias === alias) || null)));
|
||||
}
|
||||
|
||||
|
||||
|
||||
// TODO: implement unregister of extension
|
||||
}
|
||||
41
src/Umbraco.Web.UI.Client/src/core/stores/data-type.store.ts
Normal file
41
src/Umbraco.Web.UI.Client/src/core/stores/data-type.store.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import { BehaviorSubject, map, Observable } from 'rxjs';
|
||||
import { DataTypeEntity } from '../../mocks/data/content.data';
|
||||
|
||||
export class UmbDataTypeStore {
|
||||
|
||||
private _dataTypes: BehaviorSubject<Array<DataTypeEntity>> = new BehaviorSubject(<Array<DataTypeEntity>>[]);
|
||||
public readonly dataTypes: Observable<Array<DataTypeEntity>> = 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<DataTypeEntity | null> {
|
||||
|
||||
// no fetch..
|
||||
|
||||
// TODO: make pipes prettier/simpler/reuseable
|
||||
return this.dataTypes.pipe(map(((dataTypes: Array<DataTypeEntity>) => dataTypes.find((node: DataTypeEntity) => node.id === id) || null)));
|
||||
}
|
||||
|
||||
getByKey (key: string): Observable<DataTypeEntity | null> {
|
||||
|
||||
// no fetch..
|
||||
|
||||
// TODO: make pipes prettier/simpler/reuseable
|
||||
return this.dataTypes.pipe(map(((dataTypes: Array<DataTypeEntity>) => dataTypes.find((node: DataTypeEntity) => node.key === key) || null)));
|
||||
}
|
||||
}
|
||||
@@ -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<Array<DocumentNode>> = new BehaviorSubject(<Array<DocumentNode>>[]);
|
||||
public readonly nodes: Observable<Array<DocumentNode>> = this._nodes.asObservable();
|
||||
@@ -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'
|
||||
},
|
||||
],
|
||||
|
||||
@@ -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}`;
|
||||
|
||||
@@ -44,7 +44,7 @@ class UmbNodeProperty extends LitElement {
|
||||
<p>${this.property.description}</p>
|
||||
</div>
|
||||
<div class="editor">
|
||||
<umb-node-property-data-type .dataTypeAlias=${this.property.dataTypeAlias} .value=${this.value} @property-data-type-change=${this._onPropertyDataTypeChange}></umb-node-property-data-type>
|
||||
<umb-node-property-data-type .dataTypeKey=${this.property.dataTypeKey} .value=${this.value} @property-data-type-change=${this._onPropertyDataTypeChange}></umb-node-property-data-type>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
Reference in New Issue
Block a user