diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/packages/package-builder/workspace/workspace-package-builder.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/packages/package-builder/workspace/workspace-package-builder.element.ts index 9dce5a6dca..ccb98189a6 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/packages/package-builder/workspace/workspace-package-builder.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/packages/package-builder/workspace/workspace-package-builder.element.ts @@ -259,7 +259,7 @@ export class UmbWorkspacePackageBuilderElement extends UmbLitElement { #renderDataTypeSection() { return html`
- +
`; } diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/components/data-type-input/data-type-input.context.ts b/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/components/data-type-input/data-type-input.context.ts new file mode 100644 index 0000000000..2f91e61fea --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/components/data-type-input/data-type-input.context.ts @@ -0,0 +1,10 @@ +import { UmbPickerContext } from '@umbraco-cms/backoffice/picker'; +import { UmbDataTypeRepository } from '../../repository/data-type.repository'; +import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller'; +import { UMB_DATA_TYPE_PICKER_MODAL } from '@umbraco-cms/backoffice/modal'; + +export class UmbDataTypePickerContext extends UmbPickerContext { + constructor(host: UmbControllerHostElement) { + super(host, 'Umb.Repository.DataType', UMB_DATA_TYPE_PICKER_MODAL); + } +} 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 new file mode 100644 index 0000000000..126c0d97f5 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/components/data-type-input/data-type-input.element.ts @@ -0,0 +1,128 @@ +import { css, html } from 'lit'; +import { UUITextStyles } from '@umbraco-ui/uui-css/lib'; +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 { UMB_DATA_TYPE_PICKER_MODAL } from '@umbraco-cms/backoffice/modal'; + +@customElement('umb-data-type-input') +export class UmbDataTypeInputElement extends FormControlMixin(UmbLitElement) { + static styles = [ + UUITextStyles, + css` + #add-button { + width: 100%; + } + `, + ]; + /** + * This is a minimum amount of selected items in this input. + * @type {number} + * @attr + * @default undefined + */ + @property({ type: Number }) + min?: number; + + /** + * Min validation message. + * @type {boolean} + * @attr + * @default + */ + @property({ type: String, attribute: 'min-message' }) + minMessage = 'This field need more items'; + + /** + * This is a maximum amount of selected items in this input. + * @type {number} + * @attr + * @default undefined + */ + @property({ type: Number }) + max?: number; + + /** + * Max validation message. + * @type {boolean} + * @attr + * @default + */ + @property({ type: String, attribute: 'min-message' }) + maxMessage = 'This field exceeds the allowed amount of items'; + + private _selectedIds: Array = []; + public get selectedIds(): Array { + return this._selectedIds; + } + public set selectedIds(ids: Array) { + this._selectedIds = ids; + super.value = ids.join(','); + } + + @property() + public set value(idsString: string) { + if (idsString !== this._value) { + this.selectedIds = idsString.split(/[ ,]+/); + } + } + + @state() + private _items?: Array; + + #pickerContext = new UmbDataTypePickerContext(this); + + constructor() { + super(); + + this.addValidator( + 'rangeUnderflow', + () => this.minMessage, + () => !!this.min && this._selectedIds.length < this.min + ); + + this.addValidator( + 'rangeOverflow', + () => this.maxMessage, + () => !!this.max && this._selectedIds.length > this.max + ); + + this.observe(this.#pickerContext.selection, (selection) => (this.selectedIds = selection)); + //this.observe(this.#pickerContext.items, (items) => (this._items = items)); + } + + protected getFormElement() { + return undefined; + } + + render() { + return html` + ${this._items?.map((item) => this._renderItem(item))} + this.#pickerContext.openPicker()} label="open" + >Add + `; + } + + private _renderItem(item: DataTypeItemResponseModel) { + return html` + + + this.#pickerContext.removeItem(item.id)} label="Remove Data Type ${item.name}" + >Remove + + + `; + } +} + +export default UmbDataTypeInputElement; + +declare global { + interface HTMLElementTagNameMap { + 'umb-data-type-input': UmbDataTypeInputElement; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/components/index.ts b/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/components/index.ts new file mode 100644 index 0000000000..c076c5f9a3 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/components/index.ts @@ -0,0 +1 @@ +import './data-type-input/data-type-input.element'; 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 new file mode 100644 index 0000000000..758f322450 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/index.ts @@ -0,0 +1 @@ +import './components'; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/manifests.ts b/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/manifests.ts index fac58d2628..28e0f18938 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/manifests.ts @@ -3,6 +3,7 @@ import { manifests as repositoryManifests } from './repository/manifests'; import { manifests as menuItemManifests } from './menu-item/manifests'; import { manifests as treeManifests } from './tree/manifests'; import { manifests as workspaceManifests } from './workspace/manifests'; +import { manifests as modalManifests } from './modal/manifests'; export const manifests = [ ...entityActions, @@ -10,4 +11,5 @@ export const manifests = [ ...menuItemManifests, ...treeManifests, ...workspaceManifests, + ...modalManifests, ]; 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 new file mode 100644 index 0000000000..dd4d6d3a25 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/modal/data-type-picker/data-type-picker-modal.element.ts @@ -0,0 +1,77 @@ +import { css, html } from 'lit'; +import { UUITextStyles } from '@umbraco-ui/uui-css/lib'; +import { customElement, property, state } from 'lit/decorators.js'; +import type { UmbTreeElement } from '../../../../shared/components/tree/tree.element'; +import { + UmbDocumentTypePickerModalData, + UmbDocumentTypePickerModalResult, + UmbModalHandler, +} from '@umbraco-cms/backoffice/modal'; +import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; + +// TODO: make use of UmbPickerLayoutBase +@customElement('umb-data-type-picker-modal') +export class UmbDataTypePickerModalElement extends UmbLitElement { + static styles = [UUITextStyles, css``]; + + @property({ attribute: false }) + modalHandler?: UmbModalHandler; + + @property({ type: Object, attribute: false }) + data?: UmbDocumentTypePickerModalData; + + @state() + _selection: Array = []; + + @state() + _multiple = true; + + connectedCallback() { + super.connectedCallback(); + this._selection = this.data?.selection ?? []; + this._multiple = this.data?.multiple ?? true; + } + + 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]]; + } + + private _submit() { + this.modalHandler?.submit({ selection: this._selection }); + } + + private _close() { + this.modalHandler?.reject(); + } + + render() { + return html` + + + +
+ +
+
+ + +
+
+ `; + } +} + +export default UmbDataTypePickerModalElement; + +declare global { + interface HTMLElementTagNameMap { + 'umb-data-type-picker-modal': UmbDataTypePickerModalElement; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/modal/manifests.ts b/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/modal/manifests.ts new file mode 100644 index 0000000000..838aba346b --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/modal/manifests.ts @@ -0,0 +1,12 @@ +import type { ManifestModal } from '@umbraco-cms/backoffice/extensions-registry'; + +const modals: Array = [ + { + type: 'modal', + alias: 'Umb.Modal.DataTypePicker', + name: 'Data Type Picker Modal', + loader: () => import('./data-type-picker/data-type-picker-modal.element'), + }, +]; + +export const manifests = [...modals]; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/settings/index.ts b/src/Umbraco.Web.UI.Client/src/backoffice/settings/index.ts index a578c12d5c..c86644f739 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/settings/index.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/settings/index.ts @@ -11,6 +11,8 @@ import { manifests as logviewerManifests } from './logviewer/manifests'; import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extensions-api'; import { ManifestTypes } from '@umbraco-cms/backoffice/extensions-registry'; +import './data-types/components'; + export const manifests = [ ...settingsSectionManifests, ...settingsMenuManifests,