use extension initializer for tree item

This commit is contained in:
Mads Rasmussen
2024-02-27 13:47:15 +01:00
parent 94845e7504
commit 4d01fa5e4f
8 changed files with 84 additions and 41 deletions

View File

@@ -5,6 +5,7 @@ import { UmbReloadTreeItemChildrenRequestEntityActionEvent } from '../reload-tre
import { map } from '@umbraco-cms/backoffice/external/rxjs';
import { UMB_SECTION_CONTEXT, UMB_SECTION_SIDEBAR_CONTEXT } from '@umbraco-cms/backoffice/section';
import type { UmbSectionContext, UmbSectionSidebarContext } from '@umbraco-cms/backoffice/section';
import type { ManifestTreeItem } from '@umbraco-cms/backoffice/extension-registry';
import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry';
import { UmbBooleanState, UmbDeepState, UmbStringState } from '@umbraco-cms/backoffice/observable-api';
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
@@ -24,6 +25,8 @@ export abstract class UmbTreeItemContextBase<TreeItemType extends UmbTreeItemMod
public unique?: string | null;
public entityType?: string;
#manifest?: ManifestTreeItem;
#treeItem = new UmbDeepState<TreeItemType | undefined>(undefined);
treeItem = this.#treeItem.asObservable();
@@ -64,6 +67,25 @@ export abstract class UmbTreeItemContextBase<TreeItemType extends UmbTreeItemMod
this.#consumeContexts();
}
/**
* Sets the manifest
* @param {ManifestCollection} manifest
* @memberof UmbCollectionContext
*/
public setManifest(manifest: ManifestTreeItem | undefined) {
if (this.#manifest === manifest) return;
this.#manifest = manifest;
}
/**
* Returns the manifest.
* @return {ManifestCollection}
* @memberof UmbCollectionContext
*/
public getManifest() {
return this.#manifest;
}
public setTreeItem(treeItem: TreeItemType | undefined) {
if (!treeItem) {
this.#treeItem.setValue(undefined);

View File

@@ -1,16 +1,19 @@
import type { UmbTreeItemContext } from '../tree-item/index.js';
import type { UmbTreeItemModelBase } from '../types.js';
import { UMB_TREE_ITEM_CONTEXT } from './tree-item-context-base.js';
import { html, nothing, state, ifDefined, repeat } from '@umbraco-cms/backoffice/external/lit';
import { html, nothing, state, ifDefined, repeat, property } from '@umbraco-cms/backoffice/external/lit';
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
// eslint-disable-next-line local-rules/enforce-element-suffix-on-element-class-name
export abstract class UmbTreeItemElementBase extends UmbLitElement {
@state()
private _item?: UmbTreeItemModelBase;
export abstract class UmbTreeItemElementBase<TreeItemModelType extends UmbTreeItemModelBase> extends UmbLitElement {
@property({ type: Object, attribute: false })
item?: TreeItemModelType;
@state()
private _childItems?: UmbTreeItemModelBase[];
private _item?: TreeItemModelType;
@state()
private _childItems?: TreeItemModelType[];
@state()
private _href?: string;
@@ -33,7 +36,7 @@ export abstract class UmbTreeItemElementBase extends UmbLitElement {
@state()
private _iconSlotHasChildren = false;
#treeItemContext?: UmbTreeItemContext<UmbTreeItemModelBase>;
#treeItemContext?: UmbTreeItemContext<TreeItemModelType>;
constructor() {
super();
@@ -41,6 +44,9 @@ export abstract class UmbTreeItemElementBase extends UmbLitElement {
this.consumeContext(UMB_TREE_ITEM_CONTEXT, (instance) => {
this.#treeItemContext = instance;
if (!this.#treeItemContext) return;
this.#treeItemContext.setTreeItem(this.item);
// TODO: investigate if we can make an observe decorator
this.observe(this.#treeItemContext.treeItem, (value) => (this._item = value));
this.observe(this.#treeItemContext.hasChildren, (value) => (this._hasChildren = value));
@@ -85,7 +91,6 @@ export abstract class UmbTreeItemElementBase extends UmbLitElement {
// If we like to be able to open items in selectable context, then we might want to make it as a menu item action, so you have to click ... and chose an action called 'Edit'
render() {
return html`
HELLO HELLO
<uui-menu-item
@show-children=${this._onShowChildren}
@selected=${this._handleSelectedItem}
@@ -97,7 +102,7 @@ export abstract class UmbTreeItemElementBase extends UmbLitElement {
.hasChildren=${this._hasChildren}
label="${ifDefined(this._item?.name)}"
href="${ifDefined(this._isSelectableContext ? undefined : this._href)}">
${this.#renderIconContainer()} ${this.#renderLabel()} ${this.#renderActions()} ${this.#renderChildItems()}
${this.#renderIconContainer()} ${this.renderLabel()} ${this.#renderActions()} ${this.#renderChildItems()}
<slot></slot>
</uui-menu-item>
`;
@@ -134,7 +139,7 @@ export abstract class UmbTreeItemElementBase extends UmbLitElement {
return html`<uui-icon slot="icon" name="icon-circle-dotted"></uui-icon>`;
}
#renderLabel() {
renderLabel() {
return html`<slot name="label" slot="label"></slot>`;
}
@@ -154,9 +159,8 @@ export abstract class UmbTreeItemElementBase extends UmbLitElement {
${this._childItems
? repeat(
this._childItems,
// TODO: get unique here instead of name. we might be able to get it from the context
(item) => item.name,
(item) => html`<umb-tree-item-default .item=${item}></umb-tree-item-default>`,
(item, index) => item.name + '___' + index,
(item) => html`<umb-tree-item .entityType=${item.entityType} .props=${{ item }}></umb-tree-item>`,
)
: ''}
`;

View File

@@ -9,3 +9,5 @@ export class UmbDefaultTreeItemContext<
super(host, (x: UmbUniqueTreeItemModel) => x.unique);
}
}
export default UmbDefaultTreeItemContext;

View File

@@ -1,8 +1,11 @@
import { UmbTreeItemElementBase } from '../tree-item-base/index.js';
import type { UmbUniqueTreeItemModel } from '../types.js';
import { customElement } from '@umbraco-cms/backoffice/external/lit';
@customElement('umb-default-tree-item')
export class UmbDefaultTreeItemElement extends UmbTreeItemElementBase {}
export class UmbDefaultTreeItemElement extends UmbTreeItemElementBase<UmbUniqueTreeItemModel> {}
export default UmbDefaultTreeItemElement;
declare global {
interface HTMLElementTagNameMap {

View File

@@ -1,2 +1,2 @@
export * from './tree-item.context.interface.js';
export * from './tree-item-context.interface.js';
export * from './tree-item.element.js';

View File

@@ -1,32 +1,43 @@
import type { UmbTreeItemModelBase } from '../types.js';
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
import { css, html, nothing, customElement, property } from '@umbraco-cms/backoffice/external/lit';
import { customElement, property } from '@umbraco-cms/backoffice/external/lit';
import type { ManifestTreeItem } from '@umbraco-cms/backoffice/extension-registry';
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
import { UmbExtensionInitializerElementBase, umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry';
@customElement('umb-tree-item')
export class UmbTreeItemElement extends UmbLitElement {
@property({ type: Object, attribute: false })
item?: UmbTreeItemModelBase;
render() {
if (!this.item) return nothing;
return html`<umb-extension-slot
type="treeItem"
.filter=${(manifests: ManifestTreeItem) => manifests.meta.entityTypes.includes(this.item!.entityType)}
.props=${{
item: this.item,
}}></umb-extension-slot>`;
export class UmbTreeItemElement extends UmbExtensionInitializerElementBase<ManifestTreeItem> {
_entityType?: string;
@property({ type: String, reflect: true })
get entityType() {
return this._entityType;
}
set entityType(newVal) {
this._entityType = newVal;
this.#observeManifest();
}
static styles = [
UmbTextStyles,
css`
:host {
display: block;
}
`,
];
#observeManifest() {
if (!this._entityType) return;
this.observe(
umbExtensionsRegistry.byTypeAndFilter(this.getExtensionType(), (manifest: ManifestTreeItem) =>
manifest.meta.entityTypes.includes(this._entityType),
),
async (manifests) => {
if (!manifests) return;
// TODO: what should we do if there are multiple tree items for an entity type?
const manifest = manifests[0];
this.createApi(manifest);
this.createElement(manifest);
},
'umbObserveTreeManifest',
);
}
getExtensionType() {
return 'treeItem';
}
getDefaultElementName() {
return 'umb-default-tree-item';
}
}
declare global {

View File

@@ -121,7 +121,9 @@ export class UmbTreeElement extends UmbLitElement {
#renderTreeRoot() {
if (this.hideTreeRoot || this._treeRoot === undefined) return nothing;
return html` <umb-tree-item .item=${this._treeRoot}></umb-tree-item> `;
return html`
<umb-tree-item .entityType=${this._treeRoot.entityType} .props=${{ item: this._treeRoot }}></umb-tree-item>
`;
}
#renderRootItems() {
@@ -129,9 +131,8 @@ export class UmbTreeElement extends UmbLitElement {
return html`
${repeat(
this._items,
// TODO: use unique here:
(item, index) => item.name + '___' + index,
(item) => html`<umb-tree-item .item=${item}></umb-tree-item>`,
(item) => html`<umb-tree-item .entityType=${this._treeRoot.entityType} .props=${item}></umb-tree-item>`,
)}
`;
}