Icon Picker: Fix empty selection allowed on mandatory fields and add validation. (#20536)

* Not show the empty tile when filtering is active.

* Added mandatory property to the icon picker.

* Avoid deselecting the icon on second click when not showing the empty option.

* Extends the form control mixin to the icon picker.

* Used super.value.

* Support mandatory from settings config.

* Removed mandatoryConf.

* remove requestUpdate

---------

Co-authored-by: Niels Lyngsø <niels.lyngso@gmail.com>
This commit is contained in:
Engiber Lozada
2025-10-23 15:49:14 +02:00
committed by GitHub
parent 3854b2bd53
commit 8434c7d0cb
3 changed files with 54 additions and 22 deletions

View File

@@ -30,6 +30,9 @@ export class UmbIconPickerModalElement extends UmbModalBaseElement<UmbIconPicker
@state()
private _colorList = umbracoColors.filter((color) => !color.legacy);
@state()
private _isSearching = false;
constructor() {
super();
this.consumeContext(UMB_ICON_REGISTRY_CONTEXT, (context) => {
@@ -44,8 +47,10 @@ export class UmbIconPickerModalElement extends UmbModalBaseElement<UmbIconPicker
if (!this.#icons) return;
const value = this._searchInput?.value;
if (value) {
this._isSearching = value.length > 0;
this._iconsFiltered = this.#icons.filter((icon) => icon.name.toLowerCase().includes(value.toLowerCase()));
} else {
this._isSearching = false;
this._iconsFiltered = this.#icons;
}
}
@@ -54,8 +59,12 @@ export class UmbIconPickerModalElement extends UmbModalBaseElement<UmbIconPicker
const isActivate = e.type === 'click' || (e.type === 'keyup' && (e as KeyboardEvent).key === 'Enter');
if (!isActivate) return;
const nextIcon = this.value.icon === iconName ? '' : iconName;
this.modalContext?.updateValue({ icon: nextIcon });
if (this.data?.showEmptyOption) {
const nextIcon = this.value.icon === iconName ? '' : iconName;
this.modalContext?.updateValue({ icon: nextIcon });
} else {
this.modalContext?.updateValue({ icon: iconName });
}
}
#onColorChange(e: UUIColorSwatchesEvent) {
@@ -93,16 +102,21 @@ export class UmbIconPickerModalElement extends UmbModalBaseElement<UmbIconPicker
</uui-color-swatches>
<hr />
<uui-scroll-container id="icons">
<uui-button
class=${!this.value.icon ? 'selected' : ''}
label=${this.localize.term('defaultdialogs_noIcon')}
title=${this.localize.term('defaultdialogs_noIcon')}
@click=${this.#clearIcon}
@keyup=${(e: KeyboardEvent) => {
if (e.key === 'Enter' || e.key === ' ') this.#clearIcon();
}}>
<uui-icon style="opacity:.35" name=${ifDefined(this.data?.placeholder)}></uui-icon> </uui-button
>${this.renderIcons()}</uui-scroll-container
${this.data?.showEmptyOption && !this._isSearching
? html`
<uui-button
class=${!this.value.icon ? 'selected' : ''}
label=${this.localize.term('defaultdialogs_noIcon')}
title=${this.localize.term('defaultdialogs_noIcon')}
@click=${this.#clearIcon}
@keyup=${(e: KeyboardEvent) => {
if (e.key === 'Enter' || e.key === ' ') this.#clearIcon();
}}>
<uui-icon style="opacity:.35" name=${ifDefined(this.data?.placeholder)}></uui-icon>
</uui-button>
`
: nothing}
${this.renderIcons()}</uui-scroll-container
>
</div>
<uui-button

View File

@@ -2,6 +2,7 @@ import { UmbModalToken } from '@umbraco-cms/backoffice/modal';
export interface UmbIconPickerModalData {
placeholder?: string;
showEmptyOption?: boolean;
}
export interface UmbIconPickerModalValue {

View File

@@ -9,28 +9,45 @@ import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
import { extractUmbColorVariable } from '@umbraco-cms/backoffice/resources';
import { UmbChangeEvent } from '@umbraco-cms/backoffice/event';
import { UmbFormControlMixin } from '@umbraco-cms/backoffice/validation';
/**
* @element umb-property-editor-ui-icon-picker
*/
@customElement('umb-property-editor-ui-icon-picker')
export class UmbPropertyEditorUIIconPickerElement extends UmbLitElement implements UmbPropertyEditorUiElement {
//
export class UmbPropertyEditorUIIconPickerElement
extends UmbFormControlMixin<string, typeof UmbLitElement, undefined>(UmbLitElement, undefined)
implements UmbPropertyEditorUiElement
{
@property({ type: Boolean })
mandatory = false;
protected override firstUpdated(): void {
this.addValidator(
'valueMissing',
() => 'Icon is required',
() => this.mandatory && !this._icon,
);
}
@property()
public set value(v: string) {
this._value = v ?? '';
const parts = this._value.split(' ');
public override set value(v: string) {
const val = v ?? '';
super.value = val;
const parts = val.split(' ');
if (parts.length === 2) {
this._icon = parts[0];
this._color = parts[1].replace('color-', '');
} else {
this._icon = this._value;
this._icon = val;
this._color = '';
}
}
public get value() {
return this._value;
public override get value() {
return (super.value as string) ?? '';
}
private _value = '';
@state()
private _icon = '';
@@ -53,7 +70,7 @@ export class UmbPropertyEditorUIIconPickerElement extends UmbLitElement implemen
icon: this._icon,
color: this._color,
},
data: { placeholder: this._placeholderIcon },
data: { placeholder: this._placeholderIcon, showEmptyOption: !this.mandatory },
}).catch(() => undefined);
if (!data) return;