Feature: Sorter for block grid and list config editor

This commit is contained in:
Lone Iversen
2024-03-21 11:10:19 +01:00
committed by Jacob Overgaard
parent f6135adfd4
commit 467e770789
3 changed files with 77 additions and 12 deletions

View File

@@ -35,6 +35,7 @@ export class UmbPropertyEditorUIBlockGridTypeConfigurationElement
extends UmbLitElement
implements UmbPropertyEditorUiElement
{
#moveData?: Array<UmbBlockTypeWithGroupKey>;
#sorter = new UmbSorterController<MappedGroupWithBlockTypes, HTMLElement>(this, {
getUniqueOfElement: (element) => element.getAttribute('data-umb-group-key'),
getUniqueOfModel: (modelEntry) => modelEntry.key!,
@@ -128,13 +129,40 @@ export class UmbPropertyEditorUIBlockGridTypeConfigurationElement
this.#sorter.setModel(this._groupsWithBlockTypes);
}
#onChange(e: CustomEvent, groupKey?: string) {
#onDelete(e: CustomEvent, groupKey?: string) {
const updatedValues = (e.target as UmbInputBlockTypeElement).value.map((value) => ({ ...value, groupKey }));
const filteredValues = this.value.filter((value) => value.groupKey !== groupKey);
this.value = [...filteredValues, ...updatedValues];
this.dispatchEvent(new UmbPropertyValueChangeEvent());
}
async #onChange(e: CustomEvent) {
e.stopPropagation();
const element = e.target as UmbInputBlockTypeElement;
const value = element.value;
if (!e.detail?.moveComplete) {
// Container change, store data of the new group...
const newGroupKey = element.getAttribute('data-umb-group-key');
const movedItem = e.detail?.item as UmbBlockTypeWithGroupKey;
// Check if item moved back to original group...
movedItem.groupKey === newGroupKey
? (this.#moveData = undefined)
: (this.#moveData = value.map((block) => ({ ...block, groupKey: newGroupKey })));
} else if (e.detail?.moveComplete) {
// Move complete, get the blocks that were in an untouched group
const blocks = this.value
.filter((block) => !value.find((value) => value.contentElementTypeKey === block.contentElementTypeKey))
.filter(
(block) => !this.#moveData?.find((value) => value.contentElementTypeKey === block.contentElementTypeKey),
);
this.value = this.#moveData ? [...blocks, ...value, ...this.#moveData] : [...blocks, ...value];
this.dispatchEvent(new UmbPropertyValueChangeEvent());
this.#moveData = undefined;
}
}
#onCreate(e: CustomEvent, groupKey?: string) {
const selectedElementType = e.detail.contentElementTypeKey;
if (selectedElementType) {
@@ -170,8 +198,9 @@ export class UmbPropertyEditorUIBlockGridTypeConfigurationElement
? html`<umb-input-block-type
.value=${this._notGroupedBlockTypes}
.workspacePath=${this._workspacePath}
@change=${this.#onChange}
@create=${(e: CustomEvent) => this.#onCreate(e, undefined)}
@change=${(e: CustomEvent) => this.#onChange(e, undefined)}></umb-input-block-type>`
@delete=${(e: CustomEvent) => this.#onDelete(e, undefined)}></umb-input-block-type>`
: ''}
${repeat(
this._groupsWithBlockTypes,
@@ -180,10 +209,12 @@ export class UmbPropertyEditorUIBlockGridTypeConfigurationElement
html`<div class="group" data-umb-group-key=${ifDefined(group.key)}>
${group.key ? this.#renderGroupInput(group.key, group.name) : nothing}
<umb-input-block-type
data-umb-group-key=${group.key}
.value=${group.blocks}
.workspacePath=${this._workspacePath}
@change=${this.#onChange}
@create=${(e: CustomEvent) => this.#onCreate(e, group.key)}
@change=${(e: CustomEvent) => this.#onChange(e, group.key)}></umb-input-block-type>
@delete=${(e: CustomEvent) => this.#onDelete(e, group.key)}></umb-input-block-type>
</div>`,
)}
</div>`;

View File

@@ -3,7 +3,10 @@ import '../../../block-type/components/input-block-type/index.js';
import { UMB_BLOCK_LIST_TYPE } from '../../types.js';
import type { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry';
import { html, customElement, property, state } from '@umbraco-cms/backoffice/external/lit';
import type { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor';
import {
UmbPropertyValueChangeEvent,
type UmbPropertyEditorConfigCollection,
} from '@umbraco-cms/backoffice/property-editor';
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
import { UMB_WORKSPACE_MODAL, UmbModalRouteRegistrationController } from '@umbraco-cms/backoffice/modal';
@@ -51,14 +54,19 @@ export class UmbPropertyEditorUIBlockListBlockConfigurationElement
}
}
#onChange(e: CustomEvent) {
e.stopPropagation();
this.value = (e.target as UmbInputBlockTypeElement).value;
this.dispatchEvent(new UmbPropertyValueChangeEvent());
}
render() {
return html`<umb-input-block-type
.value=${this.value}
.workspacePath=${this._workspacePath}
@create=${this.#onCreate}
@change=${(e: Event) => {
this.value = (e.target as UmbInputBlockTypeElement).value;
}}></umb-input-block-type>`;
@delete=${this.#onChange}
@change=${this.#onChange}></umb-input-block-type>`;
}
}

View File

@@ -1,20 +1,41 @@
import type { UmbBlockTypeBaseModel } from '../../types.js';
import type { UmbBlockTypeCardElement } from '../block-type-card/index.js';
import type { UmbBlockTypeBaseModel, UmbBlockTypeWithGroupKey } from '../../types.js';
import { UMB_MODAL_MANAGER_CONTEXT, umbConfirmModal } from '@umbraco-cms/backoffice/modal';
import '../block-type-card/index.js';
import { css, html, customElement, property, state, repeat } from '@umbraco-cms/backoffice/external/lit';
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
import type { UmbPropertyDatasetContext } from '@umbraco-cms/backoffice/property';
import { UMB_PROPERTY_DATASET_CONTEXT } from '@umbraco-cms/backoffice/property';
import { UmbChangeEvent } from '@umbraco-cms/backoffice/event';
import { UmbDeleteEvent } from '@umbraco-cms/backoffice/event';
import { UMB_DOCUMENT_TYPE_PICKER_MODAL } from '@umbraco-cms/backoffice/document-type';
import { UmbSorterController } from '@umbraco-cms/backoffice/sorter';
@customElement('umb-input-block-type')
export class UmbInputBlockTypeElement<
BlockType extends UmbBlockTypeBaseModel = UmbBlockTypeBaseModel,
BlockType extends UmbBlockTypeWithGroupKey = UmbBlockTypeWithGroupKey,
> extends UmbLitElement {
#sorter = new UmbSorterController<BlockType, UmbBlockTypeCardElement>(this, {
getUniqueOfElement: (element) => element.contentElementTypeKey,
getUniqueOfModel: (modelEntry) => modelEntry.contentElementTypeKey!,
itemSelector: 'umb-block-type-card',
identifier: 'umb-block-type-sorter',
containerSelector: '#blocks',
onChange: ({ model }) => {
this._items = model;
},
onContainerChange: ({ model, item }) => {
this._items = model;
this.dispatchEvent(new CustomEvent('change', { detail: { item } }));
},
onEnd: () => {
this.dispatchEvent(new CustomEvent('change', { detail: { moveComplete: true } }));
},
});
@property({ type: Array, attribute: false })
public set value(items) {
this._items = items ?? [];
this.#sorter.setModel(this._items);
}
public get value() {
return this._items;
@@ -67,7 +88,7 @@ export class UmbInputBlockTypeElement<
deleteItem(contentElementTypeKey: string) {
this.value = this.value.filter((x) => x.contentElementTypeKey !== contentElementTypeKey);
this.dispatchEvent(new UmbChangeEvent());
this.dispatchEvent(new UmbDeleteEvent());
}
protected getFormElement() {
@@ -85,7 +106,7 @@ export class UmbInputBlockTypeElement<
}
render() {
return html`<div>
return html`<div id="blocks">
${repeat(this.value, (block) => block.contentElementTypeKey, this.#renderItem)} ${this.#renderButton()}
</div>`;
}
@@ -93,6 +114,7 @@ export class UmbInputBlockTypeElement<
#renderItem = (block: BlockType) => {
return html`
<umb-block-type-card
.data-umb-content-element-key=${block.contentElementTypeKey}
.name=${block.label}
.iconColor=${block.iconColor}
.backgroundColor=${block.backgroundColor}
@@ -125,6 +147,10 @@ export class UmbInputBlockTypeElement<
grid-template-rows: repeat(auto-fill, minmax(160px, 1fr));
}
[drag-placeholder] {
opacity: 0.5;
}
#add-button {
text-align: center;
min-height: 150px;