render editor based on entity type

This commit is contained in:
Mads Rasmussen
2022-08-30 13:36:46 +02:00
parent baa936e5d4
commit 6b6d384a44
19 changed files with 352 additions and 244 deletions

View File

@@ -393,8 +393,6 @@ components:
MetaTree:
type: object
properties:
editor:
type: string
pathname:
type: string
label:
@@ -407,7 +405,6 @@ components:
items:
type: string
required:
- editor
- pathname
- label
- weight
@@ -434,6 +431,13 @@ components:
- meta
- name
- alias
MetaEditor:
type: object
properties:
entityType:
type: string
required:
- entityType
IManifestEditor:
type: object
properties:
@@ -441,18 +445,19 @@ components:
type: string
enum:
- editor
meta:
$ref: '#/components/schemas/MetaEditor'
name:
type: string
js:
type: string
elementName:
type: string
meta:
type: object
alias:
type: string
required:
- type
- meta
- name
- alias
MetaEntityAction:

View File

@@ -120,7 +120,6 @@ export interface components {
alias: string;
};
MetaTree: {
editor: string;
pathname: string;
label: string;
/** Format: float */
@@ -136,13 +135,16 @@ export interface components {
elementName?: string;
alias: string;
};
MetaEditor: {
entityType: string;
};
IManifestEditor: {
/** @enum {string} */
type: "editor";
meta: components["schemas"]["MetaEditor"];
name: string;
js?: string;
elementName?: string;
meta?: { [key: string]: unknown };
alias: string;
};
MetaEntityAction: {

View File

@@ -9,7 +9,7 @@ import { UmbDataTypeStore } from '../../../core/stores/data-type.store';
import { DataTypeEntity } from '../../../mocks/data/data-type.data';
import { UmbDataTypeContext } from './data-type.context';
import '../shared/editor-entity/editor-entity.element';
import '../shared/editor-entity-layout/editor-entity-layout.element';
// Lazy load
// TODO: Make this dynamic, use load-extensions method to loop over extensions for this node.
@@ -124,7 +124,7 @@ export class UmbEditorDataTypeElement extends UmbContextProviderMixin(UmbContext
return html`
${this._dataType
? html`
<umb-editor-entity alias="Umb.Editor.DataType">
<umb-editor-entity-layout alias="Umb.Editor.DataType">
<uui-input id="name" slot="name" .value=${this._dataType?.name} @input="${this._handleInput}"></uui-input>
<!-- TODO: these could be extensions points too -->
<div slot="actions">
@@ -135,7 +135,7 @@ export class UmbEditorDataTypeElement extends UmbContextProviderMixin(UmbContext
label="Save"
.state="${this._saveButtonState}"></uui-button>
</div>
</umb-editor-entity>
</umb-editor-entity-layout>
`
: nothing}
`;

View File

@@ -9,7 +9,7 @@ import { UmbDocumentTypeStore } from '../../../core/stores/document-type.store';
import { DocumentTypeEntity } from '../../../mocks/data/document-type.data';
import { UmbDocumentTypeContext } from './document-type.context';
import '../shared/editor-entity/editor-entity.element';
import '../shared/editor-entity-layout/editor-entity-layout.element';
// Lazy load
// TODO: Make this dynamic, use load-extensions method to loop over extensions for this node.
@@ -124,7 +124,7 @@ export class UmbEditorDocumentTypeElement extends UmbContextProviderMixin(UmbCon
render() {
return html`
<umb-editor-entity alias="Umb.Editor.DocumentType">
<umb-editor-entity-layout alias="Umb.Editor.DocumentType">
<div slot="icon">Icon</div>
<div slot="name">
@@ -144,7 +144,7 @@ export class UmbEditorDocumentTypeElement extends UmbContextProviderMixin(UmbCon
label="Save"
.state="${this._saveButtonState}"></uui-button>
</div>
</umb-editor-entity>
</umb-editor-entity-layout>
`;
}
}

View File

@@ -1,4 +1,4 @@
import '../shared/editor-entity/editor-entity.element';
import '../shared/editor-entity-layout/editor-entity-layout.element';
import { html, LitElement } from 'lit';
import { customElement, state } from 'lit/decorators.js';
@@ -52,7 +52,7 @@ export class UmbEditorExtensionsElement extends UmbContextConsumerMixin(LitEleme
render() {
return html`
<umb-editor-entity alias="Umb.Editor.Extensions">
<umb-editor-entity-layout alias="Umb.Editor.Extensions">
<h3 slot="name">Extensions</h3>
<uui-box headline="Extensions">
<uui-table>
@@ -75,7 +75,7 @@ export class UmbEditorExtensionsElement extends UmbContextConsumerMixin(LitEleme
)}
</uui-table>
</uui-box>
</umb-editor-entity>
</umb-editor-entity-layout>
`;
}
}

View File

@@ -2,7 +2,7 @@ import { UUITextStyles } from '@umbraco-ui/uui-css/lib';
import { css, html, LitElement } from 'lit';
import { customElement, property } from 'lit/decorators.js';
import '../shared/editor-entity/editor-entity.element';
import '../shared/editor-entity-layout/editor-entity-layout.element';
@customElement('umb-editor-member-group')
export class UmbEditorMemberGroupElement extends LitElement {
@@ -21,7 +21,9 @@ export class UmbEditorMemberGroupElement extends LitElement {
id!: string;
render() {
return html` <umb-editor-entity alias="Umb.Editor.MemberGroup">Member Group Editor</umb-editor-entity> `;
return html`
<umb-editor-entity-layout alias="Umb.Editor.MemberGroup">Member Group Editor</umb-editor-entity-layout>
`;
}
}

View File

@@ -2,7 +2,7 @@ import { UUITextStyles } from '@umbraco-ui/uui-css/lib';
import { css, html, LitElement } from 'lit';
import { customElement, property } from 'lit/decorators.js';
import '../shared/editor-entity/editor-entity.element';
import '../shared/editor-entity-layout/editor-entity-layout.element';
@customElement('umb-editor-member')
export class UmbEditorMemberElement extends LitElement {
@@ -21,7 +21,7 @@ export class UmbEditorMemberElement extends LitElement {
id!: string;
render() {
return html` <umb-editor-entity alias="Umb.Editor.Member">Member Editor</umb-editor-entity> `;
return html` <umb-editor-entity-layout alias="Umb.Editor.Member">Member Editor</umb-editor-entity-layout> `;
}
}

View File

@@ -0,0 +1,194 @@
import '../editor-layout/editor-layout.element';
import { UUITextStyles } from '@umbraco-ui/uui-css/lib';
import { css, html, LitElement, nothing } from 'lit';
import { customElement, property, state } from 'lit/decorators.js';
import { IRoute, IRoutingInfo, RouterSlot } from 'router-slot';
import { map, Subscription } from 'rxjs';
import { UmbContextConsumerMixin } from '../../../../core/context';
import { UmbExtensionRegistry } from '../../../../core/extension';
import type { ManifestEditorView } from '../../../../core/models';
@customElement('umb-editor-entity-layout')
export class UmbEditorEntityLayout extends UmbContextConsumerMixin(LitElement) {
static styles = [
UUITextStyles,
css`
:host {
display: block;
width: 100%;
height: 100%;
}
#header {
display: flex;
gap: 16px;
align-items: center;
min-height: 60px;
}
#name {
display: block;
flex: 1 1 auto;
}
#footer {
display: flex;
height: 100%;
align-items: center;
gap: 16px;
flex: 1 1 auto;
}
#actions {
display: block;
margin-left: auto;
}
uui-input {
width: 100%;
}
uui-tab-group {
--uui-tab-divider: var(--uui-color-border);
border-left: 1px solid var(--uui-color-border);
border-right: 1px solid var(--uui-color-border);
}
`,
];
@property()
alias = '';
@property()
name = '';
@state()
private _editorViews: Array<ManifestEditorView> = [];
@state()
private _currentView = '';
@state()
private _routes: Array<IRoute> = [];
private _extensionRegistry?: UmbExtensionRegistry;
private _editorViewsSubscription?: Subscription;
private _routerFolder = '';
constructor() {
super();
this.consumeContext('umbExtensionRegistry', (extensionRegistry: UmbExtensionRegistry) => {
this._extensionRegistry = extensionRegistry;
this._useEditorViews();
});
}
connectedCallback(): void {
super.connectedCallback();
/* TODO: find a way to construct absolute urls */
this._routerFolder = window.location.pathname.split('/view')[0];
}
private _useEditorViews() {
this._editorViewsSubscription?.unsubscribe();
this._editorViewsSubscription = this._extensionRegistry
?.extensionsOfType('editorView')
.pipe(
map((extensions) =>
extensions
.filter((extension) => extension.meta.editors.includes(this.alias))
.sort((a, b) => b.meta.weight - a.meta.weight)
)
)
.subscribe((editorViews) => {
this._editorViews = editorViews;
this._createRoutes();
});
}
private async _createRoutes() {
if (this._editorViews.length > 0) {
this._routes = [];
this._routes = this._editorViews.map((view) => {
return {
path: `view/${view.meta.pathname}`,
component: () => document.createElement(view.elementName || 'div'),
setup: (element: HTMLElement, info: IRoutingInfo) => {
this._currentView = info.match.route.path;
},
};
});
this._routes.push({
path: '**',
redirectTo: `view/${this._editorViews?.[0].meta.pathname}`,
});
this.requestUpdate();
await this.updateComplete;
this._forceRouteRender();
}
}
// TODO: Figure out why this has been necessary for this case. Come up with another case
private _forceRouteRender() {
const routerSlotEl = this.shadowRoot?.querySelector('router-slot') as RouterSlot;
if (routerSlotEl) {
routerSlotEl.render();
}
}
private _renderViews() {
return html`
${this._editorViews?.length > 0
? html`
<uui-tab-group slot="views">
${this._editorViews.map(
(view: ManifestEditorView) => html`
<uui-tab
.label="${view.name}"
href="${this._routerFolder}/view/${view.meta.pathname}"
?active="${this._currentView.includes(view.meta.pathname)}">
<uui-icon slot="icon" name="${view.meta.icon}"></uui-icon>
${view.name}
</uui-tab>
`
)}
</uui-tab-group>
`
: nothing}
`;
}
render() {
return html`
<umb-editor-layout>
<div id="header" slot="header">
<slot id="icon" name="icon"></slot>
<slot id="name" name="name"></slot>
${this._renderViews()}
</div>
<router-slot .routes="${this._routes}"></router-slot>
<slot></slot>
<div id="footer" slot="footer">
<slot name="footer"></slot>
<slot id="actions" name="actions"></slot>
</div>
</umb-editor-layout>
`;
}
}
declare global {
interface HTMLElementTagNameMap {
'umb-editor-entity-layout': UmbEditorEntityLayout;
}
}

View File

@@ -1,21 +1,21 @@
import './editor-entity.element';
import './editor-entity-layout.element';
import { Meta, Story } from '@storybook/web-components';
import { html } from 'lit-html';
import type { UmbEditorEntity } from './editor-entity.element';
import type { UmbEditorEntityLayout } from './editor-entity-layout.element';
export default {
title: 'Editors/Shared/Editor Entity',
component: 'umb-editor-entity',
id: 'umb-editor-entity',
title: 'Editors/Shared/Editor Entity Layout',
component: 'umb-editor-entity-layout',
id: 'umb-editor-entity-layout',
} as Meta;
export const AAAOverview: Story<UmbEditorEntity> = () => html` <umb-editor-entity>
export const AAAOverview: Story<UmbEditorEntityLayout> = () => html` <umb-editor-entity-layout>
<div slot="icon"><uui-button color="" look="placeholder">Icon slot</uui-button></div>
<div slot="name"><uui-button color="" look="placeholder">Name slot</uui-button></div>
<div slot="footer"><uui-button color="" look="placeholder">Footer slot</uui-button></div>
<div slot="actions"><uui-button color="" look="placeholder">Actions slot</uui-button></div>
<uui-button color="" look="placeholder">Main slot</uui-button>
</umb-editor-entity>`;
</umb-editor-entity-layout>`;
AAAOverview.storyName = 'Overview';

View File

@@ -1,17 +1,13 @@
import '../editor-layout/editor-layout.element';
import { css, html, LitElement } from 'lit';
import { UUITextStyles } from '@umbraco-ui/uui-css/lib';
import { css, html, LitElement, nothing } from 'lit';
import { customElement, property, state } from 'lit/decorators.js';
import { IRoute, IRoutingInfo, RouterSlot } from 'router-slot';
import { map, Subscription } from 'rxjs';
import { UmbContextConsumerMixin } from '../../../../core/context';
import { UmbExtensionRegistry } from '../../../../core/extension';
import type { ManifestEditorView } from '../../../../core/models';
import { createExtensionElement, UmbExtensionRegistry } from '../../../../core/extension';
import { map } from 'rxjs';
import { ManifestEditor } from '../../../../core/models';
@customElement('umb-editor-entity')
export class UmbEditorEntity extends UmbContextConsumerMixin(LitElement) {
export class UmbEditorEntityElement extends UmbContextConsumerMixin(LitElement) {
static styles = [
UUITextStyles,
css`
@@ -20,175 +16,74 @@ export class UmbEditorEntity extends UmbContextConsumerMixin(LitElement) {
width: 100%;
height: 100%;
}
#header {
display: flex;
gap: 16px;
align-items: center;
min-height: 60px;
}
#name {
display: block;
flex: 1 1 auto;
}
#footer {
display: flex;
height: 100%;
align-items: center;
gap: 16px;
flex: 1 1 auto;
}
#actions {
display: block;
margin-left: auto;
}
uui-input {
width: 100%;
}
uui-tab-group {
--uui-tab-divider: var(--uui-color-border);
border-left: 1px solid var(--uui-color-border);
border-right: 1px solid var(--uui-color-border);
}
`,
];
@property()
alias = '';
public entityKey!: string;
private _entityType = '';
@property()
name = '';
public get entityType(): string {
return this._entityType;
}
public set entityType(value: string) {
this._entityType = value;
this._useEditors();
}
@state()
private _editorViews: Array<ManifestEditorView> = [];
@state()
private _currentView = '';
@state()
private _routes: Array<IRoute> = [];
private _element?: any;
private _extensionRegistry?: UmbExtensionRegistry;
private _editorViewsSubscription?: Subscription;
private _routerFolder = '';
constructor() {
super();
this.consumeContext('umbExtensionRegistry', (extensionRegistry: UmbExtensionRegistry) => {
this._extensionRegistry = extensionRegistry;
this._useEditorViews();
this._useEditors();
});
}
connectedCallback(): void {
super.connectedCallback();
/* TODO: find a way to construct absolute urls */
this._routerFolder = window.location.pathname.split('/view')[0];
}
private _useEditors() {
if (!this._extensionRegistry) return;
private _useEditorViews() {
this._editorViewsSubscription?.unsubscribe();
this._editorViewsSubscription = this._extensionRegistry
?.extensionsOfType('editorView')
.pipe(
map((extensions) =>
extensions
.filter((extension) => extension.meta.editors.includes(this.alias))
.sort((a, b) => b.meta.weight - a.meta.weight)
)
)
.subscribe((editorViews) => {
this._editorViews = editorViews;
this._createRoutes();
this._extensionRegistry
.extensionsOfType('editor')
.pipe(map((editors) => editors.find((editor) => editor.meta.entityType === this.entityType)))
.subscribe((editor) => {
this._createElement(editor);
});
}
private async _createRoutes() {
if (this._editorViews.length > 0) {
this._routes = [];
private async _createElement(editor?: ManifestEditor) {
// TODO: implement fallback editor
const fallbackEditor = document.createElement('div');
fallbackEditor.innerHTML = '<p>No editor found</p>';
this._routes = this._editorViews.map((view) => {
return {
path: `view/${view.meta.pathname}`,
component: () => document.createElement(view.elementName || 'div'),
setup: (element: HTMLElement, info: IRoutingInfo) => {
this._currentView = info.match.route.path;
},
};
});
this._routes.push({
path: '**',
redirectTo: `view/${this._editorViews?.[0].meta.pathname}`,
});
this.requestUpdate();
await this.updateComplete;
this._forceRouteRender();
if (!editor) {
this._element = fallbackEditor;
return;
}
}
// TODO: Figure out why this has been necessary for this case. Come up with another case
private _forceRouteRender() {
const routerSlotEl = this.shadowRoot?.querySelector('router-slot') as RouterSlot;
if (routerSlotEl) {
routerSlotEl.render();
try {
this._element = (await createExtensionElement(editor)) as any;
this._element.entityKey = this.entityKey;
} catch (error) {
this._element = fallbackEditor;
}
}
private _renderViews() {
return html`
${this._editorViews?.length > 0
? html`
<uui-tab-group slot="views">
${this._editorViews.map(
(view: ManifestEditorView) => html`
<uui-tab
.label="${view.name}"
href="${this._routerFolder}/view/${view.meta.pathname}"
?active="${this._currentView.includes(view.meta.pathname)}">
<uui-icon slot="icon" name="${view.meta.icon}"></uui-icon>
${view.name}
</uui-tab>
`
)}
</uui-tab-group>
`
: nothing}
`;
}
render() {
return html`
<umb-editor-layout>
<div id="header" slot="header">
<slot id="icon" name="icon"></slot>
<slot id="name" name="name"></slot>
${this._renderViews()}
</div>
<router-slot .routes="${this._routes}"></router-slot>
<slot></slot>
<div id="footer" slot="footer">
<slot name="footer"></slot>
<slot id="actions" name="actions"></slot>
</div>
</umb-editor-layout>
`;
return html`${this._element}`;
}
}
export default UmbEditorEntityElement;
declare global {
interface HTMLElementTagNameMap {
'umb-editor-entity': UmbEditorEntity;
'umb-editor-entity': UmbEditorEntityElement;
}
}

View File

@@ -9,7 +9,7 @@ import { NodeEntity } from '../../../../mocks/data/node.data';
import type { UmbNotificationService } from '../../../../core/services/notification';
import { UmbNodeContext } from './node.context';
import '../../shared/editor-entity/editor-entity.element';
import '../../shared/editor-entity-layout/editor-entity-layout.element';
// Lazy load
// TODO: Make this dynamic, use load-extensions method to loop over extensions for this node.
@@ -172,7 +172,7 @@ export class UmbEditorNodeElement extends UmbContextProviderMixin(UmbContextCons
render() {
return html`
<umb-editor-entity alias=${this.alias}>
<umb-editor-entity-layout alias=${this.alias}>
<div slot="name">
<uui-input .value=${this._node?.name} @input="${this._handleInput}">
<!-- Implement Variant Selector -->
@@ -215,7 +215,7 @@ export class UmbEditorNodeElement extends UmbContextProviderMixin(UmbContextCons
color="positive"
label="Save and publish"></uui-button>
</div>
</umb-editor-entity>
</umb-editor-entity-layout>
`;
}
}

View File

@@ -3,11 +3,13 @@ import { css, html, LitElement } from 'lit';
import { customElement, state } from 'lit/decorators.js';
import { Subscription, map, switchMap, EMPTY, of } from 'rxjs';
import { UmbContextConsumerMixin } from '../../../core/context';
import { createExtensionElement, UmbExtensionRegistry } from '../../../core/extension';
import { UmbExtensionRegistry } from '../../../core/extension';
import { UmbSectionContext } from '../section.context';
import type { ManifestTree, ManifestEditor } from '../../../core/models';
import '../shared/section-trees.element.ts';
import { UmbEditorElement } from '../../editors/shared/editor-entity/editor-entity.element';
import { UmbEntityStore } from '../../../core/stores/entity.store';
@customElement('umb-section')
export class UmbSectionElement extends UmbContextConsumerMixin(LitElement) {
@@ -31,6 +33,8 @@ export class UmbSectionElement extends UmbContextConsumerMixin(LitElement) {
private _editors?: Array<ManifestEditor>;
private _editorsSubscription?: Subscription;
private _entityStore?: UmbEntityStore;
private _sectionContext?: UmbSectionContext;
private _extensionRegistry?: UmbExtensionRegistry;
private _treesSubscription?: Subscription;
@@ -40,24 +44,24 @@ export class UmbSectionElement extends UmbContextConsumerMixin(LitElement) {
// TODO: wait for more contexts
this.consumeContext('umbExtensionRegistry', (extensionsRegistry: UmbExtensionRegistry) => {
this.consumeContext('umbSectionContext', (sectionContext: UmbSectionContext) => {
this._extensionRegistry = extensionsRegistry;
this._sectionContext = sectionContext;
this._useEditors();
this._useTrees();
});
this._extensionRegistry = extensionsRegistry;
this._useTrees();
});
}
private _useEditors() {
this._editorsSubscription?.unsubscribe();
this.consumeContext('umbSectionContext', (sectionContext: UmbSectionContext) => {
this._sectionContext = sectionContext;
this._useTrees();
});
this._extensionRegistry?.extensionsOfType('editor').subscribe((editors) => {
this._editors = editors;
this.consumeContext('umbEntityStore', (entityStore: UmbEntityStore) => {
this._entityStore = entityStore;
this._useTrees();
});
}
private _useTrees() {
if (!this._sectionContext || !this._extensionRegistry || !this._entityStore) return;
this._treesSubscription?.unsubscribe();
this._treesSubscription = this._sectionContext?.data
@@ -87,20 +91,12 @@ export class UmbSectionElement extends UmbContextConsumerMixin(LitElement) {
private _createRoutes() {
const treeRoutes =
this._trees?.map((tree) => {
// TODO: make this code respond to updates in editor extensions
const editor = this._editors?.find((editor) => editor.alias === tree.meta.editor);
// TODO: implement fallback editor
const fallbackEditor = document.createElement('div');
fallbackEditor.innerHTML = '<p>No editor found</p>';
return {
path: `${tree.meta.pathname}/:id`,
component: () => (editor ? createExtensionElement(editor) : fallbackEditor),
async setup(component: any, info: any) {
// TODO: temp hack - we need to make sure it's the component and not a promise
const hello = await component;
hello.entityId = parseInt(info.match.params.id);
hello.entityKey = info.match.params.id;
path: `:entityType/:key`,
component: () => import('../../editors/shared/editor-entity/editor-entity.element'),
setup: (component: UmbEditorElement, info: any) => {
component.entityKey = info.match.params.key;
component.entityType = info.match.params.entityType;
},
};
}) ?? [];

View File

@@ -18,7 +18,7 @@ export class UmbTreeContentContext implements UmbTreeContext {
key: '485d49ef-a4aa-46ac-843f-4256fe167347',
name: 'Content',
hasChildren: true,
type: 'node',
type: 'document',
icon: 'favorite',
parentKey: '',
};

View File

@@ -18,7 +18,7 @@ export class UmbTreeMemberGroupsContext implements UmbTreeContext {
key: 'd46d144e-33d8-41e3-bf7a-545287e16e3c',
name: 'Member Groups',
hasChildren: true,
type: 'member-group',
type: 'memberGroup',
icon: 'favorite',
parentKey: '',
};

View File

@@ -5,56 +5,45 @@ import { UmbContextConsumerMixin } from '../../../core/context';
import { UmbTreeContext } from '../tree.context';
import { UUIMenuItemEvent } from '@umbraco-ui/uui';
import { UmbSectionContext } from '../../sections/section.context';
import { map, Subscription } from 'rxjs';
import { UmbEntityStore } from '../../../core/stores/entity.store';
import { Subscription } from 'rxjs';
import { Entity } from '../../../mocks/data/entity.data';
@customElement('umb-tree-item')
export class UmbTreeItem extends UmbContextConsumerMixin(LitElement) {
static styles = [UUITextStyles, css``];
@property({ type: Boolean })
hasChildren = false;
@property({ type: Number })
itemId = -1;
@property()
itemKey = '';
@property()
itemType = '';
@property({ type: String })
label = '';
@property({ type: String })
href = '';
@property({ type: Boolean })
hasChildren = false;
@state()
childItems: any[] = [];
private _childItems: Entity[] = [];
@state()
private _href = '';
@state()
private _loading = false;
@state()
private _pathName? = '';
@state()
private _sectionPathname?: string;
private _treeContext?: UmbTreeContext;
private _sectionContext?: UmbSectionContext;
private _sectionSubscription?: Subscription;
private _entitySubscription?: Subscription;
@state()
private _itemName = '';
private _childrenSubscription?: Subscription;
constructor() {
super();
this.consumeContext('umbTreeContext', (treeContext: UmbTreeContext) => {
this._treeContext = treeContext;
this._pathName = this._treeContext?.tree?.meta?.pathname;
});
this.consumeContext('umbSectionContext', (sectionContext: UmbSectionContext) => {
@@ -67,25 +56,26 @@ export class UmbTreeItem extends UmbContextConsumerMixin(LitElement) {
this._sectionSubscription?.unsubscribe();
this._sectionSubscription = this._sectionContext?.data.subscribe((section) => {
this._sectionPathname = section.meta.pathname;
this._href = this._constructPath(section.meta.pathname, this.itemType, this.itemKey);
});
}
// TODO: how do we handle this?
private _constructPath(key: string) {
return `/section/${this._sectionPathname}/${this._pathName}/${key}`;
private _constructPath(sectionPathname: string, type: string, key: string) {
return `/section/${sectionPathname}/${type}/${key}`;
}
private _onShowChildren(event: UUIMenuItemEvent) {
event.stopPropagation();
if (this.childItems.length > 0) return;
if (this._childItems.length > 0) return;
this._loading = true;
this._treeContext?.fetchChildren(this.itemKey).subscribe((items) => {
if (items?.length === 0) return;
this._childrenSubscription?.unsubscribe();
this.childItems = items;
this._childrenSubscription = this._treeContext?.fetchChildren(this.itemKey).subscribe((items) => {
if (items?.length === 0) return;
this._childItems = items;
this._loading = false;
});
}
@@ -93,15 +83,16 @@ export class UmbTreeItem extends UmbContextConsumerMixin(LitElement) {
disconnectedCallback(): void {
super.disconnectedCallback();
this._sectionSubscription?.unsubscribe();
this._childrenSubscription?.unsubscribe();
}
private _renderChildItems() {
return this.childItems.map((item) => {
return this._childItems.map((item) => {
return html`<umb-tree-item
.label=${item.name}
.hasChildren=${item.hasChildren}
.itemKey=${item.key}
href="${this._constructPath(item.key)}">
.itemType=${item.type}>
</umb-tree-item>`;
});
}
@@ -113,7 +104,7 @@ export class UmbTreeItem extends UmbContextConsumerMixin(LitElement) {
.loading=${this._loading}
.hasChildren=${this.hasChildren}
label="${this.label}"
href="${this._constructPath(this.itemKey)}">
href="${this._href}">
${this._renderChildItems()}
</uui-menu-item>
`;

View File

@@ -14,6 +14,9 @@ export class UmbTreeNavigator extends UmbContextConsumerMixin(LitElement) {
@state()
private _entityKey = '';
@state()
private _entityType = '';
@state()
private _label = '';
@@ -40,6 +43,7 @@ export class UmbTreeNavigator extends UmbContextConsumerMixin(LitElement) {
this._loading = false;
this._entityKey = items[0].key;
this._entityType = items[0].type;
this._label = items[0].name;
this._hasChildren = items[0].hasChildren;
});
@@ -54,6 +58,7 @@ export class UmbTreeNavigator extends UmbContextConsumerMixin(LitElement) {
render() {
return html`<umb-tree-item
.itemKey=${this._entityKey}
.itemType=${this._entityType}
.label=${this._label}
?hasChildren=${this._hasChildren}
.loading=${this._loading}></umb-tree-item> `;

View File

@@ -89,7 +89,7 @@ export class UmbModalLayoutContentPickerElement extends UmbModalLayoutElement<Um
render() {
return html`
<!-- TODO: maybe we need a layout component between umb-editor-layout and umb-editor-entity? -->
<umb-editor-entity>
<umb-editor-entity-layout>
<h3 slot="name">Select content</h3>
<uui-box>
<uui-input></uui-input>
@@ -110,7 +110,7 @@ export class UmbModalLayoutContentPickerElement extends UmbModalLayoutElement<Um
<uui-button label="Close" @click=${this._close}></uui-button>
<uui-button label="Submit" look="primary" color="positive" @click=${this._submit}></uui-button>
</div>
</umb-editor-entity>
</umb-editor-entity-layout>
`;
}
}

View File

@@ -248,7 +248,6 @@ export const internalManifests: Array<ManifestTypes & { loader: () => Promise<ob
loader: () => import('./backoffice/tree/data-types/tree-data-types.element'),
meta: {
pathname: 'data-types',
editor: 'Umb.Editor.DataType',
label: 'Data Types',
weight: 1,
sections: ['Umb.Section.Settings'],
@@ -262,7 +261,6 @@ export const internalManifests: Array<ManifestTypes & { loader: () => Promise<ob
loader: () => import('./backoffice/tree/document-types/tree-document-types.element'),
meta: {
pathname: 'document-types',
editor: 'Umb.Editor.DocumentType',
label: 'Document Types',
weight: 2,
sections: ['Umb.Section.Settings'],
@@ -286,7 +284,6 @@ export const internalManifests: Array<ManifestTypes & { loader: () => Promise<ob
name: 'Members Tree',
loader: () => import('./backoffice/tree/members/tree-members.element'),
meta: {
editor: 'Umb.Editor.Member',
pathname: 'members',
label: 'Members',
weight: 0,
@@ -299,7 +296,6 @@ export const internalManifests: Array<ManifestTypes & { loader: () => Promise<ob
name: 'Members Groups Tree',
loader: () => import('./backoffice/tree/member-groups/tree-member-groups.element'),
meta: {
editor: 'Umb.Editor.MemberGroup',
pathname: 'member-groups',
label: 'Member Groups',
weight: 1,
@@ -312,7 +308,6 @@ export const internalManifests: Array<ManifestTypes & { loader: () => Promise<ob
name: 'Extensions Tree',
loader: () => import('./backoffice/tree/extensions/tree-extensions.element'),
meta: {
editor: 'Umb.Editor.Extensions',
pathname: 'extensions',
label: 'Extensions',
weight: 3,
@@ -325,7 +320,6 @@ export const internalManifests: Array<ManifestTypes & { loader: () => Promise<ob
name: 'Media Tree',
loader: () => import('./backoffice/tree/media/tree-media.element'),
meta: {
editor: 'Umb.Editor.Media',
pathname: 'media',
label: 'Media',
weight: 100,
@@ -338,7 +332,6 @@ export const internalManifests: Array<ManifestTypes & { loader: () => Promise<ob
name: 'Content Tree',
loader: () => import('./backoffice/tree/content/tree-content.element'),
meta: {
editor: 'Umb.Editor.Content',
pathname: 'content',
label: 'Content',
weight: 100,
@@ -350,42 +343,63 @@ export const internalManifests: Array<ManifestTypes & { loader: () => Promise<ob
alias: 'Umb.Editor.Member',
name: 'Member Editor',
loader: () => import('./backoffice/editors/member/editor-member.element'),
meta: {
entityType: 'member',
},
},
{
type: 'editor',
alias: 'Umb.Editor.MemberGroup',
name: 'Member Group Editor',
loader: () => import('./backoffice/editors/member-group/editor-member-group.element'),
meta: {
entityType: 'memberGroup',
},
},
{
type: 'editor',
alias: 'Umb.Editor.DataType',
name: 'Data Type Editor',
loader: () => import('./backoffice/editors/data-type/editor-data-type.element'),
meta: {
entityType: 'dataType',
},
},
{
type: 'editor',
alias: 'Umb.Editor.DocumentType',
name: 'Document Type Editor',
loader: () => import('./backoffice/editors/document-type/editor-document-type.element'),
meta: {
entityType: 'documentType',
},
},
{
type: 'editor',
alias: 'Umb.Editor.Extensions',
name: 'Extensions Editor',
loader: () => import('./backoffice/editors/extensions/editor-extensions.element'),
meta: {
entityType: 'extensions',
},
},
{
type: 'editor',
alias: 'Umb.Editor.Media',
name: 'Media Editor',
loader: () => import('./backoffice/editors/media/editor-media.element'),
meta: {
entityType: 'media',
},
},
{
type: 'editor',
alias: 'Umb.Editor.Content',
name: 'Content Editor',
loader: () => import('./backoffice/editors/content/editor-content.element'),
meta: {
entityType: 'document',
},
},
{
type: 'entityAction',

View File

@@ -49,13 +49,16 @@ export interface MetaSection {
}
export interface MetaTree {
editor: string;
pathname: string;
label: string;
weight: number;
sections: Array<string>;
}
export interface MetaEditor {
entityType: string;
}
export interface MetaEntityAction {
label: string;
icon: string;
@@ -110,6 +113,7 @@ export interface IManifestTree extends IManifestElement {
export interface IManifestEditor extends IManifestElement {
type: 'editor';
meta: MetaEditor;
}
export interface IManifestEntityAction extends IManifestElement {