load trees from manifest
This commit is contained in:
@@ -5,8 +5,11 @@ import { data as dataTypeData } from '../../../mocks/data/data-type.data';
|
||||
import { data as documentTypeData } from '../../../mocks/data/document-type.data';
|
||||
import { UmbContextConsumerMixin } from '../../../core/context';
|
||||
import { UmbDataTypeStore } from '../../../core/stores/data-type.store';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { map, Subscription, first } from 'rxjs';
|
||||
import { UmbDocumentTypeStore } from '../../../core/stores/document-type.store';
|
||||
import { createExtensionElement, UmbExtensionRegistry } from '../../../core/extension';
|
||||
import '../../tree/tree.element';
|
||||
import { UmbSectionContext } from '../section.context';
|
||||
|
||||
@customElement('umb-settings-section-tree')
|
||||
class UmbSettingsSectionTree extends UmbContextConsumerMixin(LitElement) {
|
||||
@@ -26,15 +29,32 @@ class UmbSettingsSectionTree extends UmbContextConsumerMixin(LitElement) {
|
||||
@state()
|
||||
_documentTypes: Array<any> = [];
|
||||
|
||||
@state()
|
||||
private _trees: Array<any> = [];
|
||||
|
||||
@state()
|
||||
private _currentSectionAlias?: string;
|
||||
|
||||
private _dataTypeStore?: UmbDataTypeStore;
|
||||
private _documentTypeStore?: UmbDocumentTypeStore;
|
||||
|
||||
private _dataTypesSubscription?: Subscription;
|
||||
private _documentTypesSubscription?: Subscription;
|
||||
|
||||
private _extensionStore?: UmbExtensionRegistry;
|
||||
private _treeSubscription?: Subscription;
|
||||
|
||||
private _sectionContextSubscription?: Subscription;
|
||||
private _sectionContext?: UmbSectionContext;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.consumeContext('umbSectionContext', (context: UmbSectionContext) => {
|
||||
this._sectionContext = context;
|
||||
this._useSectionContext();
|
||||
});
|
||||
|
||||
// TODO: temp solution until we know where to get tree data from
|
||||
this.consumeContext('umbDataTypeStore', (store) => {
|
||||
this._dataTypeStore = store;
|
||||
@@ -52,6 +72,11 @@ class UmbSettingsSectionTree extends UmbContextConsumerMixin(LitElement) {
|
||||
this._documentTypes = documentTypes;
|
||||
});
|
||||
});
|
||||
|
||||
this.consumeContext('umbExtensionRegistry', (store) => {
|
||||
this._extensionStore = store;
|
||||
this._useTrees();
|
||||
});
|
||||
}
|
||||
|
||||
disconnectedCallback(): void {
|
||||
@@ -61,6 +86,35 @@ class UmbSettingsSectionTree extends UmbContextConsumerMixin(LitElement) {
|
||||
this._documentTypesSubscription?.unsubscribe();
|
||||
}
|
||||
|
||||
private _useSectionContext() {
|
||||
this._sectionContextSubscription?.unsubscribe();
|
||||
|
||||
this._sectionContextSubscription = this._sectionContext?.data.pipe(first()).subscribe((section) => {
|
||||
this._currentSectionAlias = section.alias;
|
||||
});
|
||||
}
|
||||
|
||||
private _useTrees() {
|
||||
//TODO: Merge streams
|
||||
if (this._extensionStore && this._currentSectionAlias) {
|
||||
this._treeSubscription?.unsubscribe();
|
||||
|
||||
this._treeSubscription = this._extensionStore
|
||||
?.extensionsOfType('tree')
|
||||
.pipe(
|
||||
map((extensions) =>
|
||||
extensions
|
||||
.filter((extension) => extension.meta.sections.includes(this._currentSectionAlias as string)) // TODO: Why do whe need "as string" here??
|
||||
.sort((a, b) => b.meta.weight - a.meta.weight)
|
||||
)
|
||||
)
|
||||
.subscribe((treeExtensions) => {
|
||||
this._trees = treeExtensions;
|
||||
console.log('Wrosk', this._trees);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<a href="${'/section/settings'}">
|
||||
@@ -85,6 +139,8 @@ class UmbSettingsSectionTree extends UmbContextConsumerMixin(LitElement) {
|
||||
`
|
||||
)}
|
||||
</uui-menu-item>
|
||||
|
||||
${this._trees.map((tree) => html`<umb-tree .tree=${tree}></umb-tree>`)}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import { css, html, LitElement } from 'lit';
|
||||
import { UUITextStyles } from '@umbraco-ui/uui-css/lib';
|
||||
import { customElement } from 'lit/decorators.js';
|
||||
import './tree-navigator.element';
|
||||
import './tree-item.element';
|
||||
|
||||
@customElement('umb-datatype-tree')
|
||||
export class UmbDatatypeTree extends LitElement {
|
||||
static styles = [UUITextStyles, css``];
|
||||
|
||||
render() {
|
||||
return html`<umb-tree-navigator></umb-tree-navigator>`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'umb-datatype-tree': UmbDatatypeTree;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import { css, html, LitElement } from 'lit';
|
||||
import { UUITextStyles } from '@umbraco-ui/uui-css/lib';
|
||||
import { customElement, property } from 'lit/decorators.js';
|
||||
import './tree-navigator.element';
|
||||
import './tree-item.element';
|
||||
|
||||
@customElement('umb-document-type-tree')
|
||||
export class UmbDocumentTypeTree extends LitElement {
|
||||
static styles = [UUITextStyles, css``];
|
||||
|
||||
@property({ type: String })
|
||||
public alias = '';
|
||||
|
||||
render() {
|
||||
return html`<umb-tree-navigator .label=${this.alias}></umb-tree-navigator>`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'umb-document-type-tree': UmbDocumentTypeTree;
|
||||
}
|
||||
}
|
||||
@@ -10,19 +10,35 @@ export class UmbTreeNavigator extends UmbContextProviderMixin(LitElement) {
|
||||
|
||||
private _treeService: UmbTreeService;
|
||||
|
||||
@state()
|
||||
id = '2';
|
||||
|
||||
@state()
|
||||
label = '';
|
||||
|
||||
@state()
|
||||
hasChildren = false;
|
||||
|
||||
@state()
|
||||
loading = true;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this._treeService = new UmbTreeService();
|
||||
this.provideContext('umbTreeService', this._treeService);
|
||||
}
|
||||
|
||||
renderItems() {
|
||||
return this._treeService.getRoot().map((item) => {
|
||||
return html`<umb-tree-item .id=${item.id} .hasChildren=${item.hasChildren} .label=${item.name}></umb-tree-item>`;
|
||||
this._treeService.getTreeItem(this.id).then((item) => {
|
||||
this.label = item.name;
|
||||
this.hasChildren = item.hasChildren;
|
||||
this.loading = false;
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
return this.renderItems();
|
||||
return html`<umb-tree-item
|
||||
.id=${this.id}
|
||||
.label=${this.label}
|
||||
?hasChildren=${this.hasChildren}
|
||||
.loading=${this.loading}></umb-tree-item> `;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
import { css, CSSResultGroup, html, LitElement } from 'lit';
|
||||
import { UUITextStyles } from '@umbraco-ui/uui-css/lib';
|
||||
import { customElement, property, state } from 'lit/decorators.js';
|
||||
import { createExtensionElement, UmbExtensionManifestTree } from '../../core/extension';
|
||||
import { UmbTreeNavigator } from './tree-navigator.element';
|
||||
|
||||
@customElement('umb-tree')
|
||||
export class UmbTree extends LitElement {
|
||||
static styles: CSSResultGroup = [UUITextStyles];
|
||||
|
||||
private _tree?: UmbExtensionManifestTree;
|
||||
|
||||
@property({ type: Object })
|
||||
public get tree(): UmbExtensionManifestTree | undefined {
|
||||
return this._tree;
|
||||
}
|
||||
public set tree(value: UmbExtensionManifestTree | undefined) {
|
||||
this._tree = value;
|
||||
this._createElement();
|
||||
}
|
||||
|
||||
@state()
|
||||
private _element?: any;
|
||||
|
||||
private async _createElement() {
|
||||
if (!this.tree) return;
|
||||
|
||||
try {
|
||||
this._element = (await createExtensionElement(this.tree)) as any | undefined;
|
||||
if (!this._element) return;
|
||||
this._element.alias = this._tree?.alias;
|
||||
} catch (error) {
|
||||
// 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`${this._element}`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'umb-tree': UmbTree;
|
||||
}
|
||||
}
|
||||
@@ -5,13 +5,18 @@ export class UmbTreeService {
|
||||
return fakeApi.getTreeRoot();
|
||||
}
|
||||
|
||||
public async getTreeItem(id: string) {
|
||||
await new Promise((resolve) => setTimeout(resolve, Math.random() * 2000));
|
||||
return fakeApi.getTreeItem(id);
|
||||
}
|
||||
|
||||
public async getChildren(id: string) {
|
||||
await new Promise((resolve) => setTimeout(resolve, Math.random() * 2000));
|
||||
return fakeApi.getTreeChildren(id);
|
||||
}
|
||||
}
|
||||
|
||||
// EVERYTHING BELOW IS FAKE DATA
|
||||
// EVERYTHING BELOW IS FAKE MOCK DATA AND WILL BE REMOVED
|
||||
|
||||
const fakeApi = {
|
||||
//find nested child id of array
|
||||
@@ -28,6 +33,16 @@ const fakeApi = {
|
||||
});
|
||||
},
|
||||
|
||||
getTreeItem: (id: string) => {
|
||||
const item = recursive(treeData, id);
|
||||
if (!item) return 'not found';
|
||||
|
||||
return {
|
||||
...item,
|
||||
hasChildren: item.children.length > 0,
|
||||
};
|
||||
},
|
||||
|
||||
getTreeRoot: () => {
|
||||
return treeData.map((item) => {
|
||||
return {
|
||||
@@ -79,20 +94,20 @@ const treeData = [
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
name: 'Templates',
|
||||
name: 'DataTypes',
|
||||
children: [
|
||||
{
|
||||
id: '2-1',
|
||||
name: 'Templates-2-1',
|
||||
name: 'DataTypes-2-1',
|
||||
children: [],
|
||||
},
|
||||
{
|
||||
id: '2-2',
|
||||
name: 'Templates-2-2',
|
||||
name: 'DataTypes-2-2',
|
||||
children: [
|
||||
{
|
||||
id: '2-2-1',
|
||||
name: 'Templates-2-2-1',
|
||||
name: 'DataTypes-2-2-1',
|
||||
children: [],
|
||||
},
|
||||
],
|
||||
|
||||
@@ -25,6 +25,16 @@ export type UmbExtensionManifestSection = {
|
||||
meta: UmbManifestSectionMeta;
|
||||
} & UmbExtensionManifestBase;
|
||||
|
||||
//tree
|
||||
export type UmbManifestTreeMeta = {
|
||||
weight: number;
|
||||
sections: Array<string>;
|
||||
};
|
||||
export type UmbExtensionManifestTree = {
|
||||
type: 'tree';
|
||||
meta: UmbManifestTreeMeta;
|
||||
} & UmbExtensionManifestBase;
|
||||
|
||||
// propertyEditor:
|
||||
export type UmbManifestPropertyEditorMeta = {
|
||||
icon: string;
|
||||
@@ -74,6 +84,7 @@ export type UmbExtensionManifestEditorView = {
|
||||
|
||||
export type UmbExtensionManifestCore =
|
||||
| UmbExtensionManifestSection
|
||||
| UmbExtensionManifestTree
|
||||
| UmbExtensionManifestDashboard
|
||||
| UmbExtensionManifestPropertyEditorUI
|
||||
| UmbExtensionManifestPropertyAction
|
||||
@@ -115,6 +126,7 @@ export class UmbExtensionRegistry {
|
||||
|
||||
// Typings concept, need to put all core types to get a good array return type for the provided type...
|
||||
extensionsOfType(type: 'section'): Observable<Array<UmbExtensionManifestSection>>;
|
||||
extensionsOfType(type: 'tree'): Observable<Array<UmbExtensionManifestTree>>;
|
||||
extensionsOfType(type: 'dashboard'): Observable<Array<UmbExtensionManifestDashboard>>;
|
||||
extensionsOfType(type: 'editorView'): Observable<Array<UmbExtensionManifestEditorView>>;
|
||||
extensionsOfType(type: 'propertyEditorUI'): Observable<Array<UmbExtensionManifestPropertyEditorUI>>;
|
||||
|
||||
38
src/Umbraco.Web.UI.Client/src/core/stores/entity.store.ts
Normal file
38
src/Umbraco.Web.UI.Client/src/core/stores/entity.store.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import { BehaviorSubject, map, Observable } from 'rxjs';
|
||||
import { Entity } from '../../mocks/data/entity.data';
|
||||
import { umbNodeData } from '../../mocks/data/node.data';
|
||||
|
||||
export class UmbEntityStore {
|
||||
private _entities: BehaviorSubject<Array<Entity>> = new BehaviorSubject(<Array<Entity>>[]);
|
||||
public readonly entities: Observable<Array<Entity>> = this._entities.asObservable();
|
||||
|
||||
getById(id: number): Observable<Entity | null> {
|
||||
// fetch from server and update store
|
||||
fetch(`/umbraco/backoffice/content/${id}`)
|
||||
.then((res) => res.json())
|
||||
.then((data) => {
|
||||
this._updateStore(data);
|
||||
});
|
||||
|
||||
return this.entities.pipe(map((nodes: Array<Entity>) => nodes.find((node: Entity) => node.id === id) || null));
|
||||
}
|
||||
|
||||
private _updateStore(fetchedNodes: Array<any>) {
|
||||
const storedNodes = this._entities.getValue();
|
||||
const updated: Entity[] = [...storedNodes];
|
||||
|
||||
fetchedNodes.forEach((fetchedNode) => {
|
||||
const index = storedNodes.map((storedNode) => storedNode.id).indexOf(fetchedNode.id);
|
||||
|
||||
if (index !== -1) {
|
||||
// If the node is already in the store, update it
|
||||
updated[index] = fetchedNode;
|
||||
} else {
|
||||
// If the node is not in the store, add it
|
||||
updated.push(fetchedNode);
|
||||
}
|
||||
});
|
||||
|
||||
this._entities.next([...updated]);
|
||||
}
|
||||
}
|
||||
43
src/Umbraco.Web.UI.Client/src/mocks/data/entity.data.ts
Normal file
43
src/Umbraco.Web.UI.Client/src/mocks/data/entity.data.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
export interface Entity {
|
||||
id: number;
|
||||
key: string;
|
||||
name: string;
|
||||
icon: string; // TODO: Should this be here?
|
||||
type: string;
|
||||
hasChildren: boolean; // TODO: Should this be here?
|
||||
}
|
||||
|
||||
export const data: Array<Entity> = [
|
||||
{
|
||||
id: 1,
|
||||
key: '74e4008a-ea4f-4793-b924-15e02fd380d1',
|
||||
name: 'Document 1',
|
||||
type: 'document',
|
||||
icon: 'document',
|
||||
hasChildren: false,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
key: '74e4008a-ea4f-4793-b924-15e02fd380d2',
|
||||
name: 'Document 2',
|
||||
type: 'document',
|
||||
icon: 'favorite',
|
||||
hasChildren: false,
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
key: 'cdd30288-2d1c-41b4-89a9-61647b4a10d5',
|
||||
name: 'Document 3',
|
||||
type: 'document',
|
||||
icon: 'document',
|
||||
hasChildren: false,
|
||||
},
|
||||
{
|
||||
id: 2001,
|
||||
key: 'f2f81a40-c989-4b6b-84e2-057cecd3adc1',
|
||||
name: 'Media 1',
|
||||
type: 'media',
|
||||
icon: 'picture',
|
||||
hasChildren: false,
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1 @@
|
||||
//TODO: MAKE
|
||||
@@ -241,4 +241,37 @@ export const internalManifests: Array<UmbExtensionManifestCore> = [
|
||||
group: 'common',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'tree',
|
||||
alias: 'Umb.Tree.Datatypes',
|
||||
name: 'DataTypes',
|
||||
elementName: 'umb-datatype-tree',
|
||||
js: () => import('./backoffice/tree/datatypes-tree.element'),
|
||||
meta: {
|
||||
weight: -10,
|
||||
sections: ['Umb.Section.Settings'],
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'tree',
|
||||
alias: 'Umb.Tree.DocumentTypes',
|
||||
name: 'DocumentTypes',
|
||||
elementName: 'umb-document-type-tree',
|
||||
js: () => import('./backoffice/tree/document-type-tree.element'),
|
||||
meta: {
|
||||
weight: -10,
|
||||
sections: ['Umb.Section.Settings'],
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'tree',
|
||||
alias: 'Umb.Tree.DocumentTypes',
|
||||
name: 'DocumentTypes',
|
||||
elementName: 'umb-document-type-tree',
|
||||
js: () => import('./backoffice/tree/document-type-tree.element'),
|
||||
meta: {
|
||||
weight: -10,
|
||||
sections: ['Umb.Section.Content'],
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user