keep tree items in the context and observe from element
This commit is contained in:
@@ -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 {
|
||||
|
||||
@@ -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>`,
|
||||
)}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>`,
|
||||
)
|
||||
)
|
||||
: ''}
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user