From c63eabb03e4c894f233e902732a0e55159cd1aeb Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Thu, 23 Mar 2023 13:00:06 +0100 Subject: [PATCH] add entity tree item kind --- .../documents/documents/tree/manifests.ts | 2 +- .../backoffice/media/media/tree/manifests.ts | 14 +++++- .../src/backoffice/shared/components/index.ts | 1 + .../entity-tree-item.context.ts | 10 ++++ .../entity-tree-item.element.ts | 41 ++++++++++++++++ .../tree-item-base/tree-item-base.context.ts | 49 +++++++++++-------- .../tree-item-base/tree-item-base.element.ts | 4 +- 7 files changed, 95 insertions(+), 26 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/backoffice/shared/components/tree/entity-tree-item/entity-tree-item.context.ts create mode 100644 src/Umbraco.Web.UI.Client/src/backoffice/shared/components/tree/entity-tree-item/entity-tree-item.element.ts diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/tree/manifests.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/tree/manifests.ts index ee92e43e8d..f3a852ed7d 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/tree/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/tree/manifests.ts @@ -14,9 +14,9 @@ const tree: ManifestTree = { const treeItem: ManifestTreeItem = { type: 'treeItem', + kind: 'entity', alias: 'Umb.TreeItem.Document', name: 'Document Tree Item', - loader: () => import('./tree-item/document-tree-item.element'), conditions: { entityType: 'document', }, diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/media/media/tree/manifests.ts b/src/Umbraco.Web.UI.Client/src/backoffice/media/media/tree/manifests.ts index 6f40b6d6ee..b3aa588fdf 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/media/media/tree/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/media/media/tree/manifests.ts @@ -1,5 +1,5 @@ import { UmbMediaRepository } from '../repository/media.repository'; -import type { ManifestTree } from '@umbraco-cms/backoffice/extensions-registry'; +import type { ManifestTree, ManifestTreeItem } from '@umbraco-cms/backoffice/extensions-registry'; const treeAlias = 'Umb.Tree.Media'; @@ -12,4 +12,14 @@ const tree: ManifestTree = { }, }; -export const manifests = [tree]; +const treeItem: ManifestTreeItem = { + type: 'treeItem', + kind: 'entity', + alias: 'Umb.TreeItem.Media', + name: 'Media Tree Item', + conditions: { + entityType: 'media', + }, +}; + +export const manifests = [tree, treeItem]; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/index.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/index.ts index 6bdba312a4..d3ce0e6ae5 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/index.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/index.ts @@ -34,6 +34,7 @@ import './section/section-sidebar/section-sidebar.element'; import './section/section.element'; import './table/table.element'; import './tree/tree.element'; +import './tree/entity-tree-item/entity-tree-item.element'; import './tree/tree-menu-item/tree-menu-item.element'; import './variantable-property/variantable-property.element'; import './workspace/workspace-action-menu/workspace-action-menu.element'; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/tree/entity-tree-item/entity-tree-item.context.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/tree/entity-tree-item/entity-tree-item.context.ts new file mode 100644 index 0000000000..934cd4b874 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/tree/entity-tree-item/entity-tree-item.context.ts @@ -0,0 +1,10 @@ +import { UmbTreeItemContextBase } from '../tree-item-base/tree-item-base.context'; +import { UmbControllerHostInterface } from '@umbraco-cms/backoffice/controller'; +import { EntityTreeItemResponseModel } from '@umbraco-cms/backoffice/backend-api'; + +// TODO get unique method from an entity repository static method +export class UmbEntityTreeItemContext extends UmbTreeItemContextBase { + constructor(host: UmbControllerHostInterface, treeItem: EntityTreeItemResponseModel) { + super(host, treeItem, (x: EntityTreeItemResponseModel) => x.key); + } +} diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/tree/entity-tree-item/entity-tree-item.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/tree/entity-tree-item/entity-tree-item.element.ts new file mode 100644 index 0000000000..7540c8156c --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/tree/entity-tree-item/entity-tree-item.element.ts @@ -0,0 +1,41 @@ +import { css, html, nothing } from 'lit'; +import { UUITextStyles } from '@umbraco-ui/uui-css/lib'; +import { customElement, property } from 'lit/decorators.js'; +import { UmbEntityTreeItemContext } from './entity-tree-item.context'; +import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; +import { EntityTreeItemResponseModel } from '@umbraco-cms/backoffice/backend-api'; +import { ManifestKind } from '@umbraco-cms/backoffice/extensions-registry'; +import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extensions-api'; + +// TODO: Move to separate file: +const manifest: ManifestKind = { + type: 'kind', + alias: 'Umb.Kind.EntityTreeItem', + matchKind: 'entity', + matchType: 'treeItem', + manifest: { + type: 'treeItem', + elementName: 'umb-entity-tree-item', + }, +}; +umbExtensionsRegistry.register(manifest); + +@customElement('umb-entity-tree-item') +export class UmbEntityTreeItemElement extends UmbLitElement { + static styles = [UUITextStyles, css``]; + + @property({ type: Object, attribute: false }) + item?: EntityTreeItemResponseModel; + + render() { + if (!this.item) return nothing; + new UmbEntityTreeItemContext(this, this.item); + return html``; + } +} + +declare global { + interface HTMLElementTagNameMap { + 'umb-entity-tree-item': UmbEntityTreeItemElement; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/tree/tree-item-base/tree-item-base.context.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/tree/tree-item-base/tree-item-base.context.ts index b3560cbbc7..298fb67aa9 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/tree/tree-item-base/tree-item-base.context.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/tree/tree-item-base/tree-item-base.context.ts @@ -15,12 +15,18 @@ import { import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extensions-api'; import type { TreeItemPresentationModel } from '@umbraco-cms/backoffice/backend-api'; +// add type for unique function +export type UmbTreeItemUniqueFunction = (x: T) => string | null | undefined; + export class UmbTreeItemContextBase { public host: UmbControllerHostInterface; public treeItem: T; public unique: string; public type: string; + #hasChildren = new BooleanState(false); + hasChildren = this.#hasChildren.asObservable(); + #isLoading = new BooleanState(false); isLoading = this.#isLoading.asObservable(); @@ -43,36 +49,22 @@ export class UmbTreeItemContextBase string | null | undefined) { + constructor(host: UmbControllerHostInterface, treeItem: T, getUniqueFunction: UmbTreeItemUniqueFunction) { this.host = host; this.treeItem = treeItem; - const unique = getUnique(this.treeItem); + const unique = getUniqueFunction(this.treeItem); if (!unique) throw new Error('Could not create tree item context, unique key is missing'); this.unique = unique; if (!this.treeItem.type) throw new Error('Could not create tree item context, tree item type is missing'); - this.type = this.treeItem.type; - new UmbContextConsumerController(host, UMB_SECTION_CONTEXT_TOKEN, (instance) => { - this.#sectionContext = instance; - this.#observeSectionPath(); - }); - - new UmbContextConsumerController(host, UMB_SECTION_SIDEBAR_CONTEXT_TOKEN, (instance) => { - this.#sectionSidebarContext = instance; - }); - - new UmbContextConsumerController(host, 'umbTreeContext', (treeContext: UmbTreeContextBase) => { - this.treeContext = treeContext; - this.#observeIsSelectable(); - this.#observeIsSelected(); - }); - - new UmbContextProviderController(host, UMB_TREE_ITEM_CONTEXT_TOKEN, this); - this.#observeTreeItemActions(); + this.#hasChildren.next(this.treeItem.hasChildren || false); + + this.#consumeContexts(); + new UmbContextProviderController(host, UMB_TREE_ITEM_CONTEXT_TOKEN, this); } public async requestChildren() { @@ -95,6 +87,23 @@ export class UmbTreeItemContextBase { + this.#sectionContext = instance; + this.#observeSectionPath(); + }); + + new UmbContextConsumerController(this.host, UMB_SECTION_SIDEBAR_CONTEXT_TOKEN, (instance) => { + this.#sectionSidebarContext = instance; + }); + + new UmbContextConsumerController(this.host, 'umbTreeContext', (treeContext: UmbTreeContextBase) => { + this.treeContext = treeContext; + this.#observeIsSelectable(); + this.#observeIsSelected(); + }); + } + #observeIsSelectable() { if (!this.treeContext) return; new UmbObserverController(this.host, this.treeContext.selectable, (value) => this.#isSelectable.next(value)); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/tree/tree-item-base/tree-item-base.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/tree/tree-item-base/tree-item-base.element.ts index 601bb816a4..54f8f9d397 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/tree/tree-item-base/tree-item-base.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/tree/tree-item-base/tree-item-base.element.ts @@ -22,9 +22,6 @@ export class UmbTreeItemBaseElement extends UmbLitElement { this.requestUpdate('item', oldVal); } - @property({ type: Boolean, attribute: 'has-children' }) - hasChildren = false; - @state() private _childItems?: EntityTreeItemResponseModel[]; @@ -52,6 +49,7 @@ export class UmbTreeItemBaseElement extends UmbLitElement { this.#treeItemContext = instance; if (!this.#treeItemContext) return; // TODO: investigate if we can make an observe decorator + this.observe(this.#treeItemContext.hasChildren, (value) => (this.hasChildren = value)); this.observe(this.#treeItemContext.isLoading, (value) => (this._isLoading = value)); this.observe(this.#treeItemContext.isSelectable, (value) => (this._isSelectable = value)); this.observe(this.#treeItemContext.isSelected, (value) => (this._isSelected = value));