From 12297ea617d5c5500aa3945a3a64b42290f26b32 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Tue, 14 Oct 2025 18:11:34 +0200 Subject: [PATCH] Picker data source: Add support for pickable filters (#20491) * add pickable to vs code dictionary * set up types for pickable filters in data sources * pass search pickable filter to search result * apply filter config in document data source example * add pickable filters to custom tree example * Update input-entity-data.context.ts * remove unused * Update types.ts --- .vscode/settings.json | 3 +++ .../example-custom-picker-tree-data-source.ts | 17 +++++++++--- .../example-document-picker-data-source.ts | 27 ++++++++++++++++--- .../collection-item-picker-modal.element.ts | 8 +++++- .../src/packages/core/modal/types.ts | 7 ++++- .../collection-data-source/types.ts | 8 +++++- .../picker-data-source/data-source/types.ts | 5 +++- .../searchable-data-source/types.ts | 9 +++++-- .../tree-data-source/types.ts | 11 ++++++-- .../search/picker-search-result.element.ts | 2 +- .../tree-picker-modal.element.ts | 7 ++++- .../input/input-entity-data.context.ts | 12 ++++++++- 12 files changed, 97 insertions(+), 19 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 662f47d2d0..7bb7e547da 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,8 @@ { "cSpell.words": [ + "backoffice", + "pickable", + "Pickable", "unprovide", "Unproviding" ], diff --git a/src/Umbraco.Web.UI.Client/examples/picker-data-source/example-custom-picker-tree-data-source.ts b/src/Umbraco.Web.UI.Client/examples/picker-data-source/example-custom-picker-tree-data-source.ts index 8e6f8e6ce2..bf4dd73ecd 100644 --- a/src/Umbraco.Web.UI.Client/examples/picker-data-source/example-custom-picker-tree-data-source.ts +++ b/src/Umbraco.Web.UI.Client/examples/picker-data-source/example-custom-picker-tree-data-source.ts @@ -1,12 +1,21 @@ import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; -import type { UmbPickerTreeDataSource } from '@umbraco-cms/backoffice/picker-data-source'; -import type { UmbSearchRequestArgs } from '@umbraco-cms/backoffice/search'; +import type { + UmbPickerSearchableDataSource, + UmbPickerTreeDataSource, +} from '@umbraco-cms/backoffice/picker-data-source'; +import type { UmbSearchRequestArgs, UmbSearchResultItemModel } from '@umbraco-cms/backoffice/search'; import type { UmbTreeChildrenOfRequestArgs, UmbTreeItemModel } from '@umbraco-cms/backoffice/tree'; export class ExampleCustomPickerTreePropertyEditorDataSource extends UmbControllerBase - implements UmbPickerTreeDataSource + implements UmbPickerTreeDataSource, UmbPickerSearchableDataSource { + treePickableFilter: (treeItem: UmbTreeItemModel) => boolean = (treeItem) => + !!treeItem.unique && treeItem.entityType === 'example'; + + searchPickableFilter: (searchItem: UmbSearchResultItemModel) => boolean = (searchItem) => + !!searchItem.unique && searchItem.entityType === 'example'; + async requestTreeRoot() { const root = { unique: null, @@ -60,7 +69,7 @@ export class ExampleCustomPickerTreePropertyEditorDataSource const data = { items: result, - totalItems: result.length, + total: result.length, }; return { data }; diff --git a/src/Umbraco.Web.UI.Client/examples/picker-data-source/example-document-picker-data-source.ts b/src/Umbraco.Web.UI.Client/examples/picker-data-source/example-document-picker-data-source.ts index 68937ecce7..47cfbdc043 100644 --- a/src/Umbraco.Web.UI.Client/examples/picker-data-source/example-document-picker-data-source.ts +++ b/src/Umbraco.Web.UI.Client/examples/picker-data-source/example-document-picker-data-source.ts @@ -3,7 +3,10 @@ import { UmbDocumentItemRepository, UmbDocumentSearchRepository, UmbDocumentTreeRepository, + type UmbDocumentSearchItemModel, type UmbDocumentSearchRequestArgs, + type UmbDocumentTreeItemModel, + type UmbDocumentTreeRootModel, } from '@umbraco-cms/backoffice/document'; import { UMB_DOCUMENT_TYPE_ENTITY_TYPE } from '@umbraco-cms/backoffice/document-type'; import type { @@ -20,16 +23,21 @@ import { getConfigValue, type UmbConfigCollectionModel } from '@umbraco-cms/back export class ExampleDocumentPickerPropertyEditorDataSource extends UmbControllerBase - implements UmbPickerTreeDataSource, UmbPickerSearchableDataSource + implements + UmbPickerTreeDataSource, + UmbPickerSearchableDataSource { #tree = new UmbDocumentTreeRepository(this); #item = new UmbDocumentItemRepository(this); #search = new UmbDocumentSearchRepository(this); #config: UmbConfigCollectionModel = []; + treePickableFilter: (treeItem: UmbDocumentTreeItemModel) => boolean = (treeItem) => !!treeItem.unique; + setConfig(config: UmbConfigCollectionModel) { // TODO: add examples for all config options this.#config = config; + this.#applyPickableFilterFromConfig(); } getConfig(): UmbConfigCollectionModel { @@ -57,6 +65,13 @@ export class ExampleDocumentPickerPropertyEditorDataSource } search(args: UmbSearchRequestArgs) { + const allowedContentTypes = this.#getAllowedDocumentTypesConfig(); + const combinedArgs: UmbDocumentSearchRequestArgs = { ...args, allowedContentTypes }; + + return this.#search.search(combinedArgs); + } + + #getAllowedDocumentTypesConfig() { const filterString = getConfigValue(this.#config, 'filter'); const filterArray = filterString ? filterString.split(',') : []; const allowedContentTypes: UmbDocumentSearchRequestArgs['allowedContentTypes'] = filterArray.map( @@ -65,10 +80,14 @@ export class ExampleDocumentPickerPropertyEditorDataSource unique, }), ); + return allowedContentTypes; + } - const combinedArgs: UmbDocumentSearchRequestArgs = { ...args, allowedContentTypes }; - - return this.#search.search(combinedArgs); + #applyPickableFilterFromConfig() { + const allowedDocumentTypes = this.#getAllowedDocumentTypesConfig(); + if (!allowedDocumentTypes || allowedDocumentTypes.length === 0) return; + this.treePickableFilter = (treeItem: UmbDocumentTreeItemModel) => + allowedDocumentTypes.some((entityType) => entityType.unique === treeItem.documentType.unique); } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/collection/collection-item-picker-modal/collection-item-picker-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/collection/collection-item-picker-modal/collection-item-picker-modal.element.ts index f9081af885..cd63b2dd4b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/collection/collection-item-picker-modal/collection-item-picker-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/collection/collection-item-picker-modal/collection-item-picker-modal.element.ts @@ -98,6 +98,8 @@ export class UmbCollectionItemPickerModalElement extends UmbModalBaseElement< this.modalContext?.dispatchEvent(new UmbDeselectedEvent(event.unique)); } + #searchSelectableFilter = () => true; + override render() { return html` @@ -106,10 +108,14 @@ export class UmbCollectionItemPickerModalElement extends UmbModalBaseElement< `; } + #renderSearch() { + const selectableFilter = + this.data?.search?.pickableFilter ?? this.data?.pickableFilter ?? this.#searchSelectableFilter; + return html` - + `; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/modal/types.ts b/src/Umbraco.Web.UI.Client/src/packages/core/modal/types.ts index bd3d4a397c..0c1128dcc0 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/modal/types.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/modal/types.ts @@ -1,5 +1,6 @@ import type { ElementLoaderProperty } from '@umbraco-cms/backoffice/extension-api'; import type { UUIModalElement, UUIModalSidebarSize } from '@umbraco-cms/backoffice/external/uui'; +import type { UmbSearchResultItemModel } from '@umbraco-cms/backoffice/search'; export type * from './extensions/types.js'; @@ -10,9 +11,13 @@ export interface UmbPickerModalData { search?: UmbPickerModalSearchConfig; } -export interface UmbPickerModalSearchConfig> { +export interface UmbPickerModalSearchConfig< + QueryParamsType = Record, + SearchResultItemType extends UmbSearchResultItemModel = UmbSearchResultItemModel, +> { providerAlias: string; queryParams?: QueryParamsType; + pickableFilter?: (item: SearchResultItemType) => boolean; } export interface UmbPickerModalValue { diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/picker-data-source/collection-data-source/types.ts b/src/Umbraco.Web.UI.Client/src/packages/core/picker-data-source/collection-data-source/types.ts index c8ef834453..80dcec6702 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/picker-data-source/collection-data-source/types.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/picker-data-source/collection-data-source/types.ts @@ -1,5 +1,11 @@ import type { UmbPickerDataSource } from '../data-source/types.js'; import type { UmbCollectionRepository } from '@umbraco-cms/backoffice/collection'; +import type { UmbItemModel } from '@umbraco-cms/backoffice/entity-item'; import type { UmbApi } from '@umbraco-cms/backoffice/extension-api'; -export interface UmbPickerCollectionDataSource extends UmbPickerDataSource, UmbCollectionRepository, UmbApi {} +export interface UmbPickerCollectionDataSource + extends UmbPickerDataSource, + UmbCollectionRepository, + UmbApi { + collectionPickableFilter?: (item: CollectionItemType) => boolean; +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/picker-data-source/data-source/types.ts b/src/Umbraco.Web.UI.Client/src/packages/core/picker-data-source/data-source/types.ts index ee184a09b3..9a78669edb 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/picker-data-source/data-source/types.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/picker-data-source/data-source/types.ts @@ -1,8 +1,11 @@ +import type { UmbItemModel } from '@umbraco-cms/backoffice/entity-item'; import type { UmbApi } from '@umbraco-cms/backoffice/extension-api'; import type { UmbItemRepository } from '@umbraco-cms/backoffice/repository'; import type { UmbConfigCollectionModel } from '@umbraco-cms/backoffice/utils'; -export interface UmbPickerDataSource extends UmbItemRepository, UmbApi { +export interface UmbPickerDataSource + extends UmbItemRepository, + UmbApi { setConfig?(config: UmbConfigCollectionModel | undefined): void; getConfig?(): UmbConfigCollectionModel | undefined; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/picker-data-source/searchable-data-source/types.ts b/src/Umbraco.Web.UI.Client/src/packages/core/picker-data-source/searchable-data-source/types.ts index 9baee08655..7f5be19b00 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/picker-data-source/searchable-data-source/types.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/picker-data-source/searchable-data-source/types.ts @@ -1,4 +1,9 @@ import type { UmbPickerDataSource } from '../types.js'; -import type { UmbSearchRepository } from '@umbraco-cms/backoffice/search'; +import type { UmbSearchRepository, UmbSearchResultItemModel } from '@umbraco-cms/backoffice/search'; -export interface UmbPickerSearchableDataSource extends UmbPickerDataSource, UmbSearchRepository {} +export interface UmbPickerSearchableDataSource< + SearchResultItemType extends UmbSearchResultItemModel = UmbSearchResultItemModel, +> extends UmbPickerDataSource, + UmbSearchRepository { + searchPickableFilter?: (searchItem: SearchResultItemType) => boolean; +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/picker-data-source/tree-data-source/types.ts b/src/Umbraco.Web.UI.Client/src/packages/core/picker-data-source/tree-data-source/types.ts index ce5cb9d63b..45172c7228 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/picker-data-source/tree-data-source/types.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/picker-data-source/tree-data-source/types.ts @@ -1,5 +1,12 @@ import type { UmbPickerDataSource } from '../data-source/types.js'; import type { UmbApi } from '@umbraco-cms/backoffice/extension-api'; -import type { UmbTreeRepository } from '@umbraco-cms/backoffice/tree'; +import type { UmbTreeItemModel, UmbTreeRepository, UmbTreeRootModel } from '@umbraco-cms/backoffice/tree'; -export interface UmbPickerTreeDataSource extends UmbPickerDataSource, UmbTreeRepository, UmbApi {} +export interface UmbPickerTreeDataSource< + TreeItemType extends UmbTreeItemModel = UmbTreeItemModel, + TreeRootType extends UmbTreeRootModel = UmbTreeRootModel, +> extends UmbPickerDataSource, + UmbTreeRepository, + UmbApi { + treePickableFilter?: (item: TreeItemType) => boolean; +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/picker/search/picker-search-result.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/picker/search/picker-search-result.element.ts index 242d58b00a..b402d66ca2 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/picker/search/picker-search-result.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/picker/search/picker-search-result.element.ts @@ -8,7 +8,7 @@ import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import type { UmbSearchRequestArgs, UmbSearchResultItemModel } from '@umbraco-cms/backoffice/search'; import type { UmbItemModel } from '@umbraco-cms/backoffice/entity-item'; -type PickableFilterMethodType = (item: T) => boolean; +type PickableFilterMethodType = (item: T) => boolean; @customElement('umb-picker-search-result') export class UmbPickerSearchResultElement extends UmbLitElement { diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-picker-modal/tree-picker-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-picker-modal/tree-picker-modal.element.ts index da5e0e8fc3..78f758d8f0 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-picker-modal/tree-picker-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-picker-modal/tree-picker-modal.element.ts @@ -172,6 +172,8 @@ export class UmbTreePickerModalElement true; + override render() { return html` @@ -181,9 +183,12 @@ export class UmbTreePickerModalElement - + `; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/property-editors/entity-data-picker/input/input-entity-data.context.ts b/src/Umbraco.Web.UI.Client/src/packages/property-editors/entity-data-picker/input/input-entity-data.context.ts index eb1db5dc65..29f301e0f8 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/property-editors/entity-data-picker/input/input-entity-data.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/property-editors/entity-data-picker/input/input-entity-data.context.ts @@ -14,6 +14,7 @@ import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registr import { UmbModalToken, type UmbPickerModalData } from '@umbraco-cms/backoffice/modal'; import { UMB_TREE_PICKER_MODAL_ALIAS, + type UmbTreeItemModel, type UmbTreePickerModalData, type UmbTreePickerModalValue, } from '@umbraco-cms/backoffice/tree'; @@ -84,6 +85,9 @@ export class UmbEntityDataPickerInputContext extends UmbPickerInputContext>) { + // TODO: investigate type issues + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore this.modalAlias = this.#getModalToken(); await super.openPicker(pickerData); } @@ -133,7 +137,7 @@ export class UmbEntityDataPickerInputContext extends UmbPickerInputContext, UmbTreePickerModalValue>( + return new UmbModalToken, UmbTreePickerModalValue>( UMB_TREE_PICKER_MODAL_ALIAS, { modal: { @@ -143,9 +147,12 @@ export class UmbEntityDataPickerInputContext extends UmbPickerInputContext