diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member-group/components/input-member-group/input-member-group.element.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member-group/components/input-member-group/input-member-group.element.ts index 40a8c9e3d5da..301b73357a46 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member-group/components/input-member-group/input-member-group.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member-group/components/input-member-group/input-member-group.element.ts @@ -3,18 +3,18 @@ import { UmbMemberGroupPickerInputContext } from './input-member-group.context.j import { css, customElement, html, nothing, property, repeat, state, when } from '@umbraco-cms/backoffice/external/lit'; import { splitStringToArray } from '@umbraco-cms/backoffice/utils'; import { UmbChangeEvent } from '@umbraco-cms/backoffice/event'; -import { UmbFormControlMixin } from '@umbraco-cms/backoffice/validation'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { UmbModalRouteRegistrationController } from '@umbraco-cms/backoffice/router'; import { UmbSorterController } from '@umbraco-cms/backoffice/sorter'; import { UMB_WORKSPACE_MODAL } from '@umbraco-cms/backoffice/workspace'; +import { UMB_VALIDATION_EMPTY_LOCALIZATION_KEY, UmbFormControlMixin } from '@umbraco-cms/backoffice/validation'; import type { UmbRepositoryItemsStatus } from '@umbraco-cms/backoffice/repository'; - import '@umbraco-cms/backoffice/entity-item'; @customElement('umb-input-member-group') -export class UmbInputMemberGroupElement extends UmbFormControlMixin( +export class UmbInputMemberGroupElement extends UmbFormControlMixin( UmbLitElement, + undefined, ) { #sorter = new UmbSorterController(this, { getUniqueOfElement: (element) => { @@ -53,7 +53,7 @@ export class UmbInputMemberGroupElement extends UmbFormControlMixin) { @@ -92,6 +92,7 @@ export class UmbInputMemberGroupElement extends UmbFormControlMixin 0 ? this.selection.join(',') : undefined; @@ -121,6 +122,15 @@ export class UmbInputMemberGroupElement extends UmbFormControlMixin this.requiredMessage ?? UMB_VALIDATION_EMPTY_LOCALIZATION_KEY, + () => !this.readonly && !!this.required && (this.value === undefined || this.value === null || this.value === ''), + ); + this.addValidator( 'rangeUnderflow', () => this.minMessage, diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member-group/property-editor/member-group-picker/property-editor-ui-member-group-picker.element.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member-group/property-editor/member-group-picker/property-editor-ui-member-group-picker.element.ts index 7c52689ce334..8d744ac54bca 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member-group/property-editor/member-group-picker/property-editor-ui-member-group-picker.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member-group/property-editor/member-group-picker/property-editor-ui-member-group-picker.element.ts @@ -7,15 +7,16 @@ import type { UmbPropertyEditorUiElement, } from '@umbraco-cms/backoffice/property-editor'; import { UmbChangeEvent } from '@umbraco-cms/backoffice/event'; +import { UMB_VALIDATION_EMPTY_LOCALIZATION_KEY, UmbFormControlMixin } from '@umbraco-cms/backoffice/validation'; /** * @element umb-property-editor-ui-member-group-picker */ @customElement('umb-property-editor-ui-member-group-picker') -export class UmbPropertyEditorUIMemberGroupPickerElement extends UmbLitElement implements UmbPropertyEditorUiElement { - @property() - public value?: string; - +export class UmbPropertyEditorUIMemberGroupPickerElement + extends UmbFormControlMixin(UmbLitElement, undefined) + implements UmbPropertyEditorUiElement +{ public set config(config: UmbPropertyEditorConfigCollection | undefined) { if (!config) return; @@ -32,6 +33,14 @@ export class UmbPropertyEditorUIMemberGroupPickerElement extends UmbLitElement i */ @property({ type: Boolean, reflect: true }) readonly = false; + @property({ type: Boolean }) + mandatory?: boolean; + @property({ type: String }) + mandatoryMessage = UMB_VALIDATION_EMPTY_LOCALIZATION_KEY; + + protected override firstUpdated() { + this.addFormControlElement(this.shadowRoot!.querySelector('umb-input-member-group')!); + } @state() private _min = 0; @@ -51,6 +60,8 @@ export class UmbPropertyEditorUIMemberGroupPickerElement extends UmbLitElement i .max=${this._max} .value=${this.value} @change=${this.#onChange} + ?required=${this.mandatory} + .requiredMessage=${this.mandatoryMessage} ?readonly=${this.readonly}> `; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member/components/input-member/input-member.element.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member/components/input-member/input-member.element.ts index 8ddf6e606824..e6c4ecdcc84c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member/components/input-member/input-member.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member/components/input-member/input-member.element.ts @@ -3,15 +3,16 @@ import { UmbMemberPickerInputContext } from './input-member.context.js'; import { css, customElement, html, nothing, property, repeat, state, when } from '@umbraco-cms/backoffice/external/lit'; import { splitStringToArray } from '@umbraco-cms/backoffice/utils'; import { UmbChangeEvent } from '@umbraco-cms/backoffice/event'; -import { UmbFormControlMixin } from '@umbraco-cms/backoffice/validation'; +import { UMB_VALIDATION_EMPTY_LOCALIZATION_KEY, UmbFormControlMixin } from '@umbraco-cms/backoffice/validation'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { UmbSorterController } from '@umbraco-cms/backoffice/sorter'; import { UMB_MEMBER_TYPE_ENTITY_TYPE } from '@umbraco-cms/backoffice/member-type'; import type { UmbRepositoryItemsStatus } from '@umbraco-cms/backoffice/repository'; @customElement('umb-input-member') -export class UmbInputMemberElement extends UmbFormControlMixin( +export class UmbInputMemberElement extends UmbFormControlMixin( UmbLitElement, + undefined, ) { #sorter = new UmbSorterController(this, { getUniqueOfElement: (element) => { @@ -50,7 +51,7 @@ export class UmbInputMemberElement extends UmbFormControlMixin; @@ -130,6 +140,12 @@ export class UmbInputMemberElement extends UmbFormControlMixin this.requiredMessage ?? UMB_VALIDATION_EMPTY_LOCALIZATION_KEY, + () => !this.readonly && !!this.required && (this.value === undefined || this.value === null || this.value === ''), + ); + this.addValidator( 'rangeUnderflow', () => this.minMessage, diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member/property-editor/member-picker/property-editor-ui-member-picker.element.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member/property-editor/member-picker/property-editor-ui-member-picker.element.ts index 747837c086ca..ba59faeb6fea 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member/property-editor/member-picker/property-editor-ui-member-picker.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member/property-editor/member-picker/property-editor-ui-member-picker.element.ts @@ -6,15 +6,16 @@ import type { UmbPropertyEditorUiElement, } from '@umbraco-cms/backoffice/property-editor'; import { UmbChangeEvent } from '@umbraco-cms/backoffice/event'; +import { UMB_VALIDATION_EMPTY_LOCALIZATION_KEY, UmbFormControlMixin } from '@umbraco-cms/backoffice/validation'; /** * @element umb-property-editor-ui-member-picker */ @customElement('umb-property-editor-ui-member-picker') -export class UmbPropertyEditorUIMemberPickerElement extends UmbLitElement implements UmbPropertyEditorUiElement { - @property() - public value?: string; - +export class UmbPropertyEditorUIMemberPickerElement + extends UmbFormControlMixin(UmbLitElement, undefined) + implements UmbPropertyEditorUiElement +{ @property({ attribute: false }) public config?: UmbPropertyEditorConfigCollection; @@ -26,6 +27,14 @@ export class UmbPropertyEditorUIMemberPickerElement extends UmbLitElement implem */ @property({ type: Boolean, reflect: true }) readonly = false; + @property({ type: Boolean }) + mandatory?: boolean; + @property({ type: String }) + mandatoryMessage = UMB_VALIDATION_EMPTY_LOCALIZATION_KEY; + + protected override firstUpdated() { + this.addFormControlElement(this.shadowRoot!.querySelector('umb-input-member')!); + } #onChange(event: CustomEvent & { target: UmbInputMemberElement }) { this.value = event.target.value; @@ -38,6 +47,8 @@ export class UmbPropertyEditorUIMemberPickerElement extends UmbLitElement implem max="1" .value=${this.value} @change=${this.#onChange} + ?required=${this.mandatory} + .requiredMessage=${this.mandatoryMessage} ?readonly=${this.readonly}>`; } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/components/user-input/user-input.element.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/components/user-input/user-input.element.ts index a1de877671c4..0445a265b4b0 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/components/user-input/user-input.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/components/user-input/user-input.element.ts @@ -5,12 +5,15 @@ import { splitStringToArray } from '@umbraco-cms/backoffice/utils'; import { UmbChangeEvent } from '@umbraco-cms/backoffice/event'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { UmbSorterController } from '@umbraco-cms/backoffice/sorter'; -import { UUIFormControlMixin } from '@umbraco-cms/backoffice/external/uui'; +import { UMB_VALIDATION_EMPTY_LOCALIZATION_KEY, UmbFormControlMixin } from '@umbraco-cms/backoffice/validation'; import type { UmbRepositoryItemsStatus } from '@umbraco-cms/backoffice/repository'; // TODO: Shall we rename to 'umb-input-user'? [LK] @customElement('umb-user-input') -export class UmbUserInputElement extends UUIFormControlMixin(UmbLitElement, '') { +export class UmbUserInputElement extends UmbFormControlMixin( + UmbLitElement, + undefined, +) { #sorter = new UmbSorterController(this, { getUniqueOfElement: (element) => { return element.id; @@ -27,6 +30,25 @@ export class UmbUserInputElement extends UUIFormControlMixin(UmbLitElement, '') }, }); + /** + * Sets the input to readonly mode, meaning value cannot be changed but still able to read and select its content. + * @type {boolean} + * @attr + * @default false + */ + @property({ type: Boolean, reflect: true }) + readonly = false; + + /** + * Sets the input to required, meaning validation will fail if the value is empty. + * @type {boolean} + */ + @property({ type: Boolean }) + required?: boolean; + + @property({ type: String }) + requiredMessage?: string; + /** * This is a minimum amount of selected items in this input. * @type {number} @@ -48,7 +70,7 @@ export class UmbUserInputElement extends UUIFormControlMixin(UmbLitElement, '') * @default */ @property({ type: String, attribute: 'min-message' }) - minMessage = 'This field need more items'; + minMessage = 'This field needs more items'; /** * This is a maximum amount of selected items in this input. @@ -70,7 +92,7 @@ export class UmbUserInputElement extends UUIFormControlMixin(UmbLitElement, '') * @attr * @default */ - @property({ type: String, attribute: 'min-message' }) + @property({ type: String, attribute: 'max-message' }) maxMessage = 'This field exceeds the allowed amount of items'; @property({ type: Array }) @@ -83,10 +105,10 @@ export class UmbUserInputElement extends UUIFormControlMixin(UmbLitElement, '') } @property() - public override set value(uniques: string) { + public override set value(uniques: string | undefined) { this.selection = splitStringToArray(uniques); } - public override get value(): string { + public override get value(): string | undefined { return this.selection.join(','); } @@ -101,6 +123,12 @@ export class UmbUserInputElement extends UUIFormControlMixin(UmbLitElement, '') constructor() { super(); + this.addValidator( + 'valueMissing', + () => this.requiredMessage ?? UMB_VALIDATION_EMPTY_LOCALIZATION_KEY, + () => !this.readonly && !!this.required && (this.value === undefined || this.value === null || this.value === ''), + ); + this.addValidator( 'rangeUnderflow', () => this.minMessage, @@ -141,7 +169,8 @@ export class UmbUserInputElement extends UUIFormControlMixin(UmbLitElement, '') id="btn-add" look="placeholder" label=${this.localize.term('general_choose')} - @click=${this.#openPicker}> + @click=${this.#openPicker} + ?disabled=${this.readonly}> `; } @@ -163,19 +192,20 @@ export class UmbUserInputElement extends UUIFormControlMixin(UmbLitElement, '') const item = this._items?.find((x) => x.unique === unique); const isError = status.state.type === 'error'; return html` - - - this.#removeItem(unique)}> - - + + + this.#removeItem(unique)}> + + `; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/property-editor/user-picker/property-editor-ui-user-picker.element.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/property-editor/user-picker/property-editor-ui-user-picker.element.ts index 1065c91ff31b..b7927a613e57 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/property-editor/user-picker/property-editor-ui-user-picker.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/property-editor/user-picker/property-editor-ui-user-picker.element.ts @@ -6,18 +6,30 @@ import type { UmbPropertyEditorConfigCollection, UmbPropertyEditorUiElement, } from '@umbraco-cms/backoffice/property-editor'; +import { UMB_VALIDATION_EMPTY_LOCALIZATION_KEY, UmbFormControlMixin } from '@umbraco-cms/backoffice/validation'; /** * @element umb-property-editor-ui-user-picker */ @customElement('umb-property-editor-ui-user-picker') -export class UmbPropertyEditorUIUserPickerElement extends UmbLitElement implements UmbPropertyEditorUiElement { - @property() - value?: string = ''; +export class UmbPropertyEditorUIUserPickerElement + extends UmbFormControlMixin(UmbLitElement, undefined) + implements UmbPropertyEditorUiElement +{ + @property({ type: Boolean, reflect: true }) + readonly = false; + @property({ type: Boolean }) + mandatory?: boolean; + @property({ type: String }) + mandatoryMessage = UMB_VALIDATION_EMPTY_LOCALIZATION_KEY; @property({ attribute: false }) public config?: UmbPropertyEditorConfigCollection; + protected override firstUpdated() { + this.addFormControlElement(this.shadowRoot!.querySelector('umb-user-input')!); + } + #onChange(event: CustomEvent & { target: UmbUserInputElement }) { this.value = event.target.value; this.dispatchEvent(new UmbChangeEvent()); @@ -25,7 +37,14 @@ export class UmbPropertyEditorUIUserPickerElement extends UmbLitElement implemen override render() { return html` - + `; } }