keep tree items in the context and observe from element

This commit is contained in:
Mads Rasmussen
2024-03-05 23:15:02 +01:00
parent a3549a6dd5
commit 5ea3a3e2e7
5 changed files with 34 additions and 85 deletions

View File

@@ -13,7 +13,7 @@ import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
import { UmbExtensionApiInitializer } from '@umbraco-cms/backoffice/extension-api';
import { UmbPaginationManager, UmbSelectionManager } from '@umbraco-cms/backoffice/utils';
import type { UmbEntityActionEvent } from '@umbraco-cms/backoffice/entity-action';
import { UmbObjectState } from '@umbraco-cms/backoffice/observable-api';
import { UmbArrayState, UmbObjectState } from '@umbraco-cms/backoffice/observable-api';
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
import { UmbChangeEvent } from '@umbraco-cms/backoffice/event';
@@ -24,6 +24,9 @@ export class UmbDefaultTreeContext<TreeItemType extends UmbTreeItemModelBase>
#treeRoot = new UmbObjectState<TreeItemType | undefined>(undefined);
treeRoot = this.#treeRoot.asObservable();
#rootItems = new UmbArrayState<TreeItemType>([], (x) => x.unique);
rootItems = this.#rootItems.asObservable();
public selectableFilter?: (item: TreeItemType) => boolean = () => true;
public filter?: (item: TreeItemType) => boolean = () => true;
public readonly selection = new UmbSelectionManager(this._host);
@@ -52,7 +55,7 @@ export class UmbDefaultTreeContext<TreeItemType extends UmbTreeItemModelBase>
// listen for page changes on the pagination manager
this.pagination.addEventListener(UmbChangeEvent.TYPE, this.#onPageChange);
this.requestTreeRoot();
this.loadTreeRoot();
}
// TODO: find a generic way to do this
@@ -91,35 +94,27 @@ export class UmbDefaultTreeContext<TreeItemType extends UmbTreeItemModelBase>
return this.#repository;
}
public async requestTreeRoot() {
public async loadTreeRoot() {
await this.#init;
const { data } = await this.#repository!.requestTreeRoot();
if (data) {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
//@ts-ignore
this.#treeRoot.setValue(data);
}
}
public async requestRootItems() {
public async loadRootItems() {
await this.#init;
const { data, error, asObservable } = await this.#repository!.requestRootTreeItems({
const { data } = await this.#repository!.requestRootTreeItems({
skip: this.#paging.skip,
take: this.#paging.take,
});
if (data) {
this.#rootItems.setValue(data.items);
this.pagination.setTotalItems(data.total);
}
return { data, error, asObservable };
}
public async rootItems() {
await this.#init;
return this.#repository!.rootTreeItems();
}
#consumeContexts() {
@@ -139,7 +134,7 @@ export class UmbDefaultTreeContext<TreeItemType extends UmbTreeItemModelBase>
#onPageChange = (event: UmbChangeEvent) => {
const target = event.target as UmbPaginationManager;
this.#paging.skip = target.getSkip();
this.requestRootItems();
this.loadRootItems();
};
#observeRepository(repositoryAlias?: string) {
@@ -161,11 +156,9 @@ export class UmbDefaultTreeContext<TreeItemType extends UmbTreeItemModelBase>
// Only handle root request here. Items are handled by the tree item context
const treeRoot = this.#treeRoot.getValue();
if (treeRoot === undefined) return;
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
if (event.getUnique() !== treeRoot.unique) return;
if (event.getEntityType() !== treeRoot.entityType) return;
this.requestRootItems();
this.loadRootItems();
};
destroy(): void {

View File

@@ -26,7 +26,7 @@ export class UmbDefaultTreeElement extends UmbLitElement {
filter: (item: UmbTreeItemModelBase) => boolean = () => true;
@state()
private _items: UmbTreeItemModelBase[] = [];
private _rootItems: UmbTreeItemModelBase[] = [];
@state()
private _treeRoot?: UmbTreeItemModelBase;
@@ -47,11 +47,10 @@ export class UmbDefaultTreeElement extends UmbLitElement {
// TODO: Notice this can be retrieve via a api property. [NL]
this.consumeContext(UMB_DEFAULT_TREE_CONTEXT, (instance) => {
this.#treeContext = instance;
this.observe(this.#treeContext.treeRoot, (treeRoot) => (this._treeRoot = treeRoot));
this.observe(this.#treeContext.rootItems, (rootItems) => (this._rootItems = rootItems));
this.observe(this.#treeContext.pagination.currentPage, (value) => (this._currentPage = value));
this.observe(this.#treeContext.pagination.totalPages, (value) => (this._totalPages = value));
this.#observeTreeRoot();
}).asPromise(),
]);
}
@@ -70,7 +69,8 @@ export class UmbDefaultTreeElement extends UmbLitElement {
if (_changedProperties.has('hideTreeRoot')) {
if (this.hideTreeRoot === true) {
this.#observeRootItems();
await this.#init;
this.#treeContext!.loadRootItems();
}
}
@@ -83,36 +83,6 @@ export class UmbDefaultTreeElement extends UmbLitElement {
}
}
#observeTreeRoot() {
if (!this.#treeContext) return;
this.observe(
this.#treeContext.treeRoot,
(treeRoot) => {
this._treeRoot = treeRoot;
},
'umbTreeRootObserver',
);
}
async #observeRootItems() {
await this.#init;
if (!this.#treeContext?.requestRootItems) throw new Error('Tree does not support root items');
const { asObservable } = await this.#treeContext.requestRootItems();
if (asObservable) {
this.observe(
asObservable(),
(rootItems) => {
const oldValue = this._items;
this._items = rootItems;
this.requestUpdate('_items', oldValue);
},
'umbRootItemsObserver',
);
}
}
getSelection() {
return this.#treeContext?.selection.getSelection();
}
@@ -129,10 +99,10 @@ export class UmbDefaultTreeElement extends UmbLitElement {
}
#renderRootItems() {
if (this._items?.length === 0) return nothing;
if (this._rootItems?.length === 0) return nothing;
return html`
${repeat(
this._items,
this._rootItems,
(item, index) => item.name + '___' + index,
(item) => html`<umb-tree-item .entityType=${item.entityType} .props=${{ item }}></umb-tree-item>`,
)}

View File

@@ -7,7 +7,7 @@ import { UMB_SECTION_CONTEXT, UMB_SECTION_SIDEBAR_CONTEXT } from '@umbraco-cms/b
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 { UmbArrayState, UmbBooleanState, UmbDeepState, UmbStringState } from '@umbraco-cms/backoffice/observable-api';
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
import { UmbContextBase } from '@umbraco-cms/backoffice/class-api';
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
@@ -33,6 +33,9 @@ export abstract class UmbTreeItemContextBase<TreeItemType extends UmbTreeItemMod
#treeItem = new UmbDeepState<TreeItemType | undefined>(undefined);
treeItem = this.#treeItem.asObservable();
#childItems = new UmbArrayState<TreeItemType>([], (x) => x.unique);
childItems = this.#childItems.asObservable();
#hasChildren = new UmbBooleanState(false);
hasChildren = this.#hasChildren.asObservable();
#hasChildrenInitValueFlag = false;
@@ -129,25 +132,26 @@ export abstract class UmbTreeItemContextBase<TreeItemType extends UmbTreeItemMod
this.#observeHasChildren();
}
public async requestChildren() {
public async loadChildren() {
if (this.unique === undefined) throw new Error('Could not request children, unique key is missing');
// TODO: wait for tree context to be ready
const repository = this.treeContext?.getRepository();
if (!repository) throw new Error('Could not request children, repository is missing');
this.#isLoading.setValue(true);
const { data, error, asObservable } = await repository.requestTreeItemsOf({
const { data } = await repository.requestTreeItemsOf({
parentUnique: this.unique,
skip: this.#paging.skip,
take: this.#paging.take,
});
if (data) {
this.#childItems.setValue(data.items);
this.pagination.setTotalItems(data.total);
}
this.#isLoading.setValue(false);
return { data, error, asObservable };
}
public toggleContextMenu() {
@@ -285,13 +289,13 @@ export abstract class UmbTreeItemContextBase<TreeItemType extends UmbTreeItemMod
if (!this.unique) return;
if (event.getUnique() !== this.unique) return;
if (event.getEntityType() !== this.entityType) return;
this.requestChildren();
this.loadChildren();
};
#onPageChange = (event: UmbChangeEvent) => {
const target = event.target as UmbPaginationManager;
this.#paging.skip = target.getSkip();
this.requestChildren();
this.loadChildren();
};
// TODO: use router context

View File

@@ -60,6 +60,7 @@ export abstract class UmbTreeItemElementBase<TreeItemModelType extends UmbTreeIt
// TODO: investigate if we can make an observe decorator
this.observe(this.#treeItemContext.treeItem, (value) => (this._item = value));
this.observe(this.#treeItemContext.childItems, (value) => (this._childItems = value));
this.observe(this.#treeItemContext.hasChildren, (value) => (this._hasChildren = value));
this.observe(this.#treeItemContext.isLoading, (value) => (this._isLoading = value));
this.observe(this.#treeItemContext.isSelectableContext, (value) => (this._isSelectableContext = value));
@@ -89,21 +90,7 @@ export abstract class UmbTreeItemElementBase<TreeItemModelType extends UmbTreeIt
// TODO: do we want to catch and emit a backoffice event here?
private _onShowChildren() {
if (this._childItems && this._childItems.length > 0) return;
this.#observeChildren();
}
async #observeChildren() {
if (!this.#treeItemContext?.requestChildren) return;
const { asObservable } = await this.#treeItemContext.requestChildren();
if (!asObservable) return;
this.observe(asObservable(), (childItems) => {
const oldValue = this._childItems;
this._childItems = childItems;
this.requestUpdate('_childItems', oldValue);
});
this.#treeItemContext?.loadChildren();
}
#onLoadMoreClick = (event: any) => {
@@ -176,7 +163,7 @@ export abstract class UmbTreeItemElementBase<TreeItemModelType extends UmbTreeIt
.entityType=${this.#treeItemContext.entityType}
.unique=${this.#treeItemContext.unique}
.label=${this._item.name}>
</umb-entity-actions-bundle>`
</umb-entity-actions-bundle>`
: '';
}
@@ -187,7 +174,7 @@ export abstract class UmbTreeItemElementBase<TreeItemModelType extends UmbTreeIt
this._childItems,
(item, index) => item.name + '___' + index,
(item) => html`<umb-tree-item .entityType=${item.entityType} .props=${{ item }}></umb-tree-item>`,
)
)
: ''}
`;
}

View File

@@ -1,14 +1,13 @@
import type { UmbTreeItemModelBase } from '../types.js';
import type { UmbPaginationManager } from '../../utils/pagination-manager/pagination.manager.js';
import type { Observable } from '@umbraco-cms/backoffice/external/rxjs';
import type { ProblemDetails } from '@umbraco-cms/backoffice/external/backend-api';
import type { UmbPagedModel } from '@umbraco-cms/backoffice/repository';
import type { UmbApi } from '@umbraco-cms/backoffice/extension-api';
export interface UmbTreeItemContext<TreeItemType extends UmbTreeItemModelBase> extends UmbApi {
unique?: string | null;
entityType?: string;
treeItem: Observable<TreeItemType | undefined>;
childItems: Observable<TreeItemType[]>;
hasChildren: Observable<boolean>;
isLoading: Observable<boolean>;
isSelectableContext: Observable<boolean>;
@@ -19,11 +18,7 @@ export interface UmbTreeItemContext<TreeItemType extends UmbTreeItemModelBase> e
path: Observable<string>;
pagination: UmbPaginationManager;
setTreeItem(treeItem: TreeItemType | undefined): void;
requestChildren(): Promise<{
data?: UmbPagedModel<TreeItemType> | undefined;
error?: ProblemDetails | undefined;
asObservable?: () => Observable<TreeItemType[]>;
}>;
loadChildren(): void;
toggleContextMenu(): void;
select(): void;
deselect(): void;