diff --git a/src/Umbraco.Web.UI.Client/libs/extensions-registry/models/entity-action.model.ts b/src/Umbraco.Web.UI.Client/libs/extensions-registry/models/entity-action.model.ts index 3c2dd26ecb..f46fb6510c 100644 --- a/src/Umbraco.Web.UI.Client/libs/extensions-registry/models/entity-action.model.ts +++ b/src/Umbraco.Web.UI.Client/libs/extensions-registry/models/entity-action.model.ts @@ -47,5 +47,5 @@ export interface MetaEntityAction { } export interface ConditionsEntityAction { - entityType: string; + entityTypes: Array; } diff --git a/src/Umbraco.Web.UI.Client/libs/extensions-registry/models/tree-item.model.ts b/src/Umbraco.Web.UI.Client/libs/extensions-registry/models/tree-item.model.ts index 7f327014a0..472d1605a5 100644 --- a/src/Umbraco.Web.UI.Client/libs/extensions-registry/models/tree-item.model.ts +++ b/src/Umbraco.Web.UI.Client/libs/extensions-registry/models/tree-item.model.ts @@ -7,5 +7,5 @@ export interface ManifestTreeItem extends ManifestElement; } diff --git a/src/Umbraco.Web.UI.Client/libs/modal/token/data-type-picker-modal.token.ts b/src/Umbraco.Web.UI.Client/libs/modal/token/data-type-picker-modal.token.ts index 51d90eb871..2297e7e023 100644 --- a/src/Umbraco.Web.UI.Client/libs/modal/token/data-type-picker-modal.token.ts +++ b/src/Umbraco.Web.UI.Client/libs/modal/token/data-type-picker-modal.token.ts @@ -1,12 +1,12 @@ import { UmbModalToken } from '@umbraco-cms/backoffice/modal'; export interface UmbDataTypePickerModalData { - selection?: Array; + selection?: Array; multiple?: boolean; } export interface UmbDataTypePickerModalResult { - selection: Array; + selection: Array; } export const UMB_DATA_TYPE_PICKER_MODAL = new UmbModalToken( diff --git a/src/Umbraco.Web.UI.Client/libs/modal/token/document-picker-modal.token.ts b/src/Umbraco.Web.UI.Client/libs/modal/token/document-picker-modal.token.ts index 133318c692..5209f34110 100644 --- a/src/Umbraco.Web.UI.Client/libs/modal/token/document-picker-modal.token.ts +++ b/src/Umbraco.Web.UI.Client/libs/modal/token/document-picker-modal.token.ts @@ -2,11 +2,11 @@ import { UmbModalToken } from '@umbraco-cms/backoffice/modal'; export interface UmbDocumentPickerModalData { multiple?: boolean; - selection?: Array; + selection?: Array; } export interface UmbDocumentPickerModalResult { - selection: Array; + selection: Array; } export const UMB_DOCUMENT_PICKER_MODAL = new UmbModalToken( diff --git a/src/Umbraco.Web.UI.Client/libs/modal/token/document-type-picker-modal.token.ts b/src/Umbraco.Web.UI.Client/libs/modal/token/document-type-picker-modal.token.ts index d0c16dfc68..6b2d0a6341 100644 --- a/src/Umbraco.Web.UI.Client/libs/modal/token/document-type-picker-modal.token.ts +++ b/src/Umbraco.Web.UI.Client/libs/modal/token/document-type-picker-modal.token.ts @@ -6,7 +6,7 @@ export interface UmbDocumentTypePickerModalData { } export interface UmbDocumentTypePickerModalResult { - selection: Array; + selection: Array; } export const UMB_DOCUMENT_TYPE_PICKER_MODAL = new UmbModalToken< diff --git a/src/Umbraco.Web.UI.Client/libs/modal/token/media-picker-modal.token.ts b/src/Umbraco.Web.UI.Client/libs/modal/token/media-picker-modal.token.ts index 94607575c4..7e25303f88 100644 --- a/src/Umbraco.Web.UI.Client/libs/modal/token/media-picker-modal.token.ts +++ b/src/Umbraco.Web.UI.Client/libs/modal/token/media-picker-modal.token.ts @@ -6,7 +6,7 @@ export interface UmbMediaPickerModalData { } export interface UmbMediaPickerModalResult { - selection: Array; + selection: Array; } export const UMB_MEDIA_PICKER_MODAL = new UmbModalToken( diff --git a/src/Umbraco.Web.UI.Client/libs/modal/token/template-picker-modal.token.ts b/src/Umbraco.Web.UI.Client/libs/modal/token/template-picker-modal.token.ts index a4660816e9..8f9b122a69 100644 --- a/src/Umbraco.Web.UI.Client/libs/modal/token/template-picker-modal.token.ts +++ b/src/Umbraco.Web.UI.Client/libs/modal/token/template-picker-modal.token.ts @@ -2,11 +2,11 @@ import { UmbModalToken } from '@umbraco-cms/backoffice/modal'; export interface UmbTemplatePickerModalData { multiple: boolean; - selection: string[]; + selection: Array; } export interface UmbTemplatePickerModalResult { - selection: string[] | undefined; + selection: Array; } export const UMB_TEMPLATE_PICKER_MODAL = new UmbModalToken( diff --git a/src/Umbraco.Web.UI.Client/libs/models/index.ts b/src/Umbraco.Web.UI.Client/libs/models/index.ts index d370269599..4b16ebba15 100644 --- a/src/Umbraco.Web.UI.Client/libs/models/index.ts +++ b/src/Umbraco.Web.UI.Client/libs/models/index.ts @@ -114,3 +114,18 @@ export interface UmbFilterModel { take?: number; filter?: string; } + +export interface UmbTreeRootModel { + type: string; + name: string; + hasChildren: boolean; + icon?: string; +} + +export interface UmbTreeRootEntityModel extends UmbTreeRootModel { + id: string | null; +} + +export interface UmbTreeRootFileSystemModel extends UmbTreeRootModel { + path: string | null; +} diff --git a/src/Umbraco.Web.UI.Client/libs/repository/data-source/copy-data-source.interface.ts b/src/Umbraco.Web.UI.Client/libs/repository/data-source/copy-data-source.interface.ts index b9d41d7971..f2477be161 100644 --- a/src/Umbraco.Web.UI.Client/libs/repository/data-source/copy-data-source.interface.ts +++ b/src/Umbraco.Web.UI.Client/libs/repository/data-source/copy-data-source.interface.ts @@ -1,5 +1,5 @@ import type { DataSourceResponse } from '@umbraco-cms/backoffice/repository'; export interface UmbCopyDataSource { - copy(unique: string, targetUnique: string): Promise>; + copy(unique: string, targetUnique: string | null): Promise>; } diff --git a/src/Umbraco.Web.UI.Client/libs/repository/data-source/move-data-source.interface.ts b/src/Umbraco.Web.UI.Client/libs/repository/data-source/move-data-source.interface.ts index c0639e31ce..e0ac7ce1d7 100644 --- a/src/Umbraco.Web.UI.Client/libs/repository/data-source/move-data-source.interface.ts +++ b/src/Umbraco.Web.UI.Client/libs/repository/data-source/move-data-source.interface.ts @@ -1,5 +1,5 @@ import type { UmbDataSourceErrorResponse } from '@umbraco-cms/backoffice/repository'; export interface UmbMoveDataSource { - move(unique: string, targetUnique: string): Promise; + move(unique: string, targetUnique: string | null): Promise; } diff --git a/src/Umbraco.Web.UI.Client/libs/repository/data-source/tree-data-source.interface.ts b/src/Umbraco.Web.UI.Client/libs/repository/data-source/tree-data-source.interface.ts index 52f623e13b..3865b21650 100644 --- a/src/Umbraco.Web.UI.Client/libs/repository/data-source/tree-data-source.interface.ts +++ b/src/Umbraco.Web.UI.Client/libs/repository/data-source/tree-data-source.interface.ts @@ -1,9 +1,9 @@ -import type { DataSourceResponse } from '@umbraco-cms/backoffice/repository'; - -export interface UmbTreeDataSource { - getRootItems(): Promise>; - getChildrenOf(parentUnique: string): Promise>; +import type { UmbPagedData } from '../tree-repository.interface'; +import type { DataSourceResponse } from './data-source-response.interface'; +export interface UmbTreeDataSource> { + getRootItems(): Promise>; + getChildrenOf(parentUnique: string | null): Promise>; // TODO: remove this when all repositories are migrated to the new items interface - getItems(unique: Array): Promise>>; + getItems(unique: Array): Promise>>; } diff --git a/src/Umbraco.Web.UI.Client/libs/repository/tree-repository.interface.ts b/src/Umbraco.Web.UI.Client/libs/repository/tree-repository.interface.ts index 17ddc1a2d4..c267d1730a 100644 --- a/src/Umbraco.Web.UI.Client/libs/repository/tree-repository.interface.ts +++ b/src/Umbraco.Web.UI.Client/libs/repository/tree-repository.interface.ts @@ -1,33 +1,44 @@ import type { Observable } from 'rxjs'; -import { ProblemDetailsModel } from '@umbraco-cms/backoffice/backend-api'; +import { ProblemDetailsModel, TreeItemPresentationModel } from '@umbraco-cms/backoffice/backend-api'; +import type { UmbTreeRootEntityModel, UmbTreeRootModel } from '@umbraco-cms/backoffice/models'; export interface UmbPagedData { total: number; items: Array; } -export interface UmbTreeRepository> { - requestRootTreeItems: () => Promise<{ - data: PagedItemType | undefined; - error: ProblemDetailsModel | undefined; - asObservable?: () => Observable; +export interface UmbTreeRepository< + TreeItemType extends TreeItemPresentationModel, + TreeRootType extends UmbTreeRootModel = UmbTreeRootEntityModel +> { + requestTreeRoot: () => Promise<{ + data?: TreeRootType; + error?: ProblemDetailsModel; }>; + + requestRootTreeItems: () => Promise<{ + data?: UmbPagedData; + error?: ProblemDetailsModel; + asObservable?: () => Observable; + }>; + requestTreeItemsOf: (parentUnique: string | null) => Promise<{ - data: PagedItemType | undefined; - error: ProblemDetailsModel | undefined; - asObservable?: () => Observable; + data?: UmbPagedData; + error?: ProblemDetailsModel; + asObservable?: () => Observable; }>; // TODO: remove this when all repositories are migrated to the new interface items interface requestItemsLegacy?: (uniques: string[]) => Promise<{ - data: Array | undefined; - error: ProblemDetailsModel | undefined; - asObservable?: () => Observable; + data?: Array; + error?: ProblemDetailsModel; + asObservable?: () => Observable; }>; - rootTreeItems: () => Promise>; - treeItemsOf: (parentUnique: string | null) => Promise>; + rootTreeItems: () => Promise>; + + treeItemsOf: (parentUnique: string | null) => Promise>; // TODO: remove this when all repositories are migrated to the new items interface - itemsLegacy?: (uniques: string[]) => Promise>; + itemsLegacy?: (uniques: string[]) => Promise>; } diff --git a/src/Umbraco.Web.UI.Client/libs/store/entity-tree-store.ts b/src/Umbraco.Web.UI.Client/libs/store/entity-tree-store.ts index fb032ac309..2de0ac6d25 100644 --- a/src/Umbraco.Web.UI.Client/libs/store/entity-tree-store.ts +++ b/src/Umbraco.Web.UI.Client/libs/store/entity-tree-store.ts @@ -39,7 +39,7 @@ export class UmbEntityTreeStore * @return {*} * @memberof UmbEntityTreeStore */ - items(ids: Array) { + items(ids: Array) { return this._data.getObservablePart((items) => items.filter((item) => ids.includes(item.id ?? ''))); } } diff --git a/src/Umbraco.Web.UI.Client/libs/store/file-system-tree.store.ts b/src/Umbraco.Web.UI.Client/libs/store/file-system-tree.store.ts index 68c9352ce5..d8294f911d 100644 --- a/src/Umbraco.Web.UI.Client/libs/store/file-system-tree.store.ts +++ b/src/Umbraco.Web.UI.Client/libs/store/file-system-tree.store.ts @@ -30,7 +30,9 @@ export class UmbFileSystemTreeStore * @memberof UmbFileSystemTreeStore */ childrenOf(parentPath: string | null) { - return this._data.getObservablePart((items) => items.filter((item) => item.path?.startsWith(parentPath + '/'))); + return this._data.getObservablePart((items) => + items.filter((item) => item.path?.startsWith(parentPath + '/') || parentPath === null) + ); } /** diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/repository/document-type.repository.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/repository/document-type.repository.ts index 72e2b8d47d..aa74883c45 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/repository/document-type.repository.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/repository/document-type.repository.ts @@ -5,12 +5,18 @@ import { UmbDocumentTypeStore, UMB_DOCUMENT_TYPE_STORE_CONTEXT_TOKEN } from './d import type { UmbTreeDataSource, UmbTreeRepository, UmbDetailRepository } from '@umbraco-cms/backoffice/repository'; import type { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller'; import { UmbContextConsumerController } from '@umbraco-cms/backoffice/context-api'; -import { DocumentTypeResponseModel, FolderTreeItemResponseModel } from '@umbraco-cms/backoffice/backend-api'; +import { + DocumentTypeResponseModel, + EntityTreeItemResponseModel, + FolderTreeItemResponseModel, +} from '@umbraco-cms/backoffice/backend-api'; import { UmbNotificationContext, UMB_NOTIFICATION_CONTEXT_TOKEN } from '@umbraco-cms/backoffice/notification'; type ItemType = DocumentTypeResponseModel; -export class UmbDocumentTypeRepository implements UmbTreeRepository, UmbDetailRepository { +export class UmbDocumentTypeRepository + implements UmbTreeRepository, UmbDetailRepository +{ #init!: Promise; #host: UmbControllerHostElement; @@ -48,6 +54,20 @@ export class UmbDocumentTypeRepository implements UmbTreeRepository, U // TODO: Trash // TODO: Move + async requestTreeRoot() { + await this.#init; + + const data = { + id: null, + type: 'document-type-root', + name: 'Document Types', + icon: 'umb:folder', + hasChildren: true, + }; + + return { data }; + } + async requestRootTreeItems() { await this.#init; @@ -62,10 +82,7 @@ export class UmbDocumentTypeRepository implements UmbTreeRepository, U async requestTreeItemsOf(parentId: string | null) { await this.#init; - - if (!parentId) { - throw new Error('Parent id is missing'); - } + if (parentId === undefined) throw new Error('Parent id is missing'); const { data, error } = await this.#treeSource.getChildrenOf(parentId); @@ -106,7 +123,7 @@ export class UmbDocumentTypeRepository implements UmbTreeRepository, U // DETAILS: async createScaffold(parentId: string | null) { - if (!parentId) throw new Error('Parent id is missing'); + if (parentId === undefined) throw new Error('Parent id is missing'); await this.#init; return this.#detailDataSource.createScaffold(parentId); } diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/repository/sources/document-type.tree.server.data.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/repository/sources/document-type.tree.server.data.ts index 93061d3d91..d67e33dea5 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/repository/sources/document-type.tree.server.data.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/repository/sources/document-type.tree.server.data.ts @@ -66,16 +66,20 @@ export class UmbDocumentTypeTreeServerDataSource implements UmbTreeDataSource { * @memberof UmbDocumentTypeTreeServerDataSource */ async getChildrenOf(parentId: string | null) { - if (!parentId) { - throw new Error('Parent id is missing'); - } + if (parentId === undefined) throw new Error('Parent id is missing'); - return tryExecuteAndNotify( - this.#host, - DocumentTypeResource.getTreeDocumentTypeChildren({ - parentId, - }) - ); + /* TODO: should we make getRootItems() internal + so it only is a server concern that there are two endpoints? */ + if (parentId === null) { + return this.getRootItems(); + } else { + return tryExecuteAndNotify( + this.#host, + DocumentTypeResource.getTreeDocumentTypeChildren({ + parentId, + }) + ); + } } /** diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/tree/manifests.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/tree/manifests.ts index 5a5ccaa08d..2f2bf1fd5b 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/tree/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/tree/manifests.ts @@ -16,7 +16,7 @@ const treeItem: ManifestTreeItem = { alias: 'Umb.TreeItem.DocumentType', name: 'Document Type Tree Item', conditions: { - entityType: 'document-type', + entityTypes: ['document-type-root', 'document-type'], }, }; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/workspace/views/templates/document-type-workspace-view-templates.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/workspace/views/templates/document-type-workspace-view-templates.element.ts index ffc8c93c8f..6d1ddf5349 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/workspace/views/templates/document-type-workspace-view-templates.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/workspace/views/templates/document-type-workspace-view-templates.element.ts @@ -44,7 +44,8 @@ export class UmbDocumentTypeWorkspaceViewTemplatesElement console.log('change', e); // save new allowed ids const input = e.target as UmbInputTemplateElement; - this.#workspaceContext?.setAllowedTemplateIds(input.selectedIds); + const idsWithoutRoot = input.selectedIds.filter((id) => id !== null) as Array; + this.#workspaceContext?.setAllowedTemplateIds(idsWithoutRoot); this.#workspaceContext?.setDefaultTemplateId(input.defaultId); } diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/entity-actions/manifests.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/entity-actions/manifests.ts index ab2d9ac5b8..e6832c79db 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/entity-actions/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/entity-actions/manifests.ts @@ -30,7 +30,7 @@ const entityActions: Array = [ api: UmbCreateDocumentEntityAction, }, conditions: { - entityType, + entityTypes: [entityType], }, }, { @@ -45,7 +45,7 @@ const entityActions: Array = [ api: UmbTrashEntityAction, }, conditions: { - entityType, + entityTypes: [entityType], }, }, { @@ -60,7 +60,7 @@ const entityActions: Array = [ api: UmbCreateDocumentBlueprintEntityAction, }, conditions: { - entityType, + entityTypes: [entityType], }, }, { @@ -75,7 +75,7 @@ const entityActions: Array = [ api: UmbMoveEntityAction, }, conditions: { - entityType, + entityTypes: [entityType], }, }, { @@ -90,7 +90,7 @@ const entityActions: Array = [ api: UmbCopyEntityAction, }, conditions: { - entityType, + entityTypes: [entityType], }, }, { @@ -105,7 +105,7 @@ const entityActions: Array = [ api: UmbSortChildrenOfEntityAction, }, conditions: { - entityType, + entityTypes: [entityType], }, }, { @@ -120,7 +120,7 @@ const entityActions: Array = [ api: UmbDocumentCultureAndHostnamesEntityAction, }, conditions: { - entityType, + entityTypes: [entityType], }, }, { @@ -134,7 +134,7 @@ const entityActions: Array = [ api: UmbDocumentPermissionsEntityAction, }, conditions: { - entityType, + entityTypes: [entityType], }, }, { @@ -148,7 +148,7 @@ const entityActions: Array = [ api: UmbDocumentPublicAccessEntityAction, }, conditions: { - entityType, + entityTypes: [entityType], }, }, { @@ -162,7 +162,7 @@ const entityActions: Array = [ api: UmbPublishDocumentEntityAction, }, conditions: { - entityType, + entityTypes: [entityType], }, }, { @@ -176,7 +176,7 @@ const entityActions: Array = [ api: UmbUnpublishDocumentEntityAction, }, conditions: { - entityType, + entityTypes: [entityType], }, }, { @@ -190,7 +190,7 @@ const entityActions: Array = [ api: UmbRollbackDocumentEntityAction, }, conditions: { - entityType, + entityTypes: [entityType], }, }, ]; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/menu-item/document-menu-item.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/menu-item/document-menu-item.element.ts index 07d94f6a1f..74197d3c8e 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/menu-item/document-menu-item.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/menu-item/document-menu-item.element.ts @@ -5,7 +5,7 @@ import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; @customElement('umb-document-menu-item') export class UmbDocumentMenuItemElement extends UmbLitElement { render() { - return html``; + return html``; } } diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/modals/document-picker/document-picker-modal.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/modals/document-picker/document-picker-modal.element.ts index f56e5a3eb6..268169db65 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/modals/document-picker/document-picker-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/modals/document-picker/document-picker-modal.element.ts @@ -11,10 +11,8 @@ export class UmbDocumentPickerModalElement extends UmbModalBaseElement< UmbDocumentPickerModalData, UmbDocumentPickerModalResult > { - - @state() - _selection: Array = []; + _selection: Array = []; @state() _multiple = true; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/modals/document-type-picker/document-type-picker-modal.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/modals/document-type-picker/document-type-picker-modal.element.ts index 32a32bb1f0..aca2946ea2 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/modals/document-type-picker/document-type-picker-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/modals/document-type-picker/document-type-picker-modal.element.ts @@ -11,10 +11,8 @@ export class UmbDocumentTypePickerModalElement extends UmbModalBaseElement< UmbDocumentTypePickerModalData, UmbDocumentTypePickerModalResult > { - - @state() - _selection: Array = []; + _selection: Array = []; @state() _multiple = true; @@ -28,8 +26,7 @@ export class UmbDocumentTypePickerModalElement extends UmbModalBaseElement< private _handleSelectionChange(e: CustomEvent) { e.stopPropagation(); const element = e.target as UmbTreeElement; - //TODO: Should multiple property be implemented here or be passed down into umb-tree? - this._selection = this._multiple ? element.selection : [element.selection[element.selection.length - 1]]; + this._selection = element.selection; } private _submit() { @@ -50,7 +47,8 @@ export class UmbDocumentTypePickerModalElement extends UmbModalBaseElement< alias="Umb.Tree.DocumentTypes" @selected=${this._handleSelectionChange} .selection=${this._selection} - selectable> + selectable + ?multiple=${this._multiple}>
diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/repository/document.repository.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/repository/document.repository.ts index 019ee03128..092a42980d 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/repository/document.repository.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/repository/document.repository.ts @@ -9,12 +9,15 @@ import { DocumentResponseModel, CreateDocumentRequestModel, UpdateDocumentRequestModel, + DocumentTreeItemResponseModel, } from '@umbraco-cms/backoffice/backend-api'; import { UmbNotificationContext, UMB_NOTIFICATION_CONTEXT_TOKEN } from '@umbraco-cms/backoffice/notification'; type ItemType = DocumentResponseModel; -export class UmbDocumentRepository implements UmbTreeRepository, UmbDetailRepository { +export class UmbDocumentRepository + implements UmbTreeRepository, UmbDetailRepository +{ #init!: Promise; #host: UmbControllerHostElement; @@ -52,6 +55,21 @@ export class UmbDocumentRepository implements UmbTreeRepository, UmbDe // TODO: Trash // TODO: Move + // TREE: + async requestTreeRoot() { + await this.#init; + + const data = { + id: null, + type: 'document-root', + name: 'Documents', + icon: 'umb:folder', + hasChildren: true, + }; + + return { data }; + } + async requestRootTreeItems() { await this.#init; @@ -66,10 +84,7 @@ export class UmbDocumentRepository implements UmbTreeRepository, UmbDe async requestTreeItemsOf(parentId: string | null) { await this.#init; - - if (!parentId) { - throw new Error('Parent id is missing'); - } + if (parentId === undefined) throw new Error('Parent id is missing'); const { data, error } = await this.#treeSource.getChildrenOf(parentId); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/repository/sources/document.tree.server.data.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/repository/sources/document.tree.server.data.ts index 74320f4691..7b599e17a8 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/repository/sources/document.tree.server.data.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/repository/sources/document.tree.server.data.ts @@ -66,16 +66,20 @@ export class UmbDocumentTreeServerDataSource implements UmbTreeDataSource { * @memberof UmbDocumentTreeServerDataSource */ async getChildrenOf(parentId: string | null) { - if (!parentId) { - throw new Error('Parent id is missing'); - } + if (parentId === undefined) throw new Error('Parent id is missing'); - return tryExecuteAndNotify( - this.#host, - DocumentResource.getTreeDocumentChildren({ - parentId, - }) - ); + /* TODO: should we make getRootItems() internal + so it only is a server concern that there are two endpoints? */ + if (parentId === null) { + return this.getRootItems(); + } else { + return tryExecuteAndNotify( + this.#host, + DocumentResource.getTreeDocumentChildren({ + parentId, + }) + ); + } } /** 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 a5dbe90321..306b2da976 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 @@ -18,7 +18,7 @@ const treeItem: ManifestTreeItem = { name: 'Document Tree Item', loader: () => import('./tree-item/document-tree-item.element'), conditions: { - entityType: 'document', + entityTypes: ['document-root', 'document'], }, }; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/media/media-types/entity-actions/manifests.ts b/src/Umbraco.Web.UI.Client/src/backoffice/media/media-types/entity-actions/manifests.ts index 0535ffc768..6ad213af98 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/media/media-types/entity-actions/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/media/media-types/entity-actions/manifests.ts @@ -20,7 +20,7 @@ const entityActions: Array = [ api: UmbCreateMediaTypeEntityAction, }, conditions: { - entityType, + entityTypes: [entityType], }, }, { @@ -35,7 +35,7 @@ const entityActions: Array = [ api: UmbMoveEntityAction, }, conditions: { - entityType, + entityTypes: [entityType], }, }, { @@ -50,7 +50,7 @@ const entityActions: Array = [ api: UmbCopyEntityAction, }, conditions: { - entityType, + entityTypes: [entityType], }, }, { @@ -65,7 +65,7 @@ const entityActions: Array = [ api: UmbDeleteEntityAction, }, conditions: { - entityType, + entityTypes: [entityType], }, }, { @@ -80,7 +80,7 @@ const entityActions: Array = [ api: UmbReloadMediaTypeEntityAction, }, conditions: { - entityType, + entityTypes: [entityType], }, }, ]; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/media/media-types/repository/media-type.repository.ts b/src/Umbraco.Web.UI.Client/src/backoffice/media/media-types/repository/media-type.repository.ts index 16b649f19b..dda6267aa8 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/media/media-types/repository/media-type.repository.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/media/media-types/repository/media-type.repository.ts @@ -7,8 +7,9 @@ import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller'; import type { MediaTypeDetails } from '@umbraco-cms/backoffice/models'; import { UmbNotificationContext, UMB_NOTIFICATION_CONTEXT_TOKEN } from '@umbraco-cms/backoffice/notification'; import { UmbTreeRepository, UmbTreeDataSource } from '@umbraco-cms/backoffice/repository'; +import { EntityTreeItemResponseModel } from '@umbraco-cms/backoffice/backend-api'; -export class UmbMediaTypeRepository implements UmbTreeRepository { +export class UmbMediaTypeRepository implements UmbTreeRepository { #init!: Promise; #host: UmbControllerHostElement; @@ -43,6 +44,21 @@ export class UmbMediaTypeRepository implements UmbTreeRepository { ]); } + // TREE: + async requestTreeRoot() { + await this.#init; + + const data = { + id: null, + type: 'media-type-root', + name: 'Media Types', + icon: 'umb:folder', + hasChildren: true, + }; + + return { data }; + } + async requestRootTreeItems() { await this.#init; @@ -57,10 +73,7 @@ export class UmbMediaTypeRepository implements UmbTreeRepository { async requestTreeItemsOf(parentId: string | null) { await this.#init; - - if (!parentId) { - throw new Error('Parent id is missing'); - } + if (parentId === undefined) throw new Error('Parent id is missing'); const { data, error } = await this.#treeSource.getChildrenOf(parentId); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/media/media-types/repository/sources/media-type.tree.server.data.ts b/src/Umbraco.Web.UI.Client/src/backoffice/media/media-types/repository/sources/media-type.tree.server.data.ts index f1b9e18602..a362697054 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/media/media-types/repository/sources/media-type.tree.server.data.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/media/media-types/repository/sources/media-type.tree.server.data.ts @@ -37,16 +37,20 @@ export class UmbMediaTypeTreeServerDataSource implements UmbTreeDataSource { * @memberof UmbMediaTypeTreeServerDataSource */ async getChildrenOf(parentId: string | null) { - if (!parentId) { - throw new Error('Parent id is missing'); - } + if (parentId === undefined) throw new Error('Parent id is missing'); - return tryExecuteAndNotify( - this.#host, - MediaTypeResource.getTreeMediaTypeChildren({ - parentId, - }) - ); + /* TODO: should we make getRootItems() internal + so it only is a server concern that there are two endpoints? */ + if (parentId === null) { + return this.getRootItems(); + } else { + return tryExecuteAndNotify( + this.#host, + MediaTypeResource.getTreeMediaTypeChildren({ + parentId, + }) + ); + } } /** diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/media/media-types/tree/manifests.ts b/src/Umbraco.Web.UI.Client/src/backoffice/media/media-types/tree/manifests.ts index fff9c37137..3b77179828 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/media/media-types/tree/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/media/media-types/tree/manifests.ts @@ -16,7 +16,7 @@ const treeItem: ManifestTreeItem = { alias: 'Umb.TreeItem.MediaType', name: 'Media Type Tree Item', conditions: { - entityType: 'media-type', + entityTypes: ['media-type-root', 'media-type'], }, }; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/media/media/entity-actions/manifests.ts b/src/Umbraco.Web.UI.Client/src/backoffice/media/media/entity-actions/manifests.ts index c8b4e44629..8f407cf171 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/media/media/entity-actions/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/media/media/entity-actions/manifests.ts @@ -14,7 +14,7 @@ const entityActions: Array = [ repositoryAlias: MEDIA_REPOSITORY_ALIAS, }, conditions: { - entityType: 'media', + entityTypes: ['media'], }, }, ]; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/media/media/menu-item/media-menu-item.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/media/media/menu-item/media-menu-item.element.ts index 6a4157ece4..c69525c8b6 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/media/media/menu-item/media-menu-item.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/media/media/menu-item/media-menu-item.element.ts @@ -5,7 +5,7 @@ import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; @customElement('umb-media-menu-item') export class UmbMediaMenuItemElement extends UmbLitElement { render() { - return html``; + return html``; } } diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/media/media/modals/media-picker/media-picker-modal.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/media/media/modals/media-picker/media-picker-modal.element.ts index 3cac92aec2..a2f4d1ba4c 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/media/media/modals/media-picker/media-picker-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/media/media/modals/media-picker/media-picker-modal.element.ts @@ -10,10 +10,8 @@ export class UmbMediaPickerModalElement extends UmbModalBaseElement< UmbMediaPickerModalData, UmbMediaPickerModalResult > { - - @state() - _selection: Array = []; + _selection: Array = []; @state() _multiple = true; @@ -27,8 +25,7 @@ export class UmbMediaPickerModalElement extends UmbModalBaseElement< private _handleSelectionChange(e: CustomEvent) { e.stopPropagation(); const element = e.target as UmbTreeElement; - //TODO: Should multiple property be implemented here or be passed down into umb-tree? - this._selection = this._multiple ? element.selection : [element.selection[element.selection.length - 1]]; + this._selection = element.selection; } private _submit() { @@ -49,7 +46,8 @@ export class UmbMediaPickerModalElement extends UmbModalBaseElement< alias="Umb.Tree.Media" @selected=${this._handleSelectionChange} .selection=${this._selection} - selectable> + selectable + ?multiple=${this._multiple}>
diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/media/media/repository/media.repository.ts b/src/Umbraco.Web.UI.Client/src/backoffice/media/media/repository/media.repository.ts index 4440fe2046..45a2899437 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/media/media/repository/media.repository.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/media/media/repository/media.repository.ts @@ -6,14 +6,20 @@ import { UmbMediaDetailServerDataSource } from './sources/media.detail.server.da import type { UmbTreeRepository, UmbTreeDataSource } from '@umbraco-cms/backoffice/repository'; import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller'; import { UmbContextConsumerController } from '@umbraco-cms/backoffice/context-api'; -import { CreateMediaRequestModel, UpdateMediaRequestModel } from '@umbraco-cms/backoffice/backend-api'; +import { + CreateMediaRequestModel, + EntityTreeItemResponseModel, + UpdateMediaRequestModel, +} from '@umbraco-cms/backoffice/backend-api'; import { UmbDetailRepository } from '@umbraco-cms/backoffice/repository'; import { UmbNotificationContext, UMB_NOTIFICATION_CONTEXT_TOKEN } from '@umbraco-cms/backoffice/notification'; type ItemDetailType = MediaDetails; export class UmbMediaRepository - implements UmbTreeRepository, UmbDetailRepository + implements + UmbTreeRepository, + UmbDetailRepository { #host: UmbControllerHostElement; @@ -63,6 +69,21 @@ export class UmbMediaRepository } } + // TREE: + async requestTreeRoot() { + await this.#init; + + const data = { + id: null, + type: 'media-root', + name: 'Media', + icon: 'umb:folder', + hasChildren: true, + }; + + return { data }; + } + async requestRootTreeItems() { await this.#init; @@ -77,10 +98,7 @@ export class UmbMediaRepository async requestTreeItemsOf(parentId: string | null) { await this.#init; - - if (!parentId) { - throw new Error('Parent id is missing'); - } + if (parentId === undefined) throw new Error('Parent id is missing'); const { data, error } = await this.#treeSource.getChildrenOf(parentId); @@ -121,7 +139,7 @@ export class UmbMediaRepository // DETAILS: async createScaffold(parentId: string | null) { - if (!parentId) throw new Error('Parent id is missing'); + if (parentId === undefined) throw new Error('Parent id is missing'); await this.#init; return this.#detailDataSource.createScaffold(parentId); } @@ -220,7 +238,7 @@ export class UmbMediaRepository alert('implement trash'); } - async move(ids: Array, destination: string) { + async move(ids: Array, destination: string | null) { // TODO: use backend cli when available. const res = await fetch('/umbraco/management/api/v1/media/move', { method: 'POST', diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/media/media/repository/sources/media.tree.server.data.ts b/src/Umbraco.Web.UI.Client/src/backoffice/media/media/repository/sources/media.tree.server.data.ts index c13e0effc8..00a80617a0 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/media/media/repository/sources/media.tree.server.data.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/media/media/repository/sources/media.tree.server.data.ts @@ -66,16 +66,20 @@ export class UmbMediaTreeServerDataSource implements UmbTreeDataSource { * @memberof UmbMediaTreeServerDataSource */ async getChildrenOf(parentId: string | null) { - if (!parentId) { - throw new Error('Parent id is missing'); - } + if (parentId === undefined) throw new Error('Parent id is missing'); - return tryExecuteAndNotify( - this.#host, - MediaResource.getTreeMediaChildren({ - parentId, - }) - ); + /* TODO: should we make getRootItems() internal + so it only is a server concern that there are two endpoints? */ + if (parentId === null) { + return this.getRootItems(); + } else { + return tryExecuteAndNotify( + this.#host, + MediaResource.getTreeMediaChildren({ + parentId, + }) + ); + } } /** 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 b66042c3ba..7f6d2e9ee2 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 @@ -18,7 +18,7 @@ const treeItem: ManifestTreeItem = { alias: 'Umb.TreeItem.Media', name: 'Media Tree Item', conditions: { - entityType: 'media', + entityTypes: ['media-root', 'media'], }, }; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/members/member-groups/entity-actions/manifests.ts b/src/Umbraco.Web.UI.Client/src/backoffice/members/member-groups/entity-actions/manifests.ts index 90bd62d4f2..9d6f24660e 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/members/member-groups/entity-actions/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/members/member-groups/entity-actions/manifests.ts @@ -14,7 +14,7 @@ const entityActions: Array = [ repositoryAlias: MEMBER_GROUP_REPOSITORY_ALIAS, }, conditions: { - entityType: 'member-group', + entityTypes: ['member-group'], }, }, ]; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/members/member-groups/repository/member-group.repository.ts b/src/Umbraco.Web.UI.Client/src/backoffice/members/member-groups/repository/member-group.repository.ts index fec1899830..a0c0c3075c 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/members/member-groups/repository/member-group.repository.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/members/member-groups/repository/member-group.repository.ts @@ -7,9 +7,12 @@ import { UmbNotificationContext, UMB_NOTIFICATION_CONTEXT_TOKEN } from '@umbraco import { UmbContextConsumerController } from '@umbraco-cms/backoffice/context-api'; import type { MemberGroupDetails } from '@umbraco-cms/backoffice/models'; import type { UmbTreeDataSource, UmbDetailRepository, UmbTreeRepository } from '@umbraco-cms/backoffice/repository'; +import { EntityTreeItemResponseModel } from '@umbraco-cms/backoffice/backend-api'; // TODO => Update type when backend updated -export class UmbMemberGroupRepository implements UmbTreeRepository, UmbDetailRepository { +export class UmbMemberGroupRepository + implements UmbTreeRepository, UmbDetailRepository +{ #init!: Promise; #host: UmbControllerHostElement; @@ -41,6 +44,21 @@ export class UmbMemberGroupRepository implements UmbTreeRepository, UmbDetailRep }); } + // TREE: + async requestTreeRoot() { + await this.#init; + + const data = { + id: null, + type: 'member-group-root', + name: 'Member Groups', + icon: 'umb:folder', + hasChildren: true, + }; + + return { data }; + } + async requestRootTreeItems() { await this.#init; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/members/member-groups/tree/manifests.ts b/src/Umbraco.Web.UI.Client/src/backoffice/members/member-groups/tree/manifests.ts index 5d379a3782..14b1109cb4 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/members/member-groups/tree/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/members/member-groups/tree/manifests.ts @@ -19,7 +19,7 @@ const treeItem: ManifestTreeItem = { alias: 'Umb.TreeItem.MemberGroup', name: 'Member Group Tree Item', conditions: { - entityType: 'member-group', + entityTypes: ['member-group-root', 'member-group'], }, }; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/members/member-types/entity-actions/manifests.ts b/src/Umbraco.Web.UI.Client/src/backoffice/members/member-types/entity-actions/manifests.ts index db52c2c7a8..4f38d477b6 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/members/member-types/entity-actions/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/members/member-types/entity-actions/manifests.ts @@ -18,7 +18,7 @@ const entityActions: Array = [ api: UmbDeleteEntityAction, }, conditions: { - entityType, + entityTypes: [entityType], }, }, ]; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/members/member-types/repository/member-type.repository.ts b/src/Umbraco.Web.UI.Client/src/backoffice/members/member-types/repository/member-type.repository.ts index 3129798500..44274b0d07 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/members/member-types/repository/member-type.repository.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/members/member-types/repository/member-type.repository.ts @@ -7,10 +7,11 @@ import { UmbContextConsumerController } from '@umbraco-cms/backoffice/context-ap import { UmbTreeDataSource, UmbDetailRepository, UmbTreeRepository } from '@umbraco-cms/backoffice/repository'; import { UmbNotificationContext, UMB_NOTIFICATION_CONTEXT_TOKEN } from '@umbraco-cms/backoffice/notification'; import type { MemberTypeDetails } from '@umbraco-cms/backoffice/models'; +import { EntityTreeItemResponseModel } from '@umbraco-cms/backoffice/backend-api'; // TODO => use correct type when available type ItemType = any; -type TreeItemType = any; +type TreeItemType = EntityTreeItemResponseModel; export class UmbMemberTypeRepository implements UmbTreeRepository, UmbDetailRepository { #init!: Promise; @@ -47,6 +48,21 @@ export class UmbMemberTypeRepository implements UmbTreeRepository, ]); } + // TREE: + async requestTreeRoot() { + await this.#init; + + const data = { + id: null, + type: 'member-type-root', + name: 'Member Types', + icon: 'umb:folder', + hasChildren: true, + }; + + return { data }; + } + async requestRootTreeItems() { await this.#init; @@ -61,10 +77,7 @@ export class UmbMemberTypeRepository implements UmbTreeRepository, async requestTreeItemsOf(parentId: string | null) { await this.#init; - - if (!parentId) { - throw new Error('Parent id is missing'); - } + if (parentId === undefined) throw new Error('Parent id is missing'); const { data, error } = await this.#treeSource.getChildrenOf(parentId); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/members/member-types/repository/sources/member-type.tree.server.data.ts b/src/Umbraco.Web.UI.Client/src/backoffice/members/member-types/repository/sources/member-type.tree.server.data.ts index f5e135b31b..d1395b7e62 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/members/member-types/repository/sources/member-type.tree.server.data.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/members/member-types/repository/sources/member-type.tree.server.data.ts @@ -37,10 +37,7 @@ export class UmbMemberTypeTreeServerDataSource implements UmbTreeDataSource { * @memberof UmbMemberTypeTreeServerDataSource */ async getChildrenOf(parentId: string | null) { - if (!parentId) { - throw new Error('Parent id is missing'); - } - + if (parentId === undefined) throw new Error('Parent id is missing'); return { error: new ApiError({} as any, {} as any, 'Not implemented for Member Type') }; } diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/members/member-types/tree/manifests.ts b/src/Umbraco.Web.UI.Client/src/backoffice/members/member-types/tree/manifests.ts index 6781a419f4..7a23f9a6ef 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/members/member-types/tree/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/members/member-types/tree/manifests.ts @@ -18,7 +18,7 @@ const treeItem: ManifestTreeItem = { alias: 'Umb.TreeItem.MemberType', name: 'Member Type Tree Item', conditions: { - entityType: 'member-type', + entityTypes: ['member-type-root', 'member-type'], }, }; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/members/members/entity-actions/manifests.ts b/src/Umbraco.Web.UI.Client/src/backoffice/members/members/entity-actions/manifests.ts index 9f2f9a28b2..8413fe7d22 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/members/members/entity-actions/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/members/members/entity-actions/manifests.ts @@ -14,7 +14,7 @@ const entityActions: Array = [ repositoryAlias: MEMBER_REPOSITORY_ALIAS, }, conditions: { - entityType: 'member', + entityTypes: ['member'], }, }, ]; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/members/members/repository/member.repository.ts b/src/Umbraco.Web.UI.Client/src/backoffice/members/members/repository/member.repository.ts index 9f20156cc8..ab19046099 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/members/members/repository/member.repository.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/members/members/repository/member.repository.ts @@ -5,7 +5,7 @@ import { UmbNotificationContext, UMB_NOTIFICATION_CONTEXT_TOKEN } from '@umbraco import { UmbContextConsumerController } from '@umbraco-cms/backoffice/context-api'; import type { UmbTreeRepository } from '@umbraco-cms/backoffice/repository'; -export class UmbMemberRepository implements UmbTreeRepository { +export class UmbMemberRepository implements UmbTreeRepository { #host: UmbControllerHostElement; #dataSource: UmbMemberTreeServerDataSource; #treeStore?: UmbMemberTreeStore; @@ -40,6 +40,22 @@ export class UmbMemberRepository implements UmbTreeRepository { } } + // TREE: + async requestTreeRoot() { + await this.#init; + + const data = { + id: null, + parentId: null, + type: 'member-root', + name: 'Members', + icon: 'umb:folder', + hasChildren: true, + }; + + return { data }; + } + async requestRootTreeItems() { await this.#init; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/members/members/tree/manifests.ts b/src/Umbraco.Web.UI.Client/src/backoffice/members/members/tree/manifests.ts index e30a124927..decc69aecf 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/members/members/tree/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/members/members/tree/manifests.ts @@ -17,7 +17,7 @@ const treeItem: ManifestTreeItem = { alias: 'Umb.TreeItem.Member', name: 'Member Tree Item', conditions: { - entityType: 'member', + entityTypes: ['member-root', 'member'], }, }; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/entity-actions/copy/manifests.ts b/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/entity-actions/copy/manifests.ts index 726637162b..a2a35bcb32 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/entity-actions/copy/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/entity-actions/copy/manifests.ts @@ -16,7 +16,7 @@ const entityActions: Array = [ api: UmbCopyDataTypeEntityAction, }, conditions: { - entityType: DATA_TYPE_ENTITY_TYPE, + entityTypes: [DATA_TYPE_ENTITY_TYPE], }, }, ]; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/entity-actions/create/manifests.ts b/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/entity-actions/create/manifests.ts index 4603e3f25f..daf6fc69bd 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/entity-actions/create/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/entity-actions/create/manifests.ts @@ -1,3 +1,4 @@ +import { DATA_TYPE_ENTITY_TYPE, DATA_TYPE_FOLDER_ENTITY_TYPE, DATA_TYPE_ROOT_ENTITY_TYPE } from '../..'; import { DATA_TYPE_REPOSITORY_ALIAS } from '../../repository/manifests'; import { UmbCreateDataTypeEntityAction } from './create.action'; import { ManifestTypes } from '@umbraco-cms/backoffice/extensions-registry'; @@ -10,12 +11,12 @@ const entityActions: Array = [ weight: 1000, meta: { icon: 'umb:add', - label: 'Create', + label: 'Create...', repositoryAlias: DATA_TYPE_REPOSITORY_ALIAS, api: UmbCreateDataTypeEntityAction, }, conditions: { - entityType: 'data-type', + entityTypes: [DATA_TYPE_ENTITY_TYPE, DATA_TYPE_ROOT_ENTITY_TYPE, DATA_TYPE_FOLDER_ENTITY_TYPE], }, }, { diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/entity-actions/manifests.ts b/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/entity-actions/manifests.ts index 64d90c4142..ab03c43bef 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/entity-actions/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/entity-actions/manifests.ts @@ -1,4 +1,4 @@ -import { DATA_TYPE_ENTITY_TYPE } from '..'; +import { DATA_TYPE_FOLDER_ENTITY_TYPE, DATA_TYPE_ENTITY_TYPE } from '..'; import { DATA_TYPE_REPOSITORY_ALIAS } from '../repository/manifests'; import { manifests as createManifests } from './create/manifests'; import { manifests as moveManifests } from './move/manifests'; @@ -24,7 +24,7 @@ const entityActions: Array = [ api: UmbDeleteEntityAction, }, conditions: { - entityType: DATA_TYPE_ENTITY_TYPE, + entityTypes: [DATA_TYPE_ENTITY_TYPE], }, }, { @@ -39,7 +39,7 @@ const entityActions: Array = [ api: UmbDeleteFolderEntityAction, }, conditions: { - entityType: DATA_TYPE_ENTITY_TYPE, + entityTypes: [DATA_TYPE_ENTITY_TYPE, DATA_TYPE_FOLDER_ENTITY_TYPE], }, }, { @@ -54,7 +54,7 @@ const entityActions: Array = [ api: UmbFolderUpdateEntityAction, }, conditions: { - entityType: DATA_TYPE_ENTITY_TYPE, + entityTypes: [DATA_TYPE_ENTITY_TYPE, DATA_TYPE_FOLDER_ENTITY_TYPE], }, }, ]; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/entity-actions/move/manifests.ts b/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/entity-actions/move/manifests.ts index 5e7ff4e7c6..b51a3e6698 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/entity-actions/move/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/entity-actions/move/manifests.ts @@ -16,7 +16,7 @@ const entityActions: Array = [ api: UmbMoveDataTypeEntityAction, }, conditions: { - entityType: DATA_TYPE_ENTITY_TYPE, + entityTypes: [DATA_TYPE_ENTITY_TYPE], }, }, ]; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/index.ts b/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/index.ts index 44cb2b0223..6d3be16e23 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/index.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/index.ts @@ -1,3 +1,5 @@ import './components'; +export const DATA_TYPE_ROOT_ENTITY_TYPE = 'data-type-root'; export const DATA_TYPE_ENTITY_TYPE = 'data-type'; +export const DATA_TYPE_FOLDER_ENTITY_TYPE = 'data-type-folder'; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/modal/data-type-picker/data-type-picker-modal.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/modal/data-type-picker/data-type-picker-modal.element.ts index b72d549d08..1f3f620f78 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/modal/data-type-picker/data-type-picker-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/modal/data-type-picker/data-type-picker-modal.element.ts @@ -12,8 +12,6 @@ import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; // TODO: make use of UmbPickerLayoutBase @customElement('umb-data-type-picker-modal') export class UmbDataTypePickerModalElement extends UmbLitElement { - - @property({ attribute: false }) modalHandler?: UmbModalHandler; @@ -21,7 +19,7 @@ export class UmbDataTypePickerModalElement extends UmbLitElement { data?: UmbDataTypePickerModalData; @state() - _selection: Array = []; + _selection: Array = []; @state() _multiple = false; @@ -64,7 +62,7 @@ export class UmbDataTypePickerModalElement extends UmbLitElement { `; } - + static styles = [UUITextStyles, css``]; } diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/repository/data-type.repository.ts b/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/repository/data-type.repository.ts index 3025554863..17a043968a 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/repository/data-type.repository.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/repository/data-type.repository.ts @@ -1,3 +1,4 @@ +import { DATA_TYPE_ROOT_ENTITY_TYPE } from '..'; import { UmbDataTypeTreeServerDataSource } from './sources/data-type.tree.server.data'; import { UmbDataTypeMoveServerDataSource } from './sources/data-type-move.server.data'; import { UmbDataTypeStore, UMB_DATA_TYPE_STORE_CONTEXT_TOKEN } from './data-type.store'; @@ -14,6 +15,12 @@ import type { UmbFolderRepository, UmbMoveRepository, UmbCopyRepository, + UmbTreeDataSource, + UmbDataSource, + UmbFolderDataSource, + UmbItemDataSource, + UmbMoveDataSource, + UmbCopyDataSource, } from '@umbraco-cms/backoffice/repository'; import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller'; import { UmbContextConsumerController } from '@umbraco-cms/backoffice/context-api'; @@ -31,7 +38,7 @@ export class UmbDataTypeRepository implements UmbItemRepository, UmbTreeRepository, - UmbDetailRepository, + UmbDetailRepository, UmbFolderRepository, UmbMoveRepository, UmbCopyRepository @@ -40,12 +47,12 @@ export class UmbDataTypeRepository #host: UmbControllerHostElement; - #treeSource: UmbDataTypeTreeServerDataSource; - #detailSource: UmbDataTypeServerDataSource; - #folderSource: UmbDataTypeFolderServerDataSource; - #itemSource: UmbDataTypeItemServerDataSource; - #moveSource: UmbDataTypeMoveServerDataSource; - #copySource: UmbDataTypeCopyServerDataSource; + #treeSource: UmbTreeDataSource; + #detailSource: UmbDataSource; + #folderSource: UmbFolderDataSource; + #itemSource: UmbItemDataSource; + #moveSource: UmbMoveDataSource; + #copySource: UmbCopyDataSource; #detailStore?: UmbDataTypeStore; #treeStore?: UmbDataTypeTreeStore; @@ -84,6 +91,20 @@ export class UmbDataTypeRepository } // TREE: + async requestTreeRoot() { + await this.#init; + + const data = { + id: null, + type: DATA_TYPE_ROOT_ENTITY_TYPE, + name: 'Data Types', + icon: 'umb:folder', + hasChildren: true, + }; + + return { data }; + } + async requestRootTreeItems() { await this.#init; @@ -97,8 +118,8 @@ export class UmbDataTypeRepository } async requestTreeItemsOf(parentId: string | null) { - if (!parentId) throw new Error('Parent id is missing'); await this.#init; + if (parentId === undefined) throw new Error('Parent id is missing'); const { data, error } = await this.#treeSource.getChildrenOf(parentId); @@ -286,14 +307,17 @@ export class UmbDataTypeRepository } // Actions - async move(id: string, targetId: string) { + async move(id: string, targetId: string | null) { await this.#init; const { error } = await this.#moveSource.move(id, targetId); if (!error) { // TODO: Be aware about this responsibility. this.#treeStore?.updateItem(id, { parentId: targetId }); - this.#treeStore?.updateItem(targetId, { hasChildren: true }); + // only update the target if its not the root + if (targetId) { + this.#treeStore?.updateItem(targetId, { hasChildren: true }); + } const notification = { data: { message: `Data type moved` } }; this.#notificationContext?.peek('positive', notification); @@ -302,7 +326,7 @@ export class UmbDataTypeRepository return { error }; } - async copy(id: string, targetId: string) { + async copy(id: string, targetId: string | null) { await this.#init; const { data: dataTypeCopyId, error } = await this.#copySource.copy(id, targetId); if (error) return { error }; @@ -310,8 +334,13 @@ export class UmbDataTypeRepository if (dataTypeCopyId) { const { data: dataTypeCopy } = await this.requestById(dataTypeCopyId); if (!dataTypeCopy) throw new Error('Could not find copied data type'); + + // TODO: Be aware about this responsibility. this.#treeStore?.appendItems([dataTypeCopy]); - this.#treeStore?.updateItem(targetId, { hasChildren: true }); + // only update the target if its not the root + if (targetId) { + this.#treeStore?.updateItem(targetId, { hasChildren: true }); + } const notification = { data: { message: `Data type copied` } }; this.#notificationContext?.peek('positive', notification); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/repository/sources/data-type-copy.server.data.ts b/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/repository/sources/data-type-copy.server.data.ts index d322063e48..4cf0be1784 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/repository/sources/data-type-copy.server.data.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/repository/sources/data-type-copy.server.data.ts @@ -26,9 +26,9 @@ export class UmbDataTypeCopyServerDataSource implements UmbCopyDataSource { * @return {*} * @memberof UmbDataTypeCopyServerDataSource */ - async copy(id: string, targetId: string) { + async copy(id: string, targetId: string | null) { if (!id) throw new Error('Id is missing'); - if (!targetId) throw new Error('Target Id is missing'); + if (targetId === undefined) throw new Error('Target Id is missing'); return tryExecuteAndNotify( this.#host, diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/repository/sources/data-type-move.server.data.ts b/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/repository/sources/data-type-move.server.data.ts index c6759d7676..5f564079db 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/repository/sources/data-type-move.server.data.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/repository/sources/data-type-move.server.data.ts @@ -22,13 +22,14 @@ export class UmbDataTypeMoveServerDataSource implements UmbMoveDataSource { /** * Move an item for the given id to the target id - * @param {Array} id + * @param {string} id + * @param {(string | null)} targetId * @return {*} * @memberof UmbDataTypeMoveServerDataSource */ - async move(id: string, targetId: string) { + async move(id: string, targetId: string | null) { if (!id) throw new Error('Id is missing'); - if (!targetId) throw new Error('Target Id is missing'); + if (targetId === undefined) throw new Error('Target Id is missing'); return tryExecuteAndNotify( this.#host, diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/repository/sources/data-type.tree.server.data.ts b/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/repository/sources/data-type.tree.server.data.ts index f1da0afaa9..9fe7b5ea13 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/repository/sources/data-type.tree.server.data.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/repository/sources/data-type.tree.server.data.ts @@ -32,19 +32,25 @@ export class UmbDataTypeTreeServerDataSource implements UmbTreeDataSource { /** * Fetches the children of a given parent id from the server - * @param {(string | null)} parentId + * @param {(string)} parentId * @return {*} * @memberof UmbDataTypeTreeServerDataSource */ async getChildrenOf(parentId: string | null) { - if (!parentId) throw new Error('Parent id is missing'); + if (parentId === undefined) throw new Error('Parent id is missing'); - return tryExecuteAndNotify( - this.#host, - DataTypeResource.getTreeDataTypeChildren({ - parentId, - }) - ); + /* TODO: should we make getRootItems() internal + so it only is a server concern that there are two endpoints? */ + if (parentId === null) { + return this.getRootItems(); + } else { + return tryExecuteAndNotify( + this.#host, + DataTypeResource.getTreeDataTypeChildren({ + parentId, + }) + ); + } } /** diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/tree/manifests.ts b/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/tree/manifests.ts index 49c0a602a4..c75132e6d1 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/tree/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/tree/manifests.ts @@ -16,7 +16,7 @@ const treeItem: ManifestTreeItem = { alias: 'Umb.TreeItem.DataType', name: 'Data Type Tree Item', conditions: { - entityType: 'data-type', + entityTypes: ['data-type-root', 'data-type'], }, }; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/types.ts b/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/types.ts new file mode 100644 index 0000000000..636a67cc81 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/types.ts @@ -0,0 +1,7 @@ +// Temp file for data type types + +import { DataTypeResponseModel } from '@umbraco-cms/backoffice/backend-api'; + +export interface UmbDataTypeModel extends Omit { + type: 'data-type' | 'data-type-folder' | 'data-type-root'; +} diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/settings/languages/entity-actions/manifests.ts b/src/Umbraco.Web.UI.Client/src/backoffice/settings/languages/entity-actions/manifests.ts index c917cfb11b..f93dc19f63 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/settings/languages/entity-actions/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/settings/languages/entity-actions/manifests.ts @@ -16,7 +16,7 @@ const entityActions: Array = [ api: UmbDeleteEntityAction, }, conditions: { - entityType, + entityTypes: [entityType], }, }, ]; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/settings/relation-types/repository/relation-type.repository.ts b/src/Umbraco.Web.UI.Client/src/backoffice/settings/relation-types/repository/relation-type.repository.ts index 8433f6c883..83da40eb18 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/settings/relation-types/repository/relation-type.repository.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/settings/relation-types/repository/relation-type.repository.ts @@ -16,7 +16,7 @@ import { UmbNotificationContext, UMB_NOTIFICATION_CONTEXT_TOKEN } from '@umbraco export class UmbRelationTypeRepository implements UmbTreeRepository, - UmbDetailRepository + UmbDetailRepository { #init!: Promise; @@ -55,6 +55,21 @@ export class UmbRelationTypeRepository // TODO: Trash // TODO: Move + // TREE: + async requestTreeRoot() { + await this.#init; + + const data = { + id: null, + type: 'relation-type-root', + name: 'Relation Types', + icon: 'umb:folder', + hasChildren: true, + }; + + return { data }; + } + async requestRootTreeItems() { await this.#init; @@ -99,12 +114,8 @@ export class UmbRelationTypeRepository // DETAILS: async createScaffold(parentId: string | null) { + if (parentId === undefined) throw new Error('Parent id is missing'); await this.#init; - - if (!parentId) { - throw new Error('Parent id is missing'); - } - return this.#detailDataSource.createScaffold(parentId); } diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/settings/relation-types/tree/manifests.ts b/src/Umbraco.Web.UI.Client/src/backoffice/settings/relation-types/tree/manifests.ts index 42dc1f26cf..4c2ae6d48c 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/settings/relation-types/tree/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/settings/relation-types/tree/manifests.ts @@ -16,7 +16,7 @@ const treeItem: ManifestTreeItem = { alias: 'Umb.TreeItem.RelationType', name: 'Relation Type Tree Item', conditions: { - entityType: 'relation-type', + entityTypes: ['relation-type-root', 'relation-type'], }, }; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/entity-action/entity-action-list.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/entity-action/entity-action-list.element.ts index 3ea2640821..bdea077ecc 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/entity-action/entity-action-list.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/entity-action/entity-action-list.element.ts @@ -32,7 +32,7 @@ class UmbEntityActionListElement extends UmbLitElement { this.observe( umbExtensionsRegistry.extensionsOfType('entityAction').pipe( map((extensions) => { - return extensions.filter((extension) => extension.conditions.entityType === this.entityType); + return extensions.filter((extension) => extension.conditions.entityTypes.includes(this.entityType)); }) ), (actions) => { diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/input-template/input-template.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/input-template/input-template.element.ts index 4fd0164550..89dcd5fd64 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/input-template/input-template.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/input-template/input-template.element.ts @@ -51,12 +51,12 @@ export class UmbInputTemplateElement extends FormControlMixin(UmbLitElement) { @property({ type: String, attribute: 'min-message' }) maxMessage = 'This field exceeds the allowed amount of items'; - _selectedIds: Array = []; - @property({ type: Array }) + _selectedIds: Array = []; + @property({ type: Array }) public get selectedIds() { return this._selectedIds; } - public set selectedIds(newKeys: Array) { + public set selectedIds(newKeys: Array) { this._selectedIds = newKeys; this.#observePickedTemplates(); } diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/menu/menu-item-base/menu-item-base.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/menu/menu-item-base/menu-item-base.element.ts index 62d89eb5a1..ccb44351e5 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/menu/menu-item-base/menu-item-base.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/menu/menu-item-base/menu-item-base.element.ts @@ -15,8 +15,6 @@ import { UmbObserverController } from '@umbraco-cms/backoffice/observable-api'; @customElement('umb-menu-item-base') export class UmbMenuItemBaseElement extends UmbLitElement { - - private _entityType?: string; @property({ type: String, attribute: 'entity-type' }) public get entityType() { @@ -65,7 +63,7 @@ export class UmbMenuItemBaseElement extends UmbLitElement { this.#actionObserver = this.observe( umbExtensionsRegistry .extensionsOfType('entityAction') - .pipe(map((actions) => actions.filter((action) => action.conditions.entityType === this.entityType))), + .pipe(map((actions) => actions.filter((action) => action.conditions.entityTypes.includes(this.entityType!)))), (actions) => { this._hasActions = actions.length > 0; }, @@ -116,7 +114,7 @@ export class UmbMenuItemBaseElement extends UmbLitElement { : nothing} `; } - + static styles = [UUITextStyles, css``]; } diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section-sidebar-context-menu/section-sidebar-context-menu.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section-sidebar-context-menu/section-sidebar-context-menu.element.ts index ba649a06bb..fff6c340a3 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section-sidebar-context-menu/section-sidebar-context-menu.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section-sidebar-context-menu/section-sidebar-context-menu.element.ts @@ -10,8 +10,6 @@ import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; @customElement('umb-section-sidebar-context-menu') export class UmbSectionSidebarContextMenuElement extends UmbLitElement { - - #sectionSidebarContext?: UmbSectionSidebarContext; @state() @@ -21,7 +19,7 @@ export class UmbSectionSidebarContextMenuElement extends UmbLitElement { private _entityType?: string; @state() - private _unique?: string; + private _unique?: string | null; @state() private _headline?: string; @@ -76,7 +74,7 @@ export class UmbSectionSidebarContextMenuElement extends UmbLitElement {
` : nothing; } - + static styles = [ UUITextStyles, css` diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section-sidebar/section-sidebar.context.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section-sidebar/section-sidebar.context.ts index 8185a4f502..8bebb7f272 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section-sidebar/section-sidebar.context.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section-sidebar/section-sidebar.context.ts @@ -10,7 +10,7 @@ export class UmbSectionSidebarContext { #entityType = new UmbStringState(undefined); entityType = this.#entityType.asObservable(); - #unique = new UmbStringState(undefined); + #unique = new UmbStringState(undefined); unique = this.#unique.asObservable(); #headline = new UmbStringState(undefined); @@ -20,14 +20,14 @@ export class UmbSectionSidebarContext { this.#host = host; } - toggleContextMenu(entityType: string, unique: string | undefined, headline: string) { + toggleContextMenu(entityType: string, unique: string | null | undefined, headline: string) { console.log('open for ', entityType, unique, headline); this.openContextMenu(entityType, unique, headline); } // TODO: we wont get notified about tree item name changes because we don't have a subscription // we need to figure out how we best can handle this when we only know the entity and unique id - openContextMenu(entityType: string, unique: string | undefined, headline: string) { + openContextMenu(entityType: string, unique: string | null | undefined, headline: string) { this.#entityType.next(entityType); this.#unique.next(unique); this.#headline.next(headline); 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 9c81962047..3ae5a626d6 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 @@ -23,16 +23,18 @@ 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 type UmbTreeItemUniqueFunction = ( + x: TreeItemType +) => string | null | undefined; -export class UmbTreeItemContextBase - implements UmbTreeItemContext +export class UmbTreeItemContextBase + implements UmbTreeItemContext { public host: UmbControllerHostElement; - public unique?: string; + public unique?: string | null; public type?: string; - #treeItem = new UmbDeepState(undefined); + #treeItem = new UmbDeepState(undefined); treeItem = this.#treeItem.asObservable(); #hasChildren = new UmbBooleanState(false); @@ -56,27 +58,28 @@ export class UmbTreeItemContextBase; #sectionContext?: UmbSectionContext; #sectionSidebarContext?: UmbSectionSidebarContext; - #getUniqueFunction: UmbTreeItemUniqueFunction; + #getUniqueFunction: UmbTreeItemUniqueFunction; #actionObserver?: UmbObserverController; - constructor(host: UmbControllerHostElement, getUniqueFunction: UmbTreeItemUniqueFunction) { + constructor(host: UmbControllerHostElement, getUniqueFunction: UmbTreeItemUniqueFunction) { this.host = host; this.#getUniqueFunction = getUniqueFunction; this.#consumeContexts(); new UmbContextProviderController(host, UMB_TREE_ITEM_CONTEXT_TOKEN, this); } - public setTreeItem(treeItem: T | undefined) { + public setTreeItem(treeItem: TreeItemType | undefined) { if (!treeItem) { this.#treeItem.next(undefined); return; } const unique = this.#getUniqueFunction(treeItem); - if (!unique) throw new Error('Could not create tree item context, unique key is missing'); + // Only check for undefined. The tree root has null as unique + if (unique === undefined) throw new Error('Could not create tree item context, unique key is missing'); this.unique = unique; if (!treeItem.type) throw new Error('Could not create tree item context, tree item type is missing'); @@ -88,7 +91,7 @@ export class UmbTreeItemContextBase { + new UmbContextConsumerController(this.host, 'umbTreeContext', (treeContext: UmbTreeContextBase) => { this.treeContext = treeContext; this.#observeIsSelectable(); this.#observeIsSelected(); @@ -143,7 +146,7 @@ export class UmbTreeItemContextBase { if (!pathname) return; if (!this.type) throw new Error('Cant construct path, entity type is missing'); - if (!this.unique) throw new Error('Cant construct path, unique is missing'); - + if (this.unique === undefined) throw new Error('Cant construct path, unique is missing'); const path = this.constructPath(pathname, this.type, this.unique); this.#path.next(path); }); @@ -174,7 +176,7 @@ export class UmbTreeItemContextBase actions.filter((action) => action.conditions.entityType === this.type))), + .pipe(map((actions) => actions.filter((action) => action.conditions.entityTypes.includes(this.type!)))), (actions) => { this.#hasActions.next(actions.length > 0); } @@ -182,9 +184,9 @@ export class UmbTreeItemContextBase('UmbTreeItemContext'); +export const UMB_TREE_ITEM_CONTEXT_TOKEN = new UmbContextToken>('UmbTreeItemContext'); 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 1337e52ae1..ec0c2f70c5 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 @@ -10,8 +10,6 @@ import { TreeItemPresentationModel } from '@umbraco-cms/backoffice/backend-api'; @customElement('umb-tree-item-base') export class UmbTreeItemBaseElement extends UmbLitElement { - - @state() private _item?: TreeItemPresentationModel; @@ -39,7 +37,7 @@ export class UmbTreeItemBaseElement extends UmbLitElement { @state() private _iconSlotHasChildren = false; - #treeItemContext?: UmbTreeItemContext; + #treeItemContext?: UmbTreeItemContext; constructor() { super(); @@ -159,7 +157,7 @@ export class UmbTreeItemBaseElement extends UmbLitElement { : ''} `; } - + static styles = [UUITextStyles, css``]; } diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/tree/tree-item.context.interface.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/tree/tree-item.context.interface.ts index 7809c8ad23..76d0211f29 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/tree/tree-item.context.interface.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/tree/tree-item.context.interface.ts @@ -1,19 +1,13 @@ import type { Observable } from 'rxjs'; import type { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller'; import type { ProblemDetailsModel, TreeItemPresentationModel } from '@umbraco-cms/backoffice/backend-api'; +import { UmbPagedData } from '@umbraco-cms/backoffice/repository'; -// TODO: temp type. Add paged response type to the repository interface -interface PagedResponse { - total: number; - items: Array; -} - -export interface UmbTreeItemContext { +export interface UmbTreeItemContext { host: UmbControllerHostElement; - unique?: string; + unique?: string | null; type?: string; - - treeItem: Observable; + treeItem: Observable; hasChildren: Observable; isLoading: Observable; isSelectable: Observable; @@ -22,11 +16,11 @@ export interface UmbTreeItemContext; path: Observable; - setTreeItem(treeItem: T | undefined): void; + setTreeItem(treeItem: TreeItemType | undefined): void; requestChildren(): Promise<{ - data: PagedResponse | undefined; - error: ProblemDetailsModel | undefined; - asObservable?: () => Observable; + data?: UmbPagedData | undefined; + error?: ProblemDetailsModel | undefined; + asObservable?: () => Observable; }>; toggleContextMenu(): void; select(): void; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/tree/tree-item/tree-item.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/tree/tree-item/tree-item.element.ts index c7e981c62f..4dd9b27a6f 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/tree/tree-item/tree-item.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/tree/tree-item/tree-item.element.ts @@ -7,8 +7,6 @@ import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; @customElement('umb-tree-item') export class UmbTreeItemElement extends UmbLitElement { - - @property({ type: Object, attribute: false }) item?: TreeItemPresentationModel; @@ -16,13 +14,20 @@ export class UmbTreeItemElement extends UmbLitElement { if (!this.item) return nothing; return html` manifests.conditions.entityType === this.item?.type} + .filter=${(manifests: ManifestTreeItem) => manifests.conditions.entityTypes.includes(this.item!.type!)} .props=${{ item: this.item, }}>`; } - - static styles = [UUITextStyles, css``]; + + static styles = [ + UUITextStyles, + css` + :host { + display: block; + } + `, + ]; } declare global { diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/tree/tree-menu-item/tree-menu-item.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/tree/tree-menu-item/tree-menu-item.element.ts index 39d2a9ea28..ddb0a7a1cd 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/tree/tree-menu-item/tree-menu-item.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/tree/tree-menu-item/tree-menu-item.element.ts @@ -1,7 +1,5 @@ import { html, nothing } from 'lit'; import { customElement, property, state } from 'lit/decorators.js'; -import { ifDefined } from 'lit/directives/if-defined.js'; -import { UUIMenuItemEvent } from '@umbraco-ui/uui'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extensions-api'; import { @@ -25,38 +23,11 @@ umbExtensionsRegistry.register(manifest); @customElement('umb-menu-item-tree') export class UmbMenuItemTreeElement extends UmbLitElement implements UmbMenuItemExtensionElement { - @state() - private _renderTree = false; - - private _onShowChildren(event: UUIMenuItemEvent) { - event.stopPropagation(); - this._renderTree = true; - } - - private _onHideChildren(event: UUIMenuItemEvent) { - event.stopPropagation(); - this._renderTree = false; - } - @property({ type: Object }) manifest?: ManifestMenuItemTreeKind; - // TODO: check if root has children before settings the has-children attribute - // TODO: how do we want to cache the tree? (do we want to rerender every time the user opens the tree)? render() { - return this.manifest - ? html` - - ${this._renderTree ? html`` : nothing} - - ` - : ''; + return this.manifest ? html` ` : nothing; } } diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/tree/tree.context.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/tree/tree.context.ts index cc720084d3..f9102ef0bd 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/tree/tree.context.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/tree/tree.context.ts @@ -1,23 +1,32 @@ -import type { Observable } from 'rxjs'; -import { UmbTreeRepository } from '@umbraco-cms/backoffice/repository'; +import { Observable, map } from 'rxjs'; +import { UmbPagedData, UmbTreeRepository } from '@umbraco-cms/backoffice/repository'; import type { ManifestTree } from '@umbraco-cms/backoffice/extensions-registry'; import { UmbBooleanState, UmbArrayState, UmbObserverController } from '@umbraco-cms/backoffice/observable-api'; import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller'; import { createExtensionClass, umbExtensionsRegistry } from '@umbraco-cms/backoffice/extensions-api'; +import { ProblemDetailsModel, TreeItemPresentationModel } from '@umbraco-cms/backoffice/backend-api'; +import { UmbContextProviderController } from '@umbraco-cms/backoffice/context-api'; -export interface UmbTreeContext { - tree: ManifestTree; +// TODO: update interface +export interface UmbTreeContext { readonly selectable: Observable; - readonly selection: Observable>; + readonly selection: Observable>; setSelectable(value: boolean): void; setMultiple(value: boolean): void; - setSelection(value: Array): void; - select(id: string): void; + setSelection(value: Array): void; + select(unique: string | null): void; + deselect(unique: string | null): void; + requestChildrenOf: (parentUnique: string | null) => Promise<{ + data?: UmbPagedData; + error?: ProblemDetailsModel; + asObservable?: () => Observable; + }>; } -export class UmbTreeContextBase implements UmbTreeContext { - host: UmbControllerHostElement; - public tree: ManifestTree; +export class UmbTreeContextBase + implements UmbTreeContext +{ + public host: UmbControllerHostElement; #selectable = new UmbBooleanState(false); public readonly selectable = this.#selectable.asObservable(); @@ -25,10 +34,13 @@ export class UmbTreeContextBase implements UmbTreeContext { #multiple = new UmbBooleanState(false); public readonly multiple = this.#multiple.asObservable(); - #selection = new UmbArrayState(>[]); + #selection = new UmbArrayState(>[]); public readonly selection = this.#selection.asObservable(); - repository?: UmbTreeRepository; + #treeAlias?: string; + repository?: UmbTreeRepository; + + #treeManifestObserver?: UmbObserverController; #initResolver?: () => void; #initialized = false; @@ -37,28 +49,9 @@ export class UmbTreeContextBase implements UmbTreeContext { this.#initialized ? resolve() : (this.#initResolver = resolve); }); - constructor(host: UmbControllerHostElement, tree: ManifestTree) { + constructor(host: UmbControllerHostElement) { this.host = host; - this.tree = tree; - - const repositoryAlias = this.tree.meta.repositoryAlias; - if (!repositoryAlias) throw new Error('Tree must have a repository alias.'); - - new UmbObserverController( - this.host, - umbExtensionsRegistry.getByTypeAndAlias('repository', this.tree.meta.repositoryAlias), - async (repositoryManifest) => { - if (!repositoryManifest) return; - - try { - const result = await createExtensionClass(repositoryManifest, [this.host]); - this.repository = result; - this.#checkIfInitialized(); - } catch (error) { - throw new Error('Could not create repository with alias: ' + repositoryAlias + ''); - } - } - ); + new UmbContextProviderController(host, 'umbTreeContext', this); } // TODO: find a generic way to do this @@ -69,6 +62,19 @@ export class UmbTreeContextBase implements UmbTreeContext { } } + public async setTreeAlias(treeAlias?: string) { + if (this.#treeAlias === treeAlias) return; + this.#treeAlias = treeAlias; + + if (treeAlias) { + this.#observeTreeManifest(); + } + } + + public getTreeAlias() { + return this.#treeAlias; + } + public setSelectable(value: boolean) { this.#selectable.next(value); } @@ -85,7 +91,7 @@ export class UmbTreeContextBase implements UmbTreeContext { return this.#multiple.getValue(); } - public setSelection(value: Array) { + public setSelection(value: Array) { if (!value) return; this.#selection.next(value); } @@ -94,15 +100,22 @@ export class UmbTreeContextBase implements UmbTreeContext { return this.#selection.getValue(); } - public select(id: string) { + public select(unique: string | null) { if (!this.getSelectable()) return; - const newSelection = this.getMultiple() ? [...this.getSelection(), id] : [id]; + const newSelection = this.getMultiple() ? [...this.getSelection(), unique] : [unique]; this.#selection.next(newSelection); + this.host.dispatchEvent(new CustomEvent('selected')); } - public deselect(id: string) { - const newSelection = this.getSelection().filter((x) => x !== id); + public deselect(unique: string | null) { + const newSelection = this.getSelection().filter((x) => x !== unique); this.#selection.next(newSelection); + this.host.dispatchEvent(new CustomEvent('selected')); + } + + public async requestTreeRoot() { + await this.#init; + return this.repository!.requestTreeRoot(); } public async requestRootItems() { @@ -110,9 +123,10 @@ export class UmbTreeContextBase implements UmbTreeContext { return this.repository!.requestRootTreeItems(); } - public async requestChildrenOf(parentId: string | null) { + public async requestChildrenOf(parentUnique: string | null) { await this.#init; - return this.repository!.requestTreeItemsOf(parentId); + if (parentUnique === undefined) throw new Error('Parent unique cannot be undefined.'); + return this.repository!.requestTreeItemsOf(parentUnique); } public async rootItems() { @@ -120,8 +134,44 @@ export class UmbTreeContextBase implements UmbTreeContext { return this.repository!.rootTreeItems(); } - public async childrenOf(parentId: string | null) { + public async childrenOf(parentUnique: string | null) { await this.#init; - return this.repository!.treeItemsOf(parentId); + return this.repository!.treeItemsOf(parentUnique); + } + + #observeTreeManifest() { + this.#treeManifestObserver?.destroy(); + + this.#treeManifestObserver = new UmbObserverController( + this.host, + umbExtensionsRegistry + .extensionsOfType('tree') + .pipe(map((treeManifests) => treeManifests.find((treeManifest) => treeManifest.alias === this.#treeAlias))), + async (treeManifest) => { + if (!treeManifest) return; + this.#observeRepository(treeManifest); + } + ); + } + + #observeRepository(treeManifest: ManifestTree) { + const repositoryAlias = treeManifest.meta.repositoryAlias; + if (!repositoryAlias) throw new Error('Tree must have a repository alias.'); + + new UmbObserverController( + this.host, + umbExtensionsRegistry.getByTypeAndAlias('repository', treeManifest.meta.repositoryAlias), + async (repositoryManifest) => { + if (!repositoryManifest) return; + + try { + const result = await createExtensionClass>(repositoryManifest, [this.host]); + this.repository = result; + this.#checkIfInitialized(); + } catch (error) { + throw new Error('Could not create repository with alias: ' + repositoryAlias + ''); + } + } + ); } } diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/tree/tree.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/tree/tree.element.ts index e50d1e7930..304fc8d38e 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/tree/tree.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/tree/tree.element.ts @@ -1,12 +1,10 @@ -import { html } from 'lit'; +import { html, nothing } from 'lit'; import { customElement, property, state } from 'lit/decorators.js'; -import { map } from 'rxjs'; import { repeat } from 'lit/directives/repeat.js'; import { UmbTreeContextBase } from './tree.context'; -import type { ManifestTree } from '@umbraco-cms/backoffice/extensions-registry'; -import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extensions-api'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; -import { EntityTreeItemResponseModel } from '@umbraco-cms/backoffice/backend-api'; +import { TreeItemPresentationModel } from '@umbraco-cms/backoffice/backend-api'; +import { UmbObserverController } from '@umbraco-cms/backoffice/observable-api'; import './tree-item/tree-item.element'; import './tree-item-base/tree-item-base.element'; @@ -15,122 +13,104 @@ import './context-menu/tree-context-menu.service'; @customElement('umb-tree') export class UmbTreeElement extends UmbLitElement { - private _alias = ''; @property({ type: String, reflect: true }) get alias() { - return this._alias; + return this.#treeContext.getTreeAlias(); } set alias(newVal) { - const oldVal = this._alias; - this._alias = newVal; - this.requestUpdate('alias', oldVal); - this._observeTree(); + this.#treeContext.setTreeAlias(newVal); } - private _selectable = false; @property({ type: Boolean, reflect: true }) get selectable() { - return this._selectable; + return this.#treeContext.getSelectable(); } set selectable(newVal) { - const oldVal = this._selectable; - this._selectable = newVal; - this.requestUpdate('selectable', oldVal); - this._treeContext?.setSelectable(newVal); + this.#treeContext.setSelectable(newVal); } - private _selection: Array = []; @property({ type: Array }) get selection() { - return this._selection; + return this.#treeContext.getSelection(); } - set selection(newVal: Array) { - const oldVal = this._selection; - this._selection = newVal; - this.requestUpdate('selection', oldVal); - this._treeContext?.setSelection(newVal); + set selection(newVal) { + this.#treeContext?.setSelection(newVal); } - private _multiple = false; @property({ type: Boolean, reflect: true }) get multiple() { - return this._multiple; + return this.#treeContext.getMultiple(); } set multiple(newVal) { - const oldVal = this._multiple; - this._multiple = newVal; - this.requestUpdate('multiple', oldVal); - this._treeContext?.setMultiple(newVal); + this.#treeContext.setMultiple(newVal); + } + + // TODO: what is the best name for this functionatliy? + private _hideTreeRoot = false; + @property({ type: Boolean, attribute: 'hide-tree-root' }) + get hideTreeRoot() { + return this._hideTreeRoot; + } + set hideTreeRoot(newVal: boolean) { + const oldVal = this._hideTreeRoot; + this._hideTreeRoot = newVal; + if (newVal === true) { + this.#observeRootItems(); + } + + this.requestUpdate('hideTreeRoot', oldVal); } @state() - private _tree?: ManifestTree; + private _items: TreeItemPresentationModel[] = []; @state() - private _items: EntityTreeItemResponseModel[] = []; + private _treeRoot?: TreeItemPresentationModel; - private _treeContext?: UmbTreeContextBase; + #treeContext = new UmbTreeContextBase(this); - protected firstUpdated(): void { - this._observeTree(); + #rootItemsObserver?: UmbObserverController>; + + connectedCallback(): void { + super.connectedCallback(); + this.#requestTreeRoot(); } - private _observeTree() { - if (!this.alias) return; + async #requestTreeRoot() { + if (!this.#treeContext?.requestTreeRoot) throw new Error('Tree does not support root'); - this.observe( - umbExtensionsRegistry - .extensionsOfType('tree') - .pipe(map((trees) => trees.find((tree) => tree.alias === this.alias))), - async (tree) => { - if (this._tree?.alias === tree?.alias) return; - - this._tree = tree; - this.#provideTreeContext(); - } - ); + const { data } = await this.#treeContext.requestTreeRoot(); + this._treeRoot = data; } - #provideTreeContext() { - if (!this._tree || this._treeContext) return; + async #observeRootItems() { + if (!this.#treeContext?.requestRootItems) throw new Error('Tree does not support root items'); + this.#rootItemsObserver?.destroy(); - // TODO: if a new tree comes around, which is different, then we should clean up and re provide. - this._treeContext = new UmbTreeContextBase(this, this._tree); - this._treeContext.setSelectable(this.selectable); - this._treeContext.setSelection(this.selection); - this._treeContext.setMultiple(this.multiple); + const { asObservable } = await this.#treeContext.requestRootItems(); - this.#observeSelection(); - this.#observeTreeRoot(); - - this.provideContext('umbTreeContext', this._treeContext); - } - - async #observeTreeRoot() { - if (!this._treeContext?.requestRootItems) return; - - this._treeContext.requestRootItems(); - - this.observe(await this._treeContext.rootItems(), (rootItems) => { - this._items = rootItems; - }); - } - - #observeSelection() { - if (!this._treeContext) return; - - this.observe(this._treeContext.selection, (selection) => { - if (this._selection === selection) return; - this._selection = selection; - this.dispatchEvent(new CustomEvent('selected')); - }); + if (asObservable) { + this.#rootItemsObserver = this.observe(asObservable(), (rootItems) => { + this._items = rootItems; + this.requestUpdate(); + }); + } } render() { + return html` ${this.#renderTreeRoot()} ${this.#renderRootItems()}`; + } + + #renderTreeRoot() { + if (this.hideTreeRoot || this._treeRoot === undefined) return nothing; + return html` `; + } + + #renderRootItems() { + if (this._items?.length === 0) return nothing; return html` ${repeat( this._items, - // TODO: add getUnique to a repository interface (item, index) => index, (item) => html`` )} diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/modals/link-picker/link-picker-modal.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/modals/link-picker/link-picker-modal.element.ts index df4510682f..a9d87472ff 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/modals/link-picker/link-picker-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/modals/link-picker/link-picker-modal.element.ts @@ -14,8 +14,6 @@ import { buildUdi, getKeyFromUdi } from '@umbraco-cms/backoffice/utils'; @customElement('umb-link-picker-modal') export class UmbLinkPickerModalElement extends UmbModalBaseElement { - - @state() _selectedKey?: string; @@ -73,6 +71,8 @@ export class UmbLinkPickerModalElement extends UmbModalBaseElement { @state() - _selection: Array = []; + _selection: Array = []; @state() _multiple = true; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/templating/components/file-system-tree-item/file-system-tree-item.context.ts b/src/Umbraco.Web.UI.Client/src/backoffice/templating/components/file-system-tree-item/file-system-tree-item.context.ts index 670d5d6be4..8427d1929c 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/templating/components/file-system-tree-item/file-system-tree-item.context.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/templating/components/file-system-tree-item/file-system-tree-item.context.ts @@ -3,7 +3,6 @@ import { urlFriendlyPathFromServerFilePath } from '../../utils'; import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller'; import { FileSystemTreeItemPresentationModel } from '@umbraco-cms/backoffice/backend-api'; -// TODO get unique method from an entity repository static method export class UmbFileSystemTreeItemContext extends UmbTreeItemContextBase { constructor(host: UmbControllerHostElement) { super(host, (x: FileSystemTreeItemPresentationModel) => x.path); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/templating/stylesheets/repository/sources/stylesheet.tree.server.data.ts b/src/Umbraco.Web.UI.Client/src/backoffice/templating/stylesheets/repository/sources/stylesheet.tree.server.data.ts index 5b7674f6df..ad3f393974 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/templating/stylesheets/repository/sources/stylesheet.tree.server.data.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/templating/stylesheets/repository/sources/stylesheet.tree.server.data.ts @@ -1,8 +1,4 @@ -import { - FileSystemTreeItemPresentationModel, - PagedFileSystemTreeItemPresentationModel, - StylesheetResource, -} from '@umbraco-cms/backoffice/backend-api'; +import { FileSystemTreeItemPresentationModel, StylesheetResource } from '@umbraco-cms/backoffice/backend-api'; import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller'; import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; import { UmbTreeDataSource } from '@umbraco-cms/backoffice/repository'; @@ -13,9 +9,7 @@ import { UmbTreeDataSource } from '@umbraco-cms/backoffice/repository'; * @class UmbStylesheetTreeServerDataSource * @implements {UmbTreeDataSource} */ -export class UmbStylesheetTreeServerDataSource - implements UmbTreeDataSource -{ +export class UmbStylesheetTreeServerDataSource implements UmbTreeDataSource { #host: UmbControllerHostElement; /** @@ -38,17 +32,25 @@ export class UmbStylesheetTreeServerDataSource /** * Fetches the children of a given stylesheet path from the server - * @param {(string | undefined)} path + * @param {(string | null)} path * @return {*} * @memberof UmbStylesheetTreeServerDataSource */ - async getChildrenOf(path: string | undefined) { - return tryExecuteAndNotify( - this.#host, - StylesheetResource.getTreeStylesheetChildren({ - path, - }) - ); + async getChildrenOf(path: string | null) { + if (path === undefined) throw new Error('Path is missing'); + + /* TODO: should we make getRootItems() internal + so it only is a server concern that there are two endpoints? */ + if (path === null) { + return this.getRootItems(); + } else { + return tryExecuteAndNotify( + this.#host, + StylesheetResource.getTreeStylesheetChildren({ + path, + }) + ); + } } /** diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/templating/stylesheets/repository/stylesheet.repository.ts b/src/Umbraco.Web.UI.Client/src/backoffice/templating/stylesheets/repository/stylesheet.repository.ts index 6660a94e1d..f1a29007ce 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/templating/stylesheets/repository/stylesheet.repository.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/templating/stylesheets/repository/stylesheet.repository.ts @@ -5,13 +5,11 @@ import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller'; import { UmbNotificationContext, UMB_NOTIFICATION_CONTEXT_TOKEN } from '@umbraco-cms/backoffice/notification'; import { UmbContextConsumerController } from '@umbraco-cms/backoffice/context-api'; import { UmbTreeRepository } from '@umbraco-cms/backoffice/repository'; -import { - FileSystemTreeItemPresentationModel, - PagedFileSystemTreeItemPresentationModel, -} from '@umbraco-cms/backoffice/backend-api'; +import { FileSystemTreeItemPresentationModel } from '@umbraco-cms/backoffice/backend-api'; +import type { UmbTreeRootFileSystemModel } from '@umbraco-cms/backoffice/models'; export class UmbStylesheetRepository - implements UmbTreeRepository + implements UmbTreeRepository { #host; #dataSource; @@ -50,6 +48,21 @@ export class UmbStylesheetRepository } } + // TREE: + async requestTreeRoot() { + await this.#init; + + const data = { + path: null, + type: 'stylesheet-root', + name: 'Stylesheets', + icon: 'umb:folder', + hasChildren: true, + }; + + return { data }; + } + async requestRootTreeItems() { await this.#init; @@ -63,7 +76,7 @@ export class UmbStylesheetRepository } async requestTreeItemsOf(path: string | null) { - if (!path) throw new Error('Cannot request tree item with missing path'); + if (path === undefined) throw new Error('Cannot request tree item with missing path'); await this.#init; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/templating/stylesheets/tree/manifests.ts b/src/Umbraco.Web.UI.Client/src/backoffice/templating/stylesheets/tree/manifests.ts index c93536bd70..6c16725a5d 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/templating/stylesheets/tree/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/templating/stylesheets/tree/manifests.ts @@ -20,7 +20,7 @@ const treeItem: ManifestTreeItem = { alias: 'Umb.TreeItem.Stylesheet', name: 'Stylesheet Tree Item', conditions: { - entityType: STYLESHEET_ENTITY_TYPE, + entityTypes: ['stylesheet-root', STYLESHEET_ENTITY_TYPE], }, }; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/templating/templates/entity-actions/manifests.ts b/src/Umbraco.Web.UI.Client/src/backoffice/templating/templates/entity-actions/manifests.ts index a85363c686..05e8bd364f 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/templating/templates/entity-actions/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/templating/templates/entity-actions/manifests.ts @@ -15,7 +15,7 @@ const entityActions: Array = [ repositoryAlias: TEMPLATE_REPOSITORY_ALIAS, }, conditions: { - entityType: 'template', + entityTypes: ['template'], }, }, { @@ -29,7 +29,7 @@ const entityActions: Array = [ repositoryAlias: TEMPLATE_REPOSITORY_ALIAS, }, conditions: { - entityType: 'template', + entityTypes: ['template'], }, }, ]; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/templating/templates/repository/sources/template.tree.server.data.ts b/src/Umbraco.Web.UI.Client/src/backoffice/templating/templates/repository/sources/template.tree.server.data.ts index d3c96eda1a..20e8726e78 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/templating/templates/repository/sources/template.tree.server.data.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/templating/templates/repository/sources/template.tree.server.data.ts @@ -37,16 +37,20 @@ export class UmbTemplateTreeServerDataSource implements TemplateTreeDataSource { * @memberof UmbTemplateTreeServerDataSource */ async getChildrenOf(parentId: string | null) { - if (!parentId) { - throw new Error('Parent id is missing'); - } + if (parentId === undefined) throw new Error('Parent id is missing'); - return tryExecuteAndNotify( - this.#host, - TemplateResource.getTreeTemplateChildren({ - parentId, - }) - ); + /* TODO: should we make getRootItems() internal + so it only is a server concern that there are two endpoints? */ + if (parentId === null) { + return this.getRootItems(); + } else { + return tryExecuteAndNotify( + this.#host, + TemplateResource.getTreeTemplateChildren({ + parentId, + }) + ); + } } /** diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/templating/templates/repository/template.repository.ts b/src/Umbraco.Web.UI.Client/src/backoffice/templating/templates/repository/template.repository.ts index c4cbd48566..ece426ed55 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/templating/templates/repository/template.repository.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/templating/templates/repository/template.repository.ts @@ -2,26 +2,32 @@ import { UmbTemplateDetailServerDataSource } from './sources/template.detail.ser import { UmbTemplateTreeServerDataSource } from './sources/template.tree.server.data'; import { UmbTemplateStore, UMB_TEMPLATE_STORE_CONTEXT_TOKEN } from './template.store'; import { UmbTemplateTreeStore, UMB_TEMPLATE_TREE_STORE_CONTEXT_TOKEN } from './template.tree.store'; -import type { UmbDetailRepository, UmbTreeRepository } from '@umbraco-cms/backoffice/repository'; +import type { + UmbDataSource, + UmbDetailRepository, + UmbTreeDataSource, + UmbTreeRepository, +} from '@umbraco-cms/backoffice/repository'; import type { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller'; import { UmbNotificationContext, UMB_NOTIFICATION_CONTEXT_TOKEN } from '@umbraco-cms/backoffice/notification'; import { UmbContextConsumerController } from '@umbraco-cms/backoffice/context-api'; import { CreateTemplateRequestModel, + EntityTreeItemResponseModel, TemplateResponseModel, UpdateTemplateRequestModel, } from '@umbraco-cms/backoffice/backend-api'; export class UmbTemplateRepository implements - UmbTreeRepository, - UmbDetailRepository + UmbTreeRepository, + UmbDetailRepository { #init; #host: UmbControllerHostElement; - #treeDataSource: UmbTemplateTreeServerDataSource; - #detailDataSource: UmbTemplateDetailServerDataSource; + #treeDataSource: UmbTreeDataSource; + #detailDataSource: UmbDataSource; #treeStore?: UmbTemplateTreeStore; #store?: UmbTemplateStore; @@ -51,6 +57,19 @@ export class UmbTemplateRepository } // TREE: + async requestTreeRoot() { + await this.#init; + + const data = { + id: null, + type: 'template-root', + name: 'Templates', + icon: 'umb:folder', + hasChildren: true, + }; + + return { data }; + } async requestRootTreeItems() { await this.#init; @@ -65,12 +84,9 @@ export class UmbTemplateRepository } async requestTreeItemsOf(parentId: string | null) { + if (parentId === undefined) throw new Error('Parent id is missing'); await this.#init; - if (!parentId) { - throw new Error('Parent id is missing'); - } - const { data, error } = await this.#treeDataSource.getChildrenOf(parentId); if (data) { @@ -102,7 +118,7 @@ export class UmbTemplateRepository return this.#treeStore!.childrenOf(parentId); } - async itemsLegacy(ids: Array) { + async itemsLegacy(ids: Array) { await this.#init; return this.#treeStore!.items(ids); } @@ -110,14 +126,9 @@ export class UmbTemplateRepository // DETAILS: async createScaffold(parentId: string | null) { + if (parentId === undefined) throw new Error('Parent id is missing'); await this.#init; - - if (!parentId) { - throw new Error('Parent id is missing'); - } - - // TODO: add parent id to create scaffold - return this.#detailDataSource.createScaffold(); + return this.#detailDataSource.createScaffold(parentId); } async requestById(id: string) { diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/templating/templates/tree/manifests.ts b/src/Umbraco.Web.UI.Client/src/backoffice/templating/templates/tree/manifests.ts index 466a222f9d..9227519e44 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/templating/templates/tree/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/templating/templates/tree/manifests.ts @@ -16,7 +16,7 @@ const treeItem: ManifestTreeItem = { alias: 'Umb.TreeItem.Template', name: 'Template Tree Item', conditions: { - entityType: 'template', + entityTypes: ['template-root', 'template'], }, }; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/templating/templates/workspace/template-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/backoffice/templating/templates/workspace/template-workspace.context.ts index 89983e09f5..4c034d9f1f 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/templating/templates/workspace/template-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/templating/templates/workspace/template-workspace.context.ts @@ -50,7 +50,7 @@ export class UmbTemplateWorkspaceContext extends UmbWorkspaceContext diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/entity-actions/manifests.ts b/src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/entity-actions/manifests.ts index fc79433c06..298a7d48fa 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/entity-actions/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/entity-actions/manifests.ts @@ -22,7 +22,7 @@ const entityActions: Array = [ api: UmbCreateDictionaryEntityAction, }, conditions: { - entityType, + entityTypes: [entityType], }, }, { @@ -37,7 +37,7 @@ const entityActions: Array = [ api: UmbMoveEntityAction, }, conditions: { - entityType, + entityTypes: [entityType], }, }, { @@ -52,7 +52,7 @@ const entityActions: Array = [ api: UmbExportDictionaryEntityAction, }, conditions: { - entityType, + entityTypes: [entityType], }, }, { @@ -67,7 +67,7 @@ const entityActions: Array = [ api: UmbImportDictionaryEntityAction, }, conditions: { - entityType, + entityTypes: [entityType], }, }, { @@ -82,7 +82,7 @@ const entityActions: Array = [ api: UmbReloadDictionaryEntityAction, }, conditions: { - entityType, + entityTypes: [entityType], }, }, { @@ -97,7 +97,7 @@ const entityActions: Array = [ api: UmbDeleteEntityAction, }, conditions: { - entityType, + entityTypes: [entityType], }, }, ]; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/menu-item/dictionary-menu-item.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/menu-item/dictionary-menu-item.element.ts index e12c1f7865..b3af10f851 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/menu-item/dictionary-menu-item.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/menu-item/dictionary-menu-item.element.ts @@ -5,7 +5,7 @@ import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; @customElement('umb-dictionary-menu-item') export class UmbDictionaryMenuItemElement extends UmbLitElement { render() { - return html``; + return html``; } } diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/repository/dictionary.repository.ts b/src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/repository/dictionary.repository.ts index 204653d4c0..a0888476de 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/repository/dictionary.repository.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/repository/dictionary.repository.ts @@ -8,6 +8,7 @@ import { UmbTreeDataSource, UmbDetailRepository, UmbTreeRepository } from '@umbr import { CreateDictionaryItemRequestModel, DictionaryOverviewResponseModel, + EntityTreeItemResponseModel, ImportDictionaryRequestModel, UpdateDictionaryItemRequestModel, } from '@umbraco-cms/backoffice/backend-api'; @@ -15,7 +16,7 @@ import { UmbNotificationContext, UMB_NOTIFICATION_CONTEXT_TOKEN } from '@umbraco export class UmbDictionaryRepository implements - UmbTreeRepository, + UmbTreeRepository, UmbDetailRepository< CreateDictionaryItemRequestModel, any, @@ -57,6 +58,21 @@ export class UmbDictionaryRepository ]); } + // TREE: + async requestTreeRoot() { + await this.#init; + + const data = { + id: null, + type: 'dictionary-root', + name: 'Dictionary', + icon: 'umb:folder', + hasChildren: true, + }; + + return { data }; + } + async requestRootTreeItems() { await this.#init; @@ -70,12 +86,9 @@ export class UmbDictionaryRepository } async requestTreeItemsOf(parentId: string | null) { + if (parentId === undefined) throw new Error('Parent id is missing'); await this.#init; - if (!parentId) { - throw new Error('Parent id is missing'); - } - const { data, error } = await this.#treeSource.getChildrenOf(parentId); if (data) { diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/repository/sources/dictionary.tree.server.data.ts b/src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/repository/sources/dictionary.tree.server.data.ts index 58a3a71d71..4a3ee66d11 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/repository/sources/dictionary.tree.server.data.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/repository/sources/dictionary.tree.server.data.ts @@ -37,16 +37,20 @@ export class UmbDictionaryTreeServerDataSource implements UmbTreeDataSource { * @memberof UmbDictionaryTreeServerDataSource */ async getChildrenOf(parentId: string | null) { - if (!parentId) { - throw new Error('Parent id is missing'); - } + if (parentId === undefined) throw new Error('Parent id is missing'); - return tryExecuteAndNotify( - this.#host, - DictionaryResource.getTreeDictionaryChildren({ - parentId, - }) - ); + /* TODO: should we make getRootItems() internal + so it only is a server concern that there are two endpoints? */ + if (parentId === null) { + return this.getRootItems(); + } else { + return tryExecuteAndNotify( + this.#host, + DictionaryResource.getTreeDictionaryChildren({ + parentId, + }) + ); + } } /** diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/tree/manifests.ts b/src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/tree/manifests.ts index dcfeba9411..53c551380b 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/tree/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/tree/manifests.ts @@ -16,7 +16,7 @@ const treeItem: ManifestTreeItem = { alias: 'Umb.TreeItem.DictionaryItem', name: 'Dictionary Item Tree Item', conditions: { - entityType: 'dictionary-item', + entityTypes: ['dictionary-root', 'dictionary-item'], }, }; diff --git a/src/Umbraco.Web.UI.Client/src/core/mocks/data/entity.data.ts b/src/Umbraco.Web.UI.Client/src/core/mocks/data/entity.data.ts index fbe98cc14f..a8a2fbd01a 100644 --- a/src/Umbraco.Web.UI.Client/src/core/mocks/data/entity.data.ts +++ b/src/Umbraco.Web.UI.Client/src/core/mocks/data/entity.data.ts @@ -44,26 +44,32 @@ export class UmbEntityData extends UmbData { return saveItem; } - move(ids: Array, destinationKey: string) { - const destinationItem = this.getById(destinationKey); - if (!destinationItem) throw new Error(`Destination item with key ${destinationKey} not found`); + move(ids: Array, destinationId: string | null) { + if (destinationId === undefined) throw new Error('Destination Id is missing'); + + if (destinationId !== null) { + const destinationItem = this.getById(destinationId); + if (!destinationItem) throw new Error(`Destination item with key ${destinationId} not found`); + } const items = this.getByIds(ids); const movedItems = items.map((item) => { return { ...item, - parentId: destinationKey, + parentId: destinationId, }; }); movedItems.forEach((movedItem) => this.updateData(movedItem)); - destinationItem.hasChildren = true; - this.updateData(destinationItem); } - copy(ids: Array, destinationKey: string) { - const destinationItem = this.getById(destinationKey); - if (!destinationItem) throw new Error(`Destination item with key ${destinationKey} not found`); + copy(ids: Array, destinationId: string | null) { + if (destinationId === undefined) throw new Error('Destination Id is missing'); + + if (destinationId !== null) { + const destinationItem = this.getById(destinationId); + if (!destinationItem) throw new Error(`Destination item with key ${destinationId} not found`); + } // TODO: Notice we don't add numbers to the 'copy' name. const items = this.getByIds(ids); @@ -72,16 +78,13 @@ export class UmbEntityData extends UmbData { ...item, name: item.name + ' Copy', id: UmbId.new(), - parentId: destinationKey, + parentId: destinationId, }; }); copyItems.forEach((copyItem) => this.insert(copyItem)); const newIds = copyItems.map((item) => item.id); - destinationItem.hasChildren = true; - this.updateData(destinationItem); - return newIds; } diff --git a/src/Umbraco.Web.UI.Client/src/core/mocks/data/template.data.ts b/src/Umbraco.Web.UI.Client/src/core/mocks/data/template.data.ts index 44e767b23b..99fbbf8c20 100644 --- a/src/Umbraco.Web.UI.Client/src/core/mocks/data/template.data.ts +++ b/src/Umbraco.Web.UI.Client/src/core/mocks/data/template.data.ts @@ -5,8 +5,8 @@ import { EntityTreeItemResponseModel, PagedEntityTreeItemResponseModel, TemplateResponseModel, - TemplateModelBaseModel, TemplateScaffoldResponseModel, + CreateTemplateRequestModel, } from '@umbraco-cms/backoffice/backend-api'; type TemplateDBItem = TemplateResponseModel & EntityTreeItemResponseModel; @@ -98,7 +98,7 @@ class UmbTemplateData extends UmbEntityData { }; } - create(templateData: TemplateModelBaseModel) { + create(templateData: CreateTemplateRequestModel) { const template = { $type: '', id: UmbId.new(), diff --git a/src/Umbraco.Web.UI.Client/src/core/mocks/data/utils.ts b/src/Umbraco.Web.UI.Client/src/core/mocks/data/utils.ts index 527d5c6304..c8d4d90e76 100644 --- a/src/Umbraco.Web.UI.Client/src/core/mocks/data/utils.ts +++ b/src/Umbraco.Web.UI.Client/src/core/mocks/data/utils.ts @@ -8,6 +8,7 @@ import type { DocumentResponseModel, FileSystemTreeItemPresentationModel, } from '@umbraco-cms/backoffice/backend-api'; +import { template } from 'lodash-es'; export const createEntityTreeItem = (item: any): EntityTreeItemResponseModel => { return { @@ -64,7 +65,11 @@ export const createDocumentTypeTreeItem = (item: DocumentTypeResponseModel): Doc export const createFileSystemTreeItem = (item: any): FileSystemTreeItemPresentationModel => { return { - ...createFolderTreeItem(item), + name: item.name, + type: item.type, + icon: item.icon, + hasChildren: item.hasChildren, path: item.path, + isFolder: item.isFolder, }; }; diff --git a/src/Umbraco.Web.UI.Client/src/core/mocks/domains/template.handlers.ts b/src/Umbraco.Web.UI.Client/src/core/mocks/domains/template.handlers.ts index 55e73ea95b..735673a0a4 100644 --- a/src/Umbraco.Web.UI.Client/src/core/mocks/domains/template.handlers.ts +++ b/src/Umbraco.Web.UI.Client/src/core/mocks/domains/template.handlers.ts @@ -1,7 +1,7 @@ import { rest } from 'msw'; import { umbTemplateData } from '../data/template.data'; import { umbracoPath } from '@umbraco-cms/backoffice/utils'; -import { TemplateModelBaseModel } from '@umbraco-cms/backoffice/backend-api'; +import { CreateTemplateRequestModel, UpdateTemplateRequestModel } from '@umbraco-cms/backoffice/backend-api'; // TODO: add schema export const handlers = [ @@ -42,7 +42,7 @@ export const handlers = [ return res(ctx.status(200), ctx.json(response)); }), - rest.put(umbracoPath('/template/:id'), async (req, res, ctx) => { + rest.put(umbracoPath('/template/:id'), async (req, res, ctx) => { const id = req.params.id as string; const data = await req.json(); if (!id) return; @@ -51,7 +51,7 @@ export const handlers = [ return res(ctx.status(200)); }), - rest.post(umbracoPath('/template'), async (req, res, ctx) => { + rest.post(umbracoPath('/template'), async (req, res, ctx) => { const data = await req.json(); if (!data) return; diff --git a/src/Umbraco.Web.UI.Client/src/stories/extending/entity-actions.mdx b/src/Umbraco.Web.UI.Client/src/stories/extending/entity-actions.mdx index eb9fe3121d..6d814db657 100644 --- a/src/Umbraco.Web.UI.Client/src/stories/extending/entity-actions.mdx +++ b/src/Umbraco.Web.UI.Client/src/stories/extending/entity-actions.mdx @@ -70,7 +70,7 @@ const manifest = { api: MyEntityAction, }, conditions: { - entityType: 'my-entity', + entityTypes: ['my-entity'], }, };