Skip to content

Commit 01b3003

Browse files
Property Editors: Added form control and mandatory support to editors in picker group(Color, Content, Date, Document, Eye dropper, Multi URL). (#20684)
* Added form control support to color picker. * Avoid submit when readonly is true. * Added mandatory support. * Added form control support to date picker. * Removed an unused import. * Added form control and mandatory support to document picker. * Added form control support to Eye dropper. * Added. mandatory support for multi url picker also bind inner input in the eye dropper. * Removed unused import. * fix update of value * fixing not needed override of get and set methods --------- Co-authored-by: Niels Lyngsø <[email protected]>
1 parent 6b4503c commit 01b3003

File tree

13 files changed

+174
-51
lines changed

13 files changed

+174
-51
lines changed

src/Umbraco.Web.UI.Client/src/packages/core/components/input-color/input-color.element.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
import { html, customElement, property, map, nothing } from '@umbraco-cms/backoffice/external/lit';
22
import { UmbChangeEvent } from '@umbraco-cms/backoffice/event';
33
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
4-
import { UUIFormControlMixin } from '@umbraco-cms/backoffice/external/uui';
54
import type { UmbSwatchDetails } from '@umbraco-cms/backoffice/models';
65
import type { UUIColorSwatchesEvent } from '@umbraco-cms/backoffice/external/uui';
6+
import { UMB_VALIDATION_EMPTY_LOCALIZATION_KEY, UmbFormControlMixin } from '@umbraco-cms/backoffice/validation';
77

88
/*
99
* This wraps the UUI library uui-color-swatches component
1010
* @element umb-input-color
1111
*/
1212
@customElement('umb-input-color')
13-
export class UmbInputColorElement extends UUIFormControlMixin(UmbLitElement, '') {
13+
export class UmbInputColorElement extends UmbFormControlMixin<string, typeof UmbLitElement, undefined>(UmbLitElement) {
1414
protected override getFormElement() {
1515
return undefined;
1616
}
@@ -22,13 +22,26 @@ export class UmbInputColorElement extends UUIFormControlMixin(UmbLitElement, '')
2222
*/
2323
@property({ type: Boolean, reflect: true })
2424
readonly = false;
25+
@property({ type: Boolean })
26+
required = false;
27+
@property({ type: String })
28+
requiredMessage: string = UMB_VALIDATION_EMPTY_LOCALIZATION_KEY;
2529

2630
@property({ type: Boolean })
2731
showLabels = false;
2832

2933
@property({ type: Array })
3034
swatches?: Array<UmbSwatchDetails>;
3135

36+
constructor() {
37+
super();
38+
this.addValidator(
39+
'valueMissing',
40+
() => this.requiredMessage ?? UMB_VALIDATION_EMPTY_LOCALIZATION_KEY,
41+
() => !!this.required && !this.value && !this.readonly,
42+
);
43+
}
44+
3245
#onChange(event: UUIColorSwatchesEvent) {
3346
this.value = event.target.value;
3447
this.dispatchEvent(new UmbChangeEvent());

src/Umbraco.Web.UI.Client/src/packages/core/components/input-eye-dropper/input-eye-dropper.element.ts

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import { customElement, html, property, when } from '@umbraco-cms/backoffice/external/lit';
22
import { UmbChangeEvent } from '@umbraco-cms/backoffice/event';
33
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
4-
import { UUIFormControlMixin } from '@umbraco-cms/backoffice/external/uui';
54
import type { UUIColorPickerChangeEvent } from '@umbraco-cms/backoffice/external/uui';
5+
import { UMB_VALIDATION_EMPTY_LOCALIZATION_KEY, UmbFormControlMixin } from '@umbraco-cms/backoffice/validation';
66

77
@customElement('umb-input-eye-dropper')
8-
export class UmbInputEyeDropperElement extends UUIFormControlMixin(UmbLitElement, '') {
8+
export class UmbInputEyeDropperElement extends UmbFormControlMixin<string, typeof UmbLitElement, undefined>(
9+
UmbLitElement,
10+
) {
911
protected override getFormElement() {
1012
return undefined;
1113
}
@@ -16,12 +18,26 @@ export class UmbInputEyeDropperElement extends UUIFormControlMixin(UmbLitElement
1618
this.dispatchEvent(new UmbChangeEvent());
1719
}
1820

21+
@property({ type: Boolean, reflect: true })
22+
readonly = false;
1923
@property({ type: Boolean })
20-
opacity = false;
24+
required = false;
25+
@property({ type: String })
26+
requiredMessage = UMB_VALIDATION_EMPTY_LOCALIZATION_KEY;
27+
28+
constructor() {
29+
super();
30+
this.addValidator(
31+
'valueMissing',
32+
() => this.requiredMessage,
33+
() => !this.readonly && this.required && (!this.value || this.value === ''),
34+
);
35+
}
2136

37+
@property({ type: Boolean })
38+
opacity = false;
2239
@property({ type: Boolean })
2340
showPalette = false;
24-
2541
@property({ type: Array })
2642
swatches?: string[];
2743

src/Umbraco.Web.UI.Client/src/packages/documents/documents/components/input-document/input-document.element.ts

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { css, customElement, html, nothing, property, repeat, state, when } from
44
import { jsonStringComparison } from '@umbraco-cms/backoffice/observable-api';
55
import { splitStringToArray } from '@umbraco-cms/backoffice/utils';
66
import { UmbChangeEvent } from '@umbraco-cms/backoffice/event';
7-
import { UmbFormControlMixin } from '@umbraco-cms/backoffice/validation';
7+
import { UMB_VALIDATION_EMPTY_LOCALIZATION_KEY, UmbFormControlMixin } from '@umbraco-cms/backoffice/validation';
88
import { UmbInteractionMemoriesChangeEvent } from '@umbraco-cms/backoffice/interaction-memory';
99
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
1010
import { UmbSorterController } from '@umbraco-cms/backoffice/sorter';
@@ -14,7 +14,7 @@ import type { UmbInteractionMemoryModel } from '@umbraco-cms/backoffice/interact
1414
import type { UmbRepositoryItemsStatus } from '@umbraco-cms/backoffice/repository';
1515

1616
@customElement('umb-input-document')
17-
export class UmbInputDocumentElement extends UmbFormControlMixin<string | undefined, typeof UmbLitElement>(
17+
export class UmbInputDocumentElement extends UmbFormControlMixin<string, typeof UmbLitElement, undefined>(
1818
UmbLitElement,
1919
) {
2020
#sorter = new UmbSorterController<string>(this, {
@@ -54,7 +54,7 @@ export class UmbInputDocumentElement extends UmbFormControlMixin<string | undefi
5454
* @default
5555
*/
5656
@property({ type: String, attribute: 'min-message' })
57-
minMessage = 'This field need more items';
57+
minMessage = 'This field needs more items';
5858

5959
/**
6060
* This is a maximum amount of selected items in this input.
@@ -125,6 +125,10 @@ export class UmbInputDocumentElement extends UmbFormControlMixin<string | undefi
125125
}
126126
}
127127
#readonly = false;
128+
@property({ type: Boolean })
129+
required = false;
130+
@property({ type: String })
131+
requiredMessage = UMB_VALIDATION_EMPTY_LOCALIZATION_KEY;
128132

129133
@property({ type: Array, attribute: false })
130134
public get interactionMemories(): Array<UmbInteractionMemoryModel> | undefined {
@@ -148,16 +152,22 @@ export class UmbInputDocumentElement extends UmbFormControlMixin<string | undefi
148152
constructor() {
149153
super();
150154

155+
this.addValidator(
156+
'valueMissing',
157+
() => this.requiredMessage,
158+
() => !this.readonly && this.required && this.selection.length === 0,
159+
);
160+
151161
this.addValidator(
152162
'rangeUnderflow',
153163
() => this.minMessage,
154-
() => !!this.min && this.selection.length < this.min,
164+
() => !this.readonly && !!this.min && this.selection.length < this.min,
155165
);
156166

157167
this.addValidator(
158168
'rangeOverflow',
159169
() => this.maxMessage,
160-
() => !!this.max && this.selection.length > this.max,
170+
() => !this.readonly && !!this.max && this.selection.length > this.max,
161171
);
162172

163173
this.observe(

src/Umbraco.Web.UI.Client/src/packages/documents/documents/property-editors/document-picker/property-editor-ui-document-picker.element.ts

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,13 @@ import type {
1111
UmbPropertyEditorUiElement,
1212
} from '@umbraco-cms/backoffice/property-editor';
1313
import type { UmbTreeStartNode } from '@umbraco-cms/backoffice/tree';
14+
import { UMB_VALIDATION_EMPTY_LOCALIZATION_KEY, UmbFormControlMixin } from '@umbraco-cms/backoffice/validation';
1415

1516
@customElement('umb-property-editor-ui-document-picker')
16-
export class UmbPropertyEditorUIDocumentPickerElement extends UmbLitElement implements UmbPropertyEditorUiElement {
17-
@property()
18-
public value?: string;
19-
17+
export class UmbPropertyEditorUIDocumentPickerElement
18+
extends UmbFormControlMixin<string, typeof UmbLitElement, undefined>(UmbLitElement)
19+
implements UmbPropertyEditorUiElement
20+
{
2021
public set config(config: UmbPropertyEditorConfigCollection | undefined) {
2122
this.#interactionMemoryManager.setPropertyEditorConfig(config);
2223

@@ -39,6 +40,10 @@ export class UmbPropertyEditorUIDocumentPickerElement extends UmbLitElement impl
3940
*/
4041
@property({ type: Boolean, reflect: true })
4142
readonly = false;
43+
@property({ type: Boolean })
44+
mandatory = false;
45+
@property({ type: String })
46+
mandatoryMessage = UMB_VALIDATION_EMPTY_LOCALIZATION_KEY;
4247

4348
@state()
4449
private _min = 0;
@@ -66,6 +71,10 @@ export class UmbPropertyEditorUIDocumentPickerElement extends UmbLitElement impl
6671
});
6772
}
6873

74+
override firstUpdated() {
75+
this.addFormControlElement(this.shadowRoot!.querySelector('umb-input-document')!);
76+
}
77+
6978
#onChange(event: CustomEvent & { target: UmbInputDocumentElement }) {
7079
this.value = event.target.value;
7180
this.dispatchEvent(new UmbChangeEvent());
@@ -95,6 +104,8 @@ export class UmbPropertyEditorUIDocumentPickerElement extends UmbLitElement impl
95104
.value=${this.value}
96105
@change=${this.#onChange}
97106
?readonly=${this.readonly}
107+
?required=${this.mandatory}
108+
.requiredMessage=${this.mandatoryMessage}
98109
.interactionMemories=${this._interactionMemories}
99110
@interaction-memories-change=${this.#onInputInteractionMemoriesChange}>
100111
</umb-input-document>

src/Umbraco.Web.UI.Client/src/packages/media/media/components/input-media/input-media.element.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -206,12 +206,12 @@ export class UmbInputMediaElement extends UmbFormControlMixin<string | undefined
206206
this.addValidator(
207207
'rangeUnderflow',
208208
() => this.minMessage,
209-
() => !!this.min && this.selection.length < this.min,
209+
() => !this.readonly && !!this.min && this.selection.length < this.min,
210210
);
211211
this.addValidator(
212212
'rangeOverflow',
213213
() => this.maxMessage,
214-
() => !!this.max && this.selection.length > this.max,
214+
() => !this.readonly && !!this.max && this.selection.length > this.max,
215215
);
216216
}
217217

src/Umbraco.Web.UI.Client/src/packages/members/member/components/input-member/input-member.element.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,13 +133,13 @@ export class UmbInputMemberElement extends UmbFormControlMixin<string | undefine
133133
this.addValidator(
134134
'rangeUnderflow',
135135
() => this.minMessage,
136-
() => !!this.min && this.selection.length < this.min,
136+
() => !this.readonly && !!this.min && this.selection.length < this.min,
137137
);
138138

139139
this.addValidator(
140140
'rangeOverflow',
141141
() => this.maxMessage,
142-
() => !!this.max && this.selection.length > this.max,
142+
() => !this.readonly && !!this.max && this.selection.length > this.max,
143143
);
144144

145145
this.observe(this.#pickerContext.selection, (selection) => (this.value = selection.join(',')), '_observeSelection');

src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/components/input-multi-url/input-multi-url.element.ts

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,9 @@ import {
2323
import { UmbMediaItemRepository, UmbMediaUrlRepository } from '@umbraco-cms/backoffice/media';
2424
import { UmbModalRouteRegistrationController } from '@umbraco-cms/backoffice/router';
2525
import { UmbSorterController } from '@umbraco-cms/backoffice/sorter';
26-
import { UUIFormControlMixin } from '@umbraco-cms/backoffice/external/uui';
2726
import type { UmbModalRouteBuilder } from '@umbraco-cms/backoffice/router';
2827
import type { UUIModalSidebarSize } from '@umbraco-cms/backoffice/external/uui';
28+
import { UMB_VALIDATION_EMPTY_LOCALIZATION_KEY, UmbFormControlMixin } from '@umbraco-cms/backoffice/validation';
2929

3030
/**
3131
* @element umb-input-multi-url
@@ -34,7 +34,9 @@ import type { UUIModalSidebarSize } from '@umbraco-cms/backoffice/external/uui';
3434
* @fires focus - when the input gains focus
3535
*/
3636
@customElement('umb-input-multi-url')
37-
export class UmbInputMultiUrlElement extends UUIFormControlMixin(UmbLitElement, '') {
37+
export class UmbInputMultiUrlElement extends UmbFormControlMixin<string, typeof UmbLitElement, undefined>(
38+
UmbLitElement,
39+
) {
3840
#sorter = new UmbSorterController<UmbLinkPickerLink>(this, {
3941
getUniqueOfElement: (element) => {
4042
return element.id;
@@ -88,7 +90,7 @@ export class UmbInputMultiUrlElement extends UUIFormControlMixin(UmbLitElement,
8890
* @attr
8991
* @default
9092
*/
91-
@property({ type: String, attribute: 'min-message' })
93+
@property({ type: String, attribute: 'max-message' })
9294
maxMessage = 'This field exceeds the allowed amount of items';
9395

9496
/**
@@ -150,6 +152,10 @@ export class UmbInputMultiUrlElement extends UUIFormControlMixin(UmbLitElement,
150152
}
151153
}
152154
#readonly = false;
155+
@property({ type: Boolean })
156+
required = false;
157+
@property({ type: String })
158+
requiredMessage = UMB_VALIDATION_EMPTY_LOCALIZATION_KEY;
153159

154160
@state()
155161
private _modalRoute?: UmbModalRouteBuilder;
@@ -165,6 +171,12 @@ export class UmbInputMultiUrlElement extends UUIFormControlMixin(UmbLitElement,
165171
constructor() {
166172
super();
167173

174+
this.addValidator(
175+
'valueMissing',
176+
() => this.requiredMessage,
177+
() => !this.readonly && this.required && (!this.value || this.value === ''),
178+
);
179+
168180
this.addValidator(
169181
'rangeUnderflow',
170182
() => this.minMessage,

src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/property-editor/property-editor-ui-multi-url-picker.element.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import type { UUIModalSidebarSize } from '@umbraco-cms/backoffice/external/uui';
1111

1212
import '../components/input-multi-url/index.js';
1313
import { UmbChangeEvent } from '@umbraco-cms/backoffice/event';
14-
import { UmbFormControlMixin } from '@umbraco-cms/backoffice/validation';
14+
import { UMB_VALIDATION_EMPTY_LOCALIZATION_KEY, UmbFormControlMixin } from '@umbraco-cms/backoffice/validation';
1515

1616
/**
1717
* @element umb-property-editor-ui-multi-url-picker
@@ -38,6 +38,10 @@ export class UmbPropertyEditorUIMultiUrlPickerElement
3838
*/
3939
@property({ type: Boolean, reflect: true })
4040
readonly = false;
41+
@property({ type: Boolean })
42+
mandatory = false;
43+
@property({ type: String })
44+
mandatoryMessage = UMB_VALIDATION_EMPTY_LOCALIZATION_KEY;
4145

4246
#parseInt(value: unknown, fallback: number): number {
4347
const num = Number(value);
@@ -101,6 +105,8 @@ export class UmbPropertyEditorUIMultiUrlPickerElement
101105
.variantId=${this._variantId}
102106
?hide-anchor=${this._hideAnchor}
103107
?readonly=${this.readonly}
108+
?required=${this.mandatory}
109+
.requiredMessage=${this.mandatoryMessage}
104110
@change=${this.#onChange}>
105111
</umb-input-multi-url>
106112
`;

src/Umbraco.Web.UI.Client/src/packages/property-editors/color-picker/property-editor-ui-color-picker.element.ts

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,22 +7,23 @@ import type {
77
import type { UmbSwatchDetails } from '@umbraco-cms/backoffice/models';
88
import type { UUIColorSwatchesEvent } from '@umbraco-cms/backoffice/external/uui';
99
import { UmbChangeEvent } from '@umbraco-cms/backoffice/event';
10+
import { UMB_VALIDATION_EMPTY_LOCALIZATION_KEY, UmbFormControlMixin } from '@umbraco-cms/backoffice/validation';
1011

1112
/**
1213
* @element umb-property-editor-ui-color-picker
1314
*/
1415
@customElement('umb-property-editor-ui-color-picker')
15-
export class UmbPropertyEditorUIColorPickerElement extends UmbLitElement implements UmbPropertyEditorUiElement {
16-
#defaultShowLabels = false;
17-
16+
export class UmbPropertyEditorUIColorPickerElement
17+
extends UmbFormControlMixin<UmbSwatchDetails, typeof UmbLitElement, undefined>(UmbLitElement)
18+
implements UmbPropertyEditorUiElement
19+
{
1820
@property({ type: Object })
19-
public set value(value: UmbSwatchDetails | undefined) {
20-
this.#value = value ? this.#ensureHashPrefix(value) : undefined;
21+
public override set value(value: UmbSwatchDetails | undefined) {
22+
super.value = value ? this.#ensureHashPrefix(value) : undefined;
2123
}
22-
public get value(): UmbSwatchDetails | undefined {
23-
return this.#value;
24+
public override get value(): UmbSwatchDetails | undefined {
25+
return super.value;
2426
}
25-
#value?: UmbSwatchDetails | undefined;
2627

2728
/**
2829
* Sets the input to readonly mode, meaning value cannot be changed but still able to read and select its content.
@@ -32,17 +33,19 @@ export class UmbPropertyEditorUIColorPickerElement extends UmbLitElement impleme
3233
*/
3334
@property({ type: Boolean, reflect: true })
3435
readonly = false;
36+
@property({ type: Boolean }) mandatory = false;
37+
@property({ type: String }) mandatoryMessage = UMB_VALIDATION_EMPTY_LOCALIZATION_KEY;
3538

3639
@state()
37-
private _showLabels = this.#defaultShowLabels;
40+
private _showLabels = false;
3841

3942
@state()
4043
private _swatches: Array<UmbSwatchDetails> = [];
4144

4245
public set config(config: UmbPropertyEditorConfigCollection | undefined) {
4346
if (!config) return;
4447

45-
this._showLabels = config?.getValueByAlias<boolean>('useLabel') ?? this.#defaultShowLabels;
48+
this._showLabels = config?.getValueByAlias<boolean>('useLabel') ?? false;
4649

4750
const swatches = config?.getValueByAlias<Array<UmbSwatchDetails>>('items') ?? [];
4851
this._swatches = swatches.map((swatch) => this.#ensureHashPrefix(swatch));
@@ -62,12 +65,18 @@ export class UmbPropertyEditorUIColorPickerElement extends UmbLitElement impleme
6265
this.dispatchEvent(new UmbChangeEvent());
6366
}
6467

68+
override firstUpdated() {
69+
this.addFormControlElement(this.shadowRoot!.querySelector('umb-input-color')!);
70+
}
71+
6572
override render() {
6673
return html`<umb-input-color
67-
value=${this.value?.value ?? ''}
74+
.value=${this.value?.value}
6875
.swatches=${this._swatches}
6976
?showLabels=${this._showLabels}
7077
@change=${this.#onChange}
78+
?required=${this.mandatory}
79+
.requiredMessage=${this.mandatoryMessage}
7180
?readonly=${this.readonly}></umb-input-color>`;
7281
}
7382
}

0 commit comments

Comments
 (0)