Changed the Image crop UI component to fit the new Backoffice UI design (#19204)

* Changed the Image crop UI component to fit the new Backoffice UI

* style adjustments

---------

Co-authored-by: Niels Lyngsø <niels.lyngso@gmail.com>
This commit is contained in:
Abdulaziz
2025-05-02 08:49:25 +02:00
committed by GitHub
parent 057fd05635
commit d83e9f736d
2 changed files with 107 additions and 107 deletions

View File

@@ -10,7 +10,7 @@ export const manifest: ManifestPropertyEditorSchema = {
properties: [
{
alias: 'crops',
label: 'Define Crops',
label: 'Crop options',
propertyEditorUiAlias: 'Umb.PropertyEditorUi.ImageCropsConfiguration',
},
],

View File

@@ -13,9 +13,6 @@ export type UmbCrop = {
height: number;
};
/**
* @element umb-property-editor-ui-image-crops
*/
@customElement('umb-property-editor-ui-image-crops')
export class UmbPropertyEditorUIImageCropsElement extends UmbLitElement implements UmbPropertyEditorUiElement {
@query('#label')
@@ -24,6 +21,9 @@ export class UmbPropertyEditorUIImageCropsElement extends UmbLitElement implemen
@state()
private _value: Array<UmbCrop> = [];
@state()
private _isCreating = false;
@property({ type: Array })
public set value(value: Array<UmbCrop>) {
this._value = value ?? [];
@@ -39,13 +39,8 @@ export class UmbPropertyEditorUIImageCropsElement extends UmbLitElement implemen
#oldInputValue = '';
#sorter = new UmbSorterController(this, {
getUniqueOfElement: (element: HTMLElement) => {
const unique = element.dataset['alias'];
return unique;
},
getUniqueOfModel: (modelEntry: UmbCrop) => {
return modelEntry.alias;
},
getUniqueOfElement: (element: HTMLElement) => element.dataset['alias'],
getUniqueOfModel: (modelEntry: UmbCrop) => modelEntry.alias,
identifier: 'Umb.SorterIdentifier.ImageCrops',
itemSelector: '.crop',
containerSelector: '.crops',
@@ -64,45 +59,28 @@ export class UmbPropertyEditorUIImageCropsElement extends UmbLitElement implemen
#onEdit(crop: UmbCrop) {
this.editCropAlias = crop.alias;
const form = this.shadowRoot?.querySelector('form') as HTMLFormElement;
if (!form) return;
const label = form.querySelector('#label') as HTMLInputElement;
const alias = form.querySelector('#alias') as HTMLInputElement;
const width = form.querySelector('#width') as HTMLInputElement;
const height = form.querySelector('#height') as HTMLInputElement;
if (!alias || !width || !height) return;
label.value = crop.label;
alias.value = crop.alias;
width.value = crop.width.toString();
height.value = crop.height.toString();
this._isCreating = false;
}
#onEditCancel() {
this.editCropAlias = '';
this._isCreating = false;
}
#onSubmit(event: Event) {
event.preventDefault();
const form = event.target as HTMLFormElement;
if (!form) return;
if (!form.checkValidity()) return;
if (!form || !form.checkValidity()) return;
const formData = new FormData(form);
const label = formData.get('label') as string;
const alias = formData.get('alias') as string;
const width = formData.get('width') as string;
const height = formData.get('height') as string;
if (!label || !alias || !width || !height) return;
if (!this.value) this.value = [];
const newCrop = {
const newCrop: UmbCrop = {
label,
alias,
width: parseInt(width),
@@ -112,7 +90,6 @@ export class UmbPropertyEditorUIImageCropsElement extends UmbLitElement implemen
if (this.editCropAlias) {
const index = this.value.findIndex((item) => item.alias === this.editCropAlias);
if (index === -1) return;
const temp = [...this.value];
temp[index] = newCrop;
this.value = [...temp];
@@ -120,40 +97,14 @@ export class UmbPropertyEditorUIImageCropsElement extends UmbLitElement implemen
} else {
this.value = [...this.value, newCrop];
}
this.dispatchEvent(new UmbChangeEvent());
form.reset();
this._labelInput.focus();
this._labelInput?.focus();
this._isCreating = false;
}
#renderActions() {
return this.editCropAlias
? html`<uui-button @click=${this.#onEditCancel}>Cancel</uui-button>
<uui-button look="secondary" type="submit" label="Save"></uui-button>`
: html`<uui-button look="secondary" type="submit" label="Add"></uui-button>`;
}
#onLabelInput() {
const value = this._labelInput.value ?? '';
const aliasValue = generateAlias(value);
const alias = this.shadowRoot?.querySelector('#alias') as HTMLInputElement;
if (!alias) return;
const oldAliasValue = generateAlias(this.#oldInputValue);
if (alias.value === oldAliasValue || !alias.value) {
alias.value = aliasValue;
}
this.#oldInputValue = value;
}
override render() {
if (!this.value) this.value = [];
#renderForm(initial?: UmbCrop) {
return html`
<uui-form>
<form @submit=${this.#onSubmit}>
@@ -165,54 +116,118 @@ export class UmbPropertyEditorUIImageCropsElement extends UmbLitElement implemen
id="label"
name="label"
type="text"
autocomplete="false"
value=""></uui-input>
required
.value=${initial?.label ?? ''}></uui-input>
</div>
<div class="input">
<uui-label for="alias">Alias</uui-label>
<uui-input label="Alias" id="alias" name="alias" type="text" autocomplete="false" value=""></uui-input>
<uui-input
label="Alias"
id="alias"
name="alias"
type="text"
autocomplete="false"
required
.value=${initial?.alias ?? ''}></uui-input>
</div>
<div class="input">
<uui-label for="width">Width</uui-label>
<uui-input label="Width" id="width" name="width" type="number" autocomplete="false" value="" min="0">
<uui-input
label="Width"
id="width"
name="width"
type="number"
autocomplete="false"
required
.value=${initial?.width ?? ''}
min="0">
<span class="append" slot="append">px</span>
</uui-input>
</div>
<div class="input">
<uui-label for="height">Height</uui-label>
<uui-input label="Height" id="height" name="height" type="number" autocomplete="false" value="" min="0">
<uui-input
label="Height"
id="height"
name="height"
type="number"
autocomplete="false"
required
.value=${initial?.height ?? ''}
min="0">
<span class="append" slot="append">px</span>
</uui-input>
</div>
<div class="action-wrapper">${this.#renderActions()}</div>
<div class="action-wrapper">
${this.editCropAlias
? html`<uui-button @click=${this.#onEditCancel}>Cancel</uui-button>
<uui-button look="secondary" type="submit" label=${this.localize.term('general_edit')}></uui-button>`
: html`<uui-button
look="secondary"
type="submit"
label=${this.localize.term('general_create')}></uui-button>`}
</div>
</form>
</uui-form>
<div class="crops">
`;
}
#onLabelInput() {
const value = this._labelInput.value ?? '';
const aliasValue = generateAlias(value);
const alias = this.shadowRoot?.querySelector('#alias') as HTMLInputElement;
if (!alias) return;
const oldAliasValue = generateAlias(this.#oldInputValue);
if (alias.value === oldAliasValue || !alias.value) {
alias.value = aliasValue;
}
this.#oldInputValue = value;
}
override render() {
return html`
<uui-ref-list class="crops">
${repeat(
this.value,
(item) => item.alias,
(item) => html`
<div class="crop" data-alias="${item.alias}">
<uui-icon name="icon-grip" class="crop-drag"></uui-icon>
<span><strong>${item.label}</strong> <em>(${item.alias})</em></span>
<span class="crop-size">(${item.width} x ${item.height}px)</span>
<div class="crop-actions">
<uui-button
label=${this.localize.term('general_edit')}
color="default"
@click=${() => this.#onEdit(item)}></uui-button>
<uui-button
label=${this.localize.term('general_remove')}
color="danger"
@click=${() => this.#onRemove(item.alias)}></uui-button>
</div>
</div>
${this.editCropAlias === item.alias
? html`<div class="crop-form">${this.#renderForm(item)}</div>`
: html`
<uui-ref-node
class="crop"
data-alias="${item.alias}"
detail="${item.width} x ${item.height}px"
name="${item.label} (${item.alias})">
<uui-icon slot="icon" name="icon-crop"></uui-icon>
<uui-action-bar slot="actions">
<uui-button
label=${this.localize.term('general_edit')}
data-mark="action:crop-edit"
@click=${() => this.#onEdit(item)}></uui-button>
<uui-button
label=${this.localize.term('general_remove')}
data-mark="action:crop-remove"
@click=${() => this.#onRemove(item.alias)}></uui-button>
</uui-action-bar>
</uui-ref-node>
`}
`,
)}
</div>
</uui-ref-list>
${!this._isCreating && !this.editCropAlias
? html`<uui-button
id="create"
data-mark="action:crop-create"
look="placeholder"
@click=${() => (this._isCreating = true)}
label=${this.localize.term('general_create')}></uui-button>`
: ''}
${this._isCreating ? this.#renderForm() : ''}
`;
}
static override readonly styles = [
UmbTextStyles,
css`
@@ -228,34 +243,16 @@ export class UmbPropertyEditorUIImageCropsElement extends UmbLitElement implemen
.crop {
display: flex;
align-items: center;
background: var(--uui-color-background);
}
.crop-drag {
cursor: grab;
padding-inline: var(--uui-size-space-4);
color: var(--uui-color-disabled-contrast);
font-weight: bold;
}
.crop-drag:active {
cursor: grabbing;
}
.crop-size {
font-size: 0.9em;
padding-inline: var(--uui-size-space-4);
}
.crop-actions {
display: flex;
margin-left: auto;
}
form {
display: flex;
gap: var(--uui-size-space-2);
flex-wrap: wrap;
}
.input {
display: flex;
flex-direction: column;
flex: 1 1 200px;
}
uui-input[type='number'] {
text-align: right;
@@ -273,6 +270,9 @@ export class UmbPropertyEditorUIImageCropsElement extends UmbLitElement implemen
display: flex;
align-items: flex-end;
}
#create {
width: 100%;
}
`,
];
}