Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions src/Umbraco.Web.UI.Client/examples/tree/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Tree Example

This example demonstrates how to register a tree

The example includes:

- Tree + Tree Item Registration
- Tree Repository + Store Registration
- A Dashboard to show how to render a tree anywhere in the backoffice
- How to use the tree in the sidebar menu
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { EXAMPLE_TREE_ALIAS } from '../tree/constants.js';
import { html, customElement, LitElement, css } from '@umbraco-cms/backoffice/external/lit';
import { UmbElementMixin } from '@umbraco-cms/backoffice/element-api';

@customElement('example-dashboard-with-tree')
export class ExampleDashboardWithTree extends UmbElementMixin(LitElement) {
override render() {
return html`<uui-box><umb-tree alias=${EXAMPLE_TREE_ALIAS}></umb-tree></uui-box>`;
}

static override styles = [
css`
:host {
display: block;
padding: var(--uui-size-layout-1);
}
`,
];
}

export { ExampleDashboardWithTree as element };

declare global {
interface HTMLElementTagNameMap {
'example-dashboard-with-tree': ExampleDashboardWithTree;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export const manifests: Array<UmbExtensionManifest> = [
{
type: 'dashboard',
kind: 'default',
name: 'Example Dashboard With Tree',
alias: 'Example.Dashboard.WithTree',
element: () => import('./dashboard-with-tree.element.js'),
weight: 3000,
meta: {
label: 'Tree Example',
pathname: 'tree-example',
},
},
];
5 changes: 5 additions & 0 deletions src/Umbraco.Web.UI.Client/examples/tree/entity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export const EXAMPLE_ENTITY_TYPE = 'example';
export const EXAMPLE_ROOT_ENTITY_TYPE = 'example-root';

export type ExampleEntityType = typeof EXAMPLE_ENTITY_TYPE;
export type ExampleRootEntityType = typeof EXAMPLE_ROOT_ENTITY_TYPE;
5 changes: 5 additions & 0 deletions src/Umbraco.Web.UI.Client/examples/tree/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { manifests as dashboardManifests } from './dashboard-with-tree/manifests.js';
import { manifests as menuItemManifests } from './menu-item-with-tree/manifests.js';
import { manifests as treeManifests } from './tree/manifests.js';

export const manifests: Array<UmbExtensionManifest> = [...dashboardManifests, ...menuItemManifests, ...treeManifests];
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { EXAMPLE_TREE_ALIAS } from '../tree/constants.js';
import { UMB_CONTENT_MENU_ALIAS } from '@umbraco-cms/backoffice/document';

export const manifests: Array<UmbExtensionManifest> = [
{
type: 'menuItem',
kind: 'tree',
alias: 'Example.MenuItem.Tree',
name: 'Example Tree Menu Item',
weight: 1000,
meta: {
label: 'Example Tree',
menus: [UMB_CONTENT_MENU_ALIAS],
treeAlias: EXAMPLE_TREE_ALIAS,
},
},
];
1 change: 1 addition & 0 deletions src/Umbraco.Web.UI.Client/examples/tree/tree/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const EXAMPLE_TREE_ALIAS = 'Example.Tree';
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './repository/constants.js';
export * from './store/constants.js';
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './tree.local.data-source.js';
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import type { ExampleTreeItemModel } from '../../types.js';
import { EXAMPLE_ENTITY_TYPE, EXAMPLE_ROOT_ENTITY_TYPE } from '../../../entity.js';
import type {
UmbTreeAncestorsOfRequestArgs,
UmbTreeChildrenOfRequestArgs,
UmbTreeDataSource,
UmbTreeRootItemsRequestArgs,
} from '@umbraco-cms/backoffice/tree';
import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api';

const EXAMPLE_TREE_DATA: Array<ExampleTreeItemModel> = [
{
entityType: EXAMPLE_ENTITY_TYPE,
hasChildren: false,
isFolder: false,
name: 'Item 1',
parent: { unique: null, entityType: EXAMPLE_ROOT_ENTITY_TYPE },
unique: 'ab7b6e03-5f4d-4a6b-9f4c-21292d462e08',
icon: 'icon-newspaper',
},
{
entityType: EXAMPLE_ENTITY_TYPE,
hasChildren: true,
isFolder: false,
name: 'Item 2',
parent: { unique: null, entityType: EXAMPLE_ROOT_ENTITY_TYPE },
unique: '74a5b2d9-3564-45b8-a3ee-98fc7ec0c1fb',
icon: 'icon-newspaper',
},
{
entityType: EXAMPLE_ENTITY_TYPE,
hasChildren: false,
isFolder: false,
name: 'Item 3',
parent: { unique: null, entityType: EXAMPLE_ROOT_ENTITY_TYPE },
unique: '1b8ed2ac-b4bb-4384-972e-2cf18f40586a',
icon: 'icon-newspaper',
},
{
entityType: EXAMPLE_ENTITY_TYPE,
hasChildren: false,
isFolder: false,
name: 'Item 2.1',
parent: { unique: '74a5b2d9-3564-45b8-a3ee-98fc7ec0c1fb', entityType: EXAMPLE_ENTITY_TYPE },
unique: '62dbd672-b198-4fc8-8b42-5d21dfbd3788',
icon: 'icon-newspaper',
},
{
entityType: EXAMPLE_ENTITY_TYPE,
hasChildren: true,
isFolder: false,
name: 'Item 2.2',
parent: { unique: '74a5b2d9-3564-45b8-a3ee-98fc7ec0c1fb', entityType: EXAMPLE_ENTITY_TYPE },
unique: 'deaa3f8c-e40b-4eb7-8268-34014504152e',
icon: 'icon-newspaper',
},
{
entityType: EXAMPLE_ENTITY_TYPE,
hasChildren: false,
isFolder: false,
name: 'Item 2.2.1',
parent: { unique: 'deaa3f8c-e40b-4eb7-8268-34014504152e', entityType: EXAMPLE_ENTITY_TYPE },
unique: 'd4cf5fd2-1f84-4f3b-b63b-5f29a38e72d1',
icon: 'icon-newspaper',
},
];

export class ExampleTreeLocalDataSource extends UmbControllerBase implements UmbTreeDataSource<ExampleTreeItemModel> {
async getRootItems(args: UmbTreeRootItemsRequestArgs) {
// TODO: handle skip, take, foldersOnly.
console.log(args);
const rootItems: Array<ExampleTreeItemModel> = EXAMPLE_TREE_DATA.filter((item) => item.parent.unique === null);
return { data: { items: rootItems, total: rootItems.length } };
}

async getChildrenOf(args: UmbTreeChildrenOfRequestArgs) {
// TODO: handle skip, take, foldersOnly.
const children = EXAMPLE_TREE_DATA.filter(
(item) => item.parent.unique === args.parent.unique && item.parent.entityType === args.parent.entityType,
);

return { data: { items: children, total: children.length } };
}

async getAncestorsOf(args: UmbTreeAncestorsOfRequestArgs) {
const ancestors = findAncestors(args.treeItem.unique, args.treeItem.entityType);
return { data: ancestors };
}
}

// Helper function to find ancestors recursively
const findAncestors = (unique: string, entityType: string): Array<ExampleTreeItemModel> => {
const item = EXAMPLE_TREE_DATA.find((i) => i.unique === unique && i.entityType === entityType);

if (!item || !item.parent || item.parent.unique === null) {

Check warning on line 95 in src/Umbraco.Web.UI.Client/examples/tree/tree/data/local-data-source/tree.local.data-source.ts

View check run for this annotation

CodeScene Delta Analysis / CodeScene Cloud Delta Analysis (main)

❌ New issue: Complex Conditional

findAncestors has 1 complex conditionals with 2 branches, threshold = 2. A complex conditional is an expression inside a branch (e.g. if, for, while) which consists of multiple, logical operators such as AND/OR. The more logical operators in an expression, the more severe the code smell.
return [];
}

const parent = EXAMPLE_TREE_DATA.find(
(i) => i.unique === item.parent.unique && i.entityType === item.parent.entityType,
);

if (!parent) {
return [];
}

return [parent, ...findAncestors(parent.unique, parent.entityType)];
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { manifests as repositoryManifests } from './repository/manifests.js';
import { manifests as storeManifests } from './store/manifests.js';

export const manifests: Array<UmbExtensionManifest> = [...repositoryManifests, ...storeManifests];
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const EXAMPLE_TREE_REPOSITORY_ALIAS = 'Example.Repository.Tree';
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { EXAMPLE_TREE_REPOSITORY_ALIAS } from './constants.js';

export const manifests: Array<UmbExtensionManifest> = [
{
type: 'repository',
alias: EXAMPLE_TREE_REPOSITORY_ALIAS,
name: 'Example Tree Repository',
api: () => import('./tree.repository.js'),
},
];
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import type { ExampleTreeItemModel, ExampleTreeRootModel } from '../../types.js';
import { EXAMPLE_ROOT_ENTITY_TYPE } from '../../../entity.js';
import { EXAMPLE_TREE_STORE_CONTEXT } from '../store/index.js';
import { ExampleTreeLocalDataSource } from '../local-data-source/index.js';
import { UmbTreeRepositoryBase, type UmbTreeRepository } from '@umbraco-cms/backoffice/tree';
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
import type { UmbApi } from '@umbraco-cms/backoffice/extension-api';

export class ExampleTreeRepository
extends UmbTreeRepositoryBase<ExampleTreeItemModel, ExampleTreeRootModel>
implements UmbTreeRepository, UmbApi
{
constructor(host: UmbControllerHost) {
super(host, ExampleTreeLocalDataSource, EXAMPLE_TREE_STORE_CONTEXT);
}

async requestTreeRoot() {
const root: ExampleTreeRootModel = {
unique: null,
entityType: EXAMPLE_ROOT_ENTITY_TYPE,
name: 'Example Tree',
hasChildren: true,
isFolder: true,
};

return { data: root };
}
}

export { ExampleTreeRepository as api };
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const EXAMPLE_TREE_STORE_ALIAS = 'Example.Store.Tree';
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './tree.store.context-token.js';
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { EXAMPLE_TREE_STORE_ALIAS } from './constants.js';

export const manifests: Array<UmbExtensionManifest> = [
{
type: 'treeStore',
alias: EXAMPLE_TREE_STORE_ALIAS,
name: 'Example Tree Store',
api: () => import('./tree.store.js'),
},
];
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import type { ExampleTreeStore } from './tree.store.js';
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';

export const EXAMPLE_TREE_STORE_CONTEXT = new UmbContextToken<ExampleTreeStore>('ExampleTreeStore');
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { EXAMPLE_TREE_STORE_CONTEXT } from './tree.store.context-token.js';
import { UmbUniqueTreeStore } from '@umbraco-cms/backoffice/tree';
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';

export class ExampleTreeStore extends UmbUniqueTreeStore {
constructor(host: UmbControllerHost) {
super(host, EXAMPLE_TREE_STORE_CONTEXT.toString());
}
}

export { ExampleTreeStore as api };
24 changes: 24 additions & 0 deletions src/Umbraco.Web.UI.Client/examples/tree/tree/manifests.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { EXAMPLE_ENTITY_TYPE, EXAMPLE_ROOT_ENTITY_TYPE } from '../entity.js';
import { EXAMPLE_TREE_ALIAS } from './constants.js';
import { EXAMPLE_TREE_REPOSITORY_ALIAS } from './data/constants.js';
import { manifests as dataManifests } from './data/manifests.js';

export const manifests: Array<UmbExtensionManifest> = [
{
type: 'tree',
kind: 'default',
alias: EXAMPLE_TREE_ALIAS,
name: 'Example Tree',
meta: {
repositoryAlias: EXAMPLE_TREE_REPOSITORY_ALIAS,
},
},
{
type: 'treeItem',
kind: 'default',
alias: 'Example.TreeItem',
name: 'Example Tree Item',
forEntityTypes: [EXAMPLE_ROOT_ENTITY_TYPE, EXAMPLE_ENTITY_TYPE],
},
...dataManifests,
];
10 changes: 10 additions & 0 deletions src/Umbraco.Web.UI.Client/examples/tree/tree/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import type { ExampleEntityType, ExampleRootEntityType } from '../entity.js';
import type { UmbTreeItemModel, UmbTreeRootModel } from '@umbraco-cms/backoffice/tree';

export interface ExampleTreeItemModel extends UmbTreeItemModel {
entityType: ExampleEntityType;
}

export interface ExampleTreeRootModel extends UmbTreeRootModel {
entityType: ExampleRootEntityType;
}
Loading