diff --git a/src/Umbraco.Web.UI.Client/.vscode/settings.json b/src/Umbraco.Web.UI.Client/.vscode/settings.json index 1040ea46cb..04238ad7e0 100644 --- a/src/Umbraco.Web.UI.Client/.vscode/settings.json +++ b/src/Umbraco.Web.UI.Client/.vscode/settings.json @@ -5,6 +5,7 @@ "Backoffice", "combobox", "Elementable", + "pickable", "templating", "variantable" ], diff --git a/src/Umbraco.Web.UI.Client/libs/modal/modal.interfaces.ts b/src/Umbraco.Web.UI.Client/libs/modal/modal.interfaces.ts index bcc2f9ad6d..c5bd4ad006 100644 --- a/src/Umbraco.Web.UI.Client/libs/modal/modal.interfaces.ts +++ b/src/Umbraco.Web.UI.Client/libs/modal/modal.interfaces.ts @@ -1,7 +1,8 @@ export interface UmbPickerModalData { - multiple: boolean; - selection: Array; + multiple?: boolean; + selection?: Array; filter?: (item: T) => boolean; + pickableFilter?: (item: T) => boolean; } export interface UmbPickerModalResult { 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 2297e7e023..49a3b54798 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,13 +1,8 @@ -import { UmbModalToken } from '@umbraco-cms/backoffice/modal'; +import { FolderTreeItemResponseModel } from '@umbraco-cms/backoffice/backend-api'; +import { UmbModalToken, UmbPickerModalData, UmbPickerModalResult } from '@umbraco-cms/backoffice/modal'; -export interface UmbDataTypePickerModalData { - selection?: Array; - multiple?: boolean; -} - -export interface UmbDataTypePickerModalResult { - selection: Array; -} +export type UmbDataTypePickerModalData = UmbPickerModalData; +export type UmbDataTypePickerModalResult = UmbPickerModalResult; export const UMB_DATA_TYPE_PICKER_MODAL = new UmbModalToken( 'Umb.Modal.DataTypePicker', diff --git a/src/Umbraco.Web.UI.Client/libs/picker-input/picker-input.context.ts b/src/Umbraco.Web.UI.Client/libs/picker-input/picker-input.context.ts index 3c8bae91c2..669e18d129 100644 --- a/src/Umbraco.Web.UI.Client/libs/picker-input/picker-input.context.ts +++ b/src/Umbraco.Web.UI.Client/libs/picker-input/picker-input.context.ts @@ -7,6 +7,7 @@ import { UMB_MODAL_CONTEXT_TOKEN, UmbModalContext, UmbModalToken, + UmbPickerModalData, } from '@umbraco-cms/backoffice/modal'; import { UmbContextConsumerController } from '@umbraco-cms/backoffice/context-api'; import { ItemResponseModelBaseModel } from '@umbraco-cms/backoffice/backend-api'; @@ -20,6 +21,8 @@ export class UmbPickerInputContext public modalContext?: UmbModalContext; + public pickableFilter?: (item: ItemType) => boolean = () => true; + #selection = new UmbArrayState([]); selection = this.#selection.asObservable(); @@ -74,14 +77,14 @@ export class UmbPickerInputContext this.#selection.next(selection); } - // TODO: revisit this method. How do we best pass picker data? - // If modalAlias is a ModalToken, then via TS, we should get the correct type for pickerData. Otherwise fallback to unknown. - openPicker(pickerData?: any) { + // TODO: If modalAlias is a ModalToken, then via TS, we should get the correct type for pickerData. Otherwise fallback to unknown. + openPicker(pickerData?: Partial>) { if (!this.modalContext) throw new Error('Modal context is not initialized'); const modalHandler = this.modalContext.open(this.modalAlias, { multiple: this.max === 1 ? false : true, selection: [...this.getSelection()], + pickableFilter: this.pickableFilter, ...pickerData, }); diff --git a/src/Umbraco.Web.UI.Client/libs/tree/tree-item-base.context.ts b/src/Umbraco.Web.UI.Client/libs/tree/tree-item-base.context.ts index 73c885daac..e325121b1e 100644 --- a/src/Umbraco.Web.UI.Client/libs/tree/tree-item-base.context.ts +++ b/src/Umbraco.Web.UI.Client/libs/tree/tree-item-base.context.ts @@ -138,7 +138,13 @@ export class UmbTreeItemContextBase this.#isSelectable.next(value)); + new UmbObserverController(this.host, this.treeContext.selectable, (value) => { + // If the tree is selectable, check if this item is selectable + if (value === true) { + const isSelectable = this.treeContext?.selectableFilter?.(this.getTreeItem()!) ?? true; + this.#isSelectable.next(isSelectable); + } + }); } #observeIsSelected() { diff --git a/src/Umbraco.Web.UI.Client/libs/tree/tree.context.ts b/src/Umbraco.Web.UI.Client/libs/tree/tree.context.ts index d5ae1bfbe1..bfbec36b74 100644 --- a/src/Umbraco.Web.UI.Client/libs/tree/tree.context.ts +++ b/src/Umbraco.Web.UI.Client/libs/tree/tree.context.ts @@ -40,8 +40,10 @@ export class UmbTreeContextBase public readonly multiple = this.#selectionManager.multiple; public readonly selection = this.#selectionManager.selection; + public repository?: UmbTreeRepository; + public selectableFilter?: (item: TreeItemType) => boolean = () => true; + #treeAlias?: string; - repository?: UmbTreeRepository; #treeManifestObserver?: UmbObserverController; #initResolver?: () => void; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/core/components/input-list-base/input-list-base.ts b/src/Umbraco.Web.UI.Client/src/backoffice/core/components/input-list-base/input-list-base.ts index 8d5208dc37..fe8713e0ab 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/core/components/input-list-base/input-list-base.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/core/components/input-list-base/input-list-base.ts @@ -6,7 +6,7 @@ import { UmbModalToken, UmbModalType, UMB_MODAL_CONTEXT_TOKEN, - UmbPickerModalData, + UmbPickerModalResult, } from '@umbraco-cms/backoffice/modal'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; @@ -43,7 +43,7 @@ export class UmbInputListBaseElement extends UmbLitElement { selection: this.value, }); - modalHandler?.onSubmit().then((data: UmbPickerModalData) => { + modalHandler?.onSubmit().then((data: UmbPickerModalResult) => { if (data) { this.value = data.selection.filter((id) => id !== null && id !== undefined) as Array; this.selectionUpdated(); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/core/components/tree/tree.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/core/components/tree/tree.element.ts index 4261cc5568..c347737de6 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/core/components/tree/tree.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/core/components/tree/tree.element.ts @@ -62,6 +62,14 @@ export class UmbTreeElement extends UmbLitElement { this.requestUpdate('hideTreeRoot', oldVal); } + @property() + get selectableFilter() { + return this.#treeContext.selectableFilter; + } + set selectableFilter(newVal) { + this.#treeContext.selectableFilter = newVal; + } + @state() private _items: TreeItemPresentationModel[] = []; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/components/data-type-input/data-type-input.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/components/data-type-input/data-type-input.element.ts index c8c28070e2..8a5e055745 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/components/data-type-input/data-type-input.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/components/data-type-input/data-type-input.element.ts @@ -4,7 +4,7 @@ import { customElement, property, state } from 'lit/decorators.js'; import { FormControlMixin } from '@umbraco-ui/uui-base/lib/mixins'; import { UmbDataTypePickerContext } from './data-type-input.context'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; -import type { DataTypeItemResponseModel } from '@umbraco-cms/backoffice/backend-api'; +import type { DataTypeItemResponseModel, FolderTreeItemResponseModel } from '@umbraco-cms/backoffice/backend-api'; @customElement('umb-data-type-input') export class UmbDataTypeInputElement extends FormControlMixin(UmbLitElement) { @@ -67,6 +67,14 @@ export class UmbDataTypeInputElement extends FormControlMixin(UmbLitElement) { this.selectedIds = idsString.split(/[ ,]+/); } + @property() + get pickableFilter() { + return this.#pickerContext.pickableFilter; + } + set pickableFilter(newVal) { + this.#pickerContext.pickableFilter = newVal; + } + @state() private _items?: Array; 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 e900eb8bb6..30dda0bd0a 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 @@ -53,6 +53,7 @@ export class UmbDataTypePickerModalElement extends UmbLitElement { @selected=${this.#onSelectionChange} .selection=${this._selection} selectable + .selectableFilter=${this.data?.pickableFilter} ?multiple=${this._multiple}>