From 6b6d384a445da6216bddc926269f2f524cd66680 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Tue, 30 Aug 2022 13:36:46 +0200 Subject: [PATCH] render editor based on entity type --- src/Umbraco.Web.UI.Client/schemas/api/api.yml | 15 +- .../schemas/generated-schema.ts | 6 +- .../data-type/editor-data-type.element.ts | 6 +- .../editor-document-type.element.ts | 6 +- .../extensions/editor-extensions.element.ts | 6 +- .../editor-member-group.element.ts | 6 +- .../editors/member/editor-member.element.ts | 4 +- .../editor-entity-layout.element.ts | 194 ++++++++++++++++++ .../editor-entity-layout.stories.ts} | 14 +- .../editor-entity/editor-entity.element.ts | 183 ++++------------- .../shared/node/editor-node.element.ts | 6 +- .../sections/shared/section.element.ts | 46 ++--- .../tree/content/tree-content.context.ts | 2 +- .../tree-member-groups.context.ts | 2 +- .../tree/shared/tree-item.element.ts | 57 +++-- .../tree/shared/tree-navigator.element.ts | 5 + .../modal-layout-content-picker.element.ts | 4 +- .../src/temp-internal-manifests.ts | 28 ++- .../temp-schema-generator/manifests.ts | 6 +- 19 files changed, 352 insertions(+), 244 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/backoffice/editors/shared/editor-entity-layout/editor-entity-layout.element.ts rename src/Umbraco.Web.UI.Client/src/backoffice/editors/shared/{editor-entity/editor-entity.stories.ts => editor-entity-layout/editor-entity-layout.stories.ts} (61%) diff --git a/src/Umbraco.Web.UI.Client/schemas/api/api.yml b/src/Umbraco.Web.UI.Client/schemas/api/api.yml index 1220584192..57f8e09168 100644 --- a/src/Umbraco.Web.UI.Client/schemas/api/api.yml +++ b/src/Umbraco.Web.UI.Client/schemas/api/api.yml @@ -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: diff --git a/src/Umbraco.Web.UI.Client/schemas/generated-schema.ts b/src/Umbraco.Web.UI.Client/schemas/generated-schema.ts index 7961eba2da..0e21eee54e 100644 --- a/src/Umbraco.Web.UI.Client/schemas/generated-schema.ts +++ b/src/Umbraco.Web.UI.Client/schemas/generated-schema.ts @@ -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: { diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/editors/data-type/editor-data-type.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/editors/data-type/editor-data-type.element.ts index f365349ad9..7b703c05f6 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/editors/data-type/editor-data-type.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/editors/data-type/editor-data-type.element.ts @@ -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` - +
@@ -135,7 +135,7 @@ export class UmbEditorDataTypeElement extends UmbContextProviderMixin(UmbContext label="Save" .state="${this._saveButtonState}">
-
+ ` : nothing} `; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/editors/document-type/editor-document-type.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/editors/document-type/editor-document-type.element.ts index 6068610efb..2093ab11b5 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/editors/document-type/editor-document-type.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/editors/document-type/editor-document-type.element.ts @@ -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` - +
Icon
@@ -144,7 +144,7 @@ export class UmbEditorDocumentTypeElement extends UmbContextProviderMixin(UmbCon label="Save" .state="${this._saveButtonState}">
-
+ `; } } diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/editors/extensions/editor-extensions.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/editors/extensions/editor-extensions.element.ts index 1f1950d6a3..a3b41a9161 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/editors/extensions/editor-extensions.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/editors/extensions/editor-extensions.element.ts @@ -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` - +

Extensions

@@ -75,7 +75,7 @@ export class UmbEditorExtensionsElement extends UmbContextConsumerMixin(LitEleme )} -
+ `; } } diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/editors/member-group/editor-member-group.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/editors/member-group/editor-member-group.element.ts index d235a8c921..5e721597bd 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/editors/member-group/editor-member-group.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/editors/member-group/editor-member-group.element.ts @@ -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` Member Group Editor `; + return html` + Member Group Editor + `; } } diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/editors/member/editor-member.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/editors/member/editor-member.element.ts index 3db0a73cf4..e71046b28a 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/editors/member/editor-member.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/editors/member/editor-member.element.ts @@ -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` Member Editor `; + return html` Member Editor `; } } diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/editors/shared/editor-entity-layout/editor-entity-layout.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/editors/shared/editor-entity-layout/editor-entity-layout.element.ts new file mode 100644 index 0000000000..0973719d8e --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/backoffice/editors/shared/editor-entity-layout/editor-entity-layout.element.ts @@ -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 = []; + + @state() + private _currentView = ''; + + @state() + private _routes: Array = []; + + 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` + + ${this._editorViews.map( + (view: ManifestEditorView) => html` + + + ${view.name} + + ` + )} + + ` + : nothing} + `; + } + + render() { + return html` + + + + + + + + + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + 'umb-editor-entity-layout': UmbEditorEntityLayout; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/editors/shared/editor-entity/editor-entity.stories.ts b/src/Umbraco.Web.UI.Client/src/backoffice/editors/shared/editor-entity-layout/editor-entity-layout.stories.ts similarity index 61% rename from src/Umbraco.Web.UI.Client/src/backoffice/editors/shared/editor-entity/editor-entity.stories.ts rename to src/Umbraco.Web.UI.Client/src/backoffice/editors/shared/editor-entity-layout/editor-entity-layout.stories.ts index 7c17cf1266..c06f125a0e 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/editors/shared/editor-entity/editor-entity.stories.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/editors/shared/editor-entity-layout/editor-entity-layout.stories.ts @@ -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 = () => html` +export const AAAOverview: Story = () => html`
Icon slot
Name slot
Footer slot
Actions slot
Main slot -
`; +`; AAAOverview.storyName = 'Overview'; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/editors/shared/editor-entity/editor-entity.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/editors/shared/editor-entity/editor-entity.element.ts index 8a2220e081..3742dfc0b7 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/editors/shared/editor-entity/editor-entity.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/editors/shared/editor-entity/editor-entity.element.ts @@ -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 = []; - - @state() - private _currentView = ''; - - @state() - private _routes: Array = []; + 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 = '

No editor found

'; - 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` - - ${this._editorViews.map( - (view: ManifestEditorView) => html` - - - ${view.name} - - ` - )} - - ` - : nothing} - `; - } - render() { - return html` - - - - - - - - - `; + return html`${this._element}`; } } +export default UmbEditorEntityElement; + declare global { interface HTMLElementTagNameMap { - 'umb-editor-entity': UmbEditorEntity; + 'umb-editor-entity': UmbEditorEntityElement; } } diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/editors/shared/node/editor-node.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/editors/shared/node/editor-node.element.ts index b29c7057ba..738adbda92 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/editors/shared/node/editor-node.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/editors/shared/node/editor-node.element.ts @@ -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` - +
@@ -215,7 +215,7 @@ export class UmbEditorNodeElement extends UmbContextProviderMixin(UmbContextCons color="positive" label="Save and publish">
-
+ `; } } diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/sections/shared/section.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/sections/shared/section.element.ts index b1924bd319..66506fdab4 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/sections/shared/section.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/sections/shared/section.element.ts @@ -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; 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 = '

No editor found

'; - 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; }, }; }) ?? []; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/tree/content/tree-content.context.ts b/src/Umbraco.Web.UI.Client/src/backoffice/tree/content/tree-content.context.ts index 4eeff7b481..f6c4502845 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/tree/content/tree-content.context.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/tree/content/tree-content.context.ts @@ -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: '', }; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/tree/member-groups/tree-member-groups.context.ts b/src/Umbraco.Web.UI.Client/src/backoffice/tree/member-groups/tree-member-groups.context.ts index 93347eb675..98761d2e29 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/tree/member-groups/tree-member-groups.context.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/tree/member-groups/tree-member-groups.context.ts @@ -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: '', }; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/tree/shared/tree-item.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/tree/shared/tree-item.element.ts index 12195fb92a..003c1e5f75 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/tree/shared/tree-item.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/tree/shared/tree-item.element.ts @@ -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` + .itemType=${item.type}> `; }); } @@ -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()} `; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/tree/shared/tree-navigator.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/tree/shared/tree-navigator.element.ts index 83713e0aa6..914f7d0f0c 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/tree/shared/tree-navigator.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/tree/shared/tree-navigator.element.ts @@ -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` `; diff --git a/src/Umbraco.Web.UI.Client/src/core/services/modal/layouts/content-picker/modal-layout-content-picker.element.ts b/src/Umbraco.Web.UI.Client/src/core/services/modal/layouts/content-picker/modal-layout-content-picker.element.ts index 1e671bbbc2..b60cd79dbe 100644 --- a/src/Umbraco.Web.UI.Client/src/core/services/modal/layouts/content-picker/modal-layout-content-picker.element.ts +++ b/src/Umbraco.Web.UI.Client/src/core/services/modal/layouts/content-picker/modal-layout-content-picker.element.ts @@ -89,7 +89,7 @@ export class UmbModalLayoutContentPickerElement extends UmbModalLayoutElement - +

Select content

@@ -110,7 +110,7 @@ export class UmbModalLayoutContentPickerElement extends UmbModalLayoutElement -
+ `; } } diff --git a/src/Umbraco.Web.UI.Client/src/temp-internal-manifests.ts b/src/Umbraco.Web.UI.Client/src/temp-internal-manifests.ts index 91d21af562..fd93ad093f 100644 --- a/src/Umbraco.Web.UI.Client/src/temp-internal-manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/temp-internal-manifests.ts @@ -248,7 +248,6 @@ export const internalManifests: Array Promise 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 Promise 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 Promise 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 Promise 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 Promise 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 Promise 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 Promise 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 Promise 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', diff --git a/src/Umbraco.Web.UI.Client/temp-schema-generator/manifests.ts b/src/Umbraco.Web.UI.Client/temp-schema-generator/manifests.ts index 28f9d5080c..6818e6361b 100644 --- a/src/Umbraco.Web.UI.Client/temp-schema-generator/manifests.ts +++ b/src/Umbraco.Web.UI.Client/temp-schema-generator/manifests.ts @@ -49,13 +49,16 @@ export interface MetaSection { } export interface MetaTree { - editor: string; pathname: string; label: string; weight: number; sections: Array; } +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 {