Merge pull request #18108 from umbraco/v15/bugfix/17372
Fix: Mandatory for Image Cropper (17372)
This commit is contained in:
@@ -12,6 +12,7 @@ import './image-cropper.element.js';
|
||||
import './image-cropper-focus-setter.element.js';
|
||||
import './image-cropper-preview.element.js';
|
||||
import './image-cropper-field.element.js';
|
||||
import { UMB_VALIDATION_EMPTY_LOCALIZATION_KEY, UmbFormControlMixin } from '@umbraco-cms/backoffice/validation';
|
||||
|
||||
const DefaultFocalPoint = { left: 0.5, top: 0.5 };
|
||||
const DefaultValue = {
|
||||
@@ -22,12 +23,23 @@ const DefaultValue = {
|
||||
};
|
||||
|
||||
@customElement('umb-input-image-cropper')
|
||||
export class UmbInputImageCropperElement extends UmbLitElement {
|
||||
export class UmbInputImageCropperElement extends UmbFormControlMixin<
|
||||
UmbImageCropperPropertyEditorValue,
|
||||
typeof UmbLitElement,
|
||||
undefined
|
||||
>(UmbLitElement, undefined) {
|
||||
@query('#dropzone')
|
||||
private _dropzone?: UUIFileDropzoneElement;
|
||||
|
||||
@property({ attribute: false })
|
||||
value: UmbImageCropperPropertyEditorValue = DefaultValue;
|
||||
/**
|
||||
* 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;
|
||||
|
||||
@property({ attribute: false })
|
||||
crops: UmbImageCropperPropertyEditorValue['crops'] = [];
|
||||
@@ -43,6 +55,14 @@ export class UmbInputImageCropperElement extends UmbLitElement {
|
||||
constructor() {
|
||||
super();
|
||||
this.#manager = new UmbTemporaryFileManager(this);
|
||||
|
||||
this.addValidator(
|
||||
'valueMissing',
|
||||
() => this.requiredMessage ?? UMB_VALIDATION_EMPTY_LOCALIZATION_KEY,
|
||||
() => {
|
||||
return !!this.required && (!this.value || (this.value.src === '' && this.value.temporaryFileId == null));
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
protected override firstUpdated(): void {
|
||||
@@ -57,7 +77,7 @@ export class UmbInputImageCropperElement extends UmbLitElement {
|
||||
this.file = file;
|
||||
this.fileUnique = unique;
|
||||
|
||||
this.value = assignToFrozenObject(this.value, { temporaryFileId: unique });
|
||||
this.value = assignToFrozenObject(this.value ?? DefaultValue, { temporaryFileId: unique });
|
||||
|
||||
this.#manager?.uploadOne({ temporaryUnique: unique, file });
|
||||
|
||||
@@ -71,7 +91,7 @@ export class UmbInputImageCropperElement extends UmbLitElement {
|
||||
}
|
||||
|
||||
#onRemove = () => {
|
||||
this.value = assignToFrozenObject(this.value, DefaultValue);
|
||||
this.value = undefined;
|
||||
if (this.fileUnique) {
|
||||
this.#manager?.removeOne(this.fileUnique);
|
||||
}
|
||||
@@ -82,25 +102,27 @@ export class UmbInputImageCropperElement extends UmbLitElement {
|
||||
};
|
||||
|
||||
#mergeCrops() {
|
||||
// Replace crops from the value with the crops from the config while keeping the coordinates from the value if they exist.
|
||||
const filteredCrops = this.crops.map((crop) => {
|
||||
const cropFromValue = this.value.crops.find((valueCrop) => valueCrop.alias === crop.alias);
|
||||
const result = {
|
||||
...crop,
|
||||
coordinates: cropFromValue?.coordinates ?? undefined,
|
||||
if (this.value) {
|
||||
// Replace crops from the value with the crops from the config while keeping the coordinates from the value if they exist.
|
||||
const filteredCrops = this.crops.map((crop) => {
|
||||
const cropFromValue = this.value!.crops.find((valueCrop) => valueCrop.alias === crop.alias);
|
||||
const result = {
|
||||
...crop,
|
||||
coordinates: cropFromValue?.coordinates ?? undefined,
|
||||
};
|
||||
|
||||
return result;
|
||||
});
|
||||
|
||||
this.value = {
|
||||
...this.value,
|
||||
crops: filteredCrops,
|
||||
};
|
||||
|
||||
return result;
|
||||
});
|
||||
|
||||
this.value = {
|
||||
...this.value,
|
||||
crops: filteredCrops,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
override render() {
|
||||
if (this.value.src || this.file) {
|
||||
if (this.value?.src || this.file) {
|
||||
return this.#renderImageCropper();
|
||||
}
|
||||
|
||||
@@ -119,7 +141,7 @@ export class UmbInputImageCropperElement extends UmbLitElement {
|
||||
const value = (e.target as UmbInputImageCropperFieldElement).value;
|
||||
|
||||
if (!value) {
|
||||
this.value = DefaultValue;
|
||||
this.value = undefined;
|
||||
this.dispatchEvent(new UmbChangeEvent());
|
||||
return;
|
||||
}
|
||||
@@ -128,7 +150,9 @@ export class UmbInputImageCropperElement extends UmbLitElement {
|
||||
value.temporaryFileId = this.value.temporaryFileId;
|
||||
}
|
||||
|
||||
this.value = value;
|
||||
if (value.temporaryFileId || value.src !== '') {
|
||||
this.value = value;
|
||||
}
|
||||
this.dispatchEvent(new UmbChangeEvent());
|
||||
}
|
||||
|
||||
|
||||
@@ -1,59 +1,62 @@
|
||||
import type { UmbImageCropperPropertyEditorValue, UmbInputImageCropperElement } from '../../components/index.js';
|
||||
import { html, customElement, property, nothing, state } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { html, customElement, property, state } from '@umbraco-cms/backoffice/external/lit';
|
||||
import {
|
||||
type UmbPropertyEditorUiElement,
|
||||
UmbPropertyValueChangeEvent,
|
||||
type UmbPropertyEditorConfigCollection,
|
||||
} from '@umbraco-cms/backoffice/property-editor';
|
||||
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
|
||||
import { UMB_VALIDATION_EMPTY_LOCALIZATION_KEY, UmbFormControlMixin } from '@umbraco-cms/backoffice/validation';
|
||||
|
||||
import '../../components/input-image-cropper/input-image-cropper.element.js';
|
||||
|
||||
/**
|
||||
* @element umb-property-editor-ui-image-cropper
|
||||
*/
|
||||
@customElement('umb-property-editor-ui-image-cropper')
|
||||
export class UmbPropertyEditorUIImageCropperElement extends UmbLitElement implements UmbPropertyEditorUiElement {
|
||||
@property({ attribute: false })
|
||||
value: UmbImageCropperPropertyEditorValue = {
|
||||
temporaryFileId: null,
|
||||
src: '',
|
||||
crops: [],
|
||||
focalPoint: { left: 0.5, top: 0.5 },
|
||||
};
|
||||
export class UmbPropertyEditorUIImageCropperElement
|
||||
extends UmbFormControlMixin<UmbImageCropperPropertyEditorValue | undefined, typeof UmbLitElement, undefined>(
|
||||
UmbLitElement,
|
||||
)
|
||||
implements UmbPropertyEditorUiElement
|
||||
{
|
||||
/**
|
||||
* Sets the input to mandatory, meaning validation will fail if the value is empty.
|
||||
* @type {boolean}
|
||||
*/
|
||||
@property({ type: Boolean })
|
||||
mandatory?: boolean;
|
||||
|
||||
@property({ type: String })
|
||||
mandatoryMessage = UMB_VALIDATION_EMPTY_LOCALIZATION_KEY;
|
||||
|
||||
@state()
|
||||
crops: UmbImageCropperPropertyEditorValue['crops'] = [];
|
||||
|
||||
override updated(changedProperties: Map<string | number | symbol, unknown>) {
|
||||
super.updated(changedProperties);
|
||||
if (changedProperties.has('value')) {
|
||||
if (!this.value) {
|
||||
this.value = {
|
||||
temporaryFileId: null,
|
||||
src: '',
|
||||
crops: [],
|
||||
focalPoint: { left: 0.5, top: 0.5 },
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public set config(config: UmbPropertyEditorConfigCollection | undefined) {
|
||||
this.crops = config?.getValueByAlias<UmbImageCropperPropertyEditorValue['crops']>('crops') ?? [];
|
||||
}
|
||||
|
||||
override firstUpdated() {
|
||||
this.addFormControlElement(this.shadowRoot!.querySelector('umb-input-image-cropper')!);
|
||||
}
|
||||
|
||||
override focus() {
|
||||
return this.shadowRoot?.querySelector('umb-input-image-cropper')?.focus();
|
||||
}
|
||||
|
||||
#onChange(e: Event) {
|
||||
this.value = (e.target as UmbInputImageCropperElement).value;
|
||||
this.dispatchEvent(new UmbPropertyValueChangeEvent());
|
||||
}
|
||||
|
||||
override render() {
|
||||
if (!this.value) return nothing;
|
||||
|
||||
return html`<umb-input-image-cropper
|
||||
@change=${this.#onChange}
|
||||
.value=${this.value}
|
||||
.crops=${this.crops}></umb-input-image-cropper>`;
|
||||
.crops=${this.crops}
|
||||
.required=${this.mandatory}
|
||||
.requiredMessage=${this.mandatoryMessage}></umb-input-image-cropper>`;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user