diff --git a/src/Umbraco.Web.UI.Client/examples/sorter-with-two-containers/sorter-dashboard.ts b/src/Umbraco.Web.UI.Client/examples/sorter-with-two-containers/sorter-dashboard.ts index a1a799fbc5..73bc865020 100644 --- a/src/Umbraco.Web.UI.Client/examples/sorter-with-two-containers/sorter-dashboard.ts +++ b/src/Umbraco.Web.UI.Client/examples/sorter-with-two-containers/sorter-dashboard.ts @@ -1,13 +1,47 @@ import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; -import { css, html, customElement, LitElement, state, repeat } from '@umbraco-cms/backoffice/external/lit'; +import { css, html, customElement, LitElement } from '@umbraco-cms/backoffice/external/lit'; import { UmbElementMixin } from '@umbraco-cms/backoffice/element-api'; +import type { ModelEntryType } from './sorter-group.js'; + import './sorter-group.js'; @customElement('example-sorter-dashboard') export class ExampleSorterDashboard extends UmbElementMixin(LitElement) { + groupOneItems: ModelEntryType[] = [ + { + name: 'Apple', + }, + { + name: 'Banana', + }, + { + name: 'Pear', + }, + { + name: 'Pineapple', + }, + { + name: 'Lemon', + }, + ]; + + groupTwoItems: ModelEntryType[] = [ + { + name: 'DXP', + }, + { + name: 'H5YR', + }, + { + name: 'UUI', + }, + ]; + render() { return html` - - + +
+ +
`; } @@ -22,7 +56,6 @@ export class ExampleSorterDashboard extends UmbElementMixin(LitElement) { .outer-wrapper { display: flex; - flex-direction: row; } `, ]; diff --git a/src/Umbraco.Web.UI.Client/examples/sorter-with-two-containers/sorter-group.ts b/src/Umbraco.Web.UI.Client/examples/sorter-with-two-containers/sorter-group.ts index 1db8013490..9460dfe91a 100644 --- a/src/Umbraco.Web.UI.Client/examples/sorter-with-two-containers/sorter-group.ts +++ b/src/Umbraco.Web.UI.Client/examples/sorter-with-two-containers/sorter-group.ts @@ -1,20 +1,21 @@ import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; -import { css, html, customElement, LitElement, state, repeat } from '@umbraco-cms/backoffice/external/lit'; +import { css, html, customElement, LitElement, repeat, property, state } from '@umbraco-cms/backoffice/external/lit'; import { UmbElementMixin } from '@umbraco-cms/backoffice/element-api'; import { UmbSorterConfig, UmbSorterController } from '@umbraco-cms/backoffice/sorter'; import './sorter-item.js'; +import ExampleSorterItem from './sorter-item.js'; -type ModelEntryType = { +export type ModelEntryType = { name: string; }; -const SORTER_CONFIG: UmbSorterConfig = { - compareElementToModel: (element: HTMLElement, model: ModelEntryType) => { - return element.getAttribute('name') === model.name; +const SORTER_CONFIG: UmbSorterConfig = { + compareElementToModel: (element, model) => { + return element.name === model.name; }, - querySelectModelToElement: (container: HTMLElement, modelEntry: ModelEntryType) => { - return container.querySelector('name[' + modelEntry.name + ']'); + querySelectModelToElement: (container, modelEntry) => { + return container.querySelector("example-sorter-item[name='" + modelEntry.name + "']"); }, identifier: 'string-that-identifies-all-example-sorters', itemSelector: 'example-sorter-item', @@ -23,37 +24,28 @@ const SORTER_CONFIG: UmbSorterConfig = { @customElement('example-sorter-group') export class ExampleSorterGroup extends UmbElementMixin(LitElement) { - @state() - _items: ModelEntryType[] = [ - { - name: 'Apple', - }, - { - name: 'Banana', - }, - { - name: 'Pear', - }, - { - name: 'Pineapple', - }, - { - name: 'Lemon', - }, - ]; + @property({ type: Array, attribute: false }) + public get items(): ModelEntryType[] { + return this._items; + } + public set items(value: ModelEntryType[]) { + this._items = value; + this.#sorter.setModel(this._items); + } + private _items: ModelEntryType[] = []; - #sorter = new UmbSorterController(this, { + #sorter = new UmbSorterController(this, { ...SORTER_CONFIG, performItemInsert: ({ item, newIndex }) => { this._items.splice(newIndex, 0, item); - console.log('inserted', item.name, 'at', newIndex, ' ', this._items.map((x) => x.name).join(', ')); + //console.log('inserted', item.name, 'at', newIndex, ' ', this._items.map((x) => x.name).join(', ')); this.requestUpdate('_items'); return true; }, performItemRemove: ({ item }) => { const indexToMove = this._items.findIndex((x) => x.name === item.name); this._items.splice(indexToMove, 1); - console.log('removed', item.name, 'at', indexToMove, ' ', this._items.map((x) => x.name).join(', ')); + //console.log('removed', item.name, 'at', indexToMove, ' ', this._items.map((x) => x.name).join(', ')); this.requestUpdate('_items'); return true; }, @@ -61,7 +53,6 @@ export class ExampleSorterGroup extends UmbElementMixin(LitElement) { constructor() { super(); - this.#sorter.setModel(this._items); } removeItem = (item: ModelEntryType) => { @@ -74,7 +65,7 @@ export class ExampleSorterGroup extends UmbElementMixin(LitElement) {
${repeat( this._items, - (item) => item, + (item, index) => item.name + '_ ' + index, (item) => html` @@ -89,6 +80,7 @@ export class ExampleSorterGroup extends UmbElementMixin(LitElement) { css` :host { display: block; + width: 100%; } .sorter-placeholder { diff --git a/src/Umbraco.Web.UI.Client/examples/sorter-with-two-containers/sorter-item.ts b/src/Umbraco.Web.UI.Client/examples/sorter-with-two-containers/sorter-item.ts index 2f22b1c4c4..38ad5d12d1 100644 --- a/src/Umbraco.Web.UI.Client/examples/sorter-with-two-containers/sorter-item.ts +++ b/src/Umbraco.Web.UI.Client/examples/sorter-with-two-containers/sorter-item.ts @@ -14,7 +14,7 @@ export class ExampleSorterItem extends UmbElementMixin(LitElement) { render() { return html` ${this.name} - + `; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/sorter/sorter.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/core/sorter/sorter.controller.ts index 50ae7b19ee..d159bf7f9f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/sorter/sorter.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/sorter/sorter.controller.ts @@ -61,9 +61,9 @@ function destroyPreventEvent(element: Element) { element.removeAttribute('draggable'); } -type INTERNAL_UmbSorterConfig = { - compareElementToModel: (el: HTMLElement, modelEntry: T) => boolean; - querySelectModelToElement: (container: HTMLElement, modelEntry: T) => HTMLElement | null; +type INTERNAL_UmbSorterConfig = { + compareElementToModel: (el: ElementType, modelEntry: T) => boolean; + querySelectModelToElement: (container: HTMLElement, modelEntry: T) => ElementType | null; identifier: string; itemSelector: string; disabledItemSelector?: string; @@ -74,14 +74,14 @@ type INTERNAL_UmbSorterConfig = { draggableSelector?: string; boundarySelector?: string; dataTransferResolver?: (dataTransfer: DataTransfer | null, currentItem: T) => void; - onStart?: (argument: { item: T; element: HTMLElement }) => void; - onChange?: (argument: { item: T; element: HTMLElement }) => void; - onContainerChange?: (argument: { item: T; element: HTMLElement }) => void; - onEnd?: (argument: { item: T; element: HTMLElement }) => void; + onStart?: (argument: { item: T; element: ElementType }) => void; + onChange?: (argument: { item: T; element: ElementType }) => void; + onContainerChange?: (argument: { item: T; element: ElementType }) => void; + onEnd?: (argument: { item: T; element: ElementType }) => void; onSync?: (argument: { item: T; - fromController: UmbSorterController; - toController: UmbSorterController; + fromController: UmbSorterController; + toController: UmbSorterController; }) => void; itemHasNestedContainersResolver?: (element: HTMLElement) => boolean; onDisallowed?: () => void; @@ -91,9 +91,9 @@ type INTERNAL_UmbSorterConfig = { containerElement: Element; containerRect: DOMRect; item: T; - element: HTMLElement; + element: ElementType; elementRect: DOMRect; - relatedElement: HTMLElement; + relatedElement: ElementType; relatedRect: DOMRect; placeholderIsInThisRow: boolean; horizontalPlaceAfter: boolean; @@ -103,8 +103,11 @@ type INTERNAL_UmbSorterConfig = { }; // External type with some properties optional, as they have defaults: -export type UmbSorterConfig = Omit, 'ignorerSelector' | 'containerSelector'> & - Partial, 'ignorerSelector' | 'containerSelector'>>; +export type UmbSorterConfig = Omit< + INTERNAL_UmbSorterConfig, + 'ignorerSelector' | 'containerSelector' +> & + Partial, 'ignorerSelector' | 'containerSelector'>>; /** * @export @@ -112,9 +115,9 @@ export type UmbSorterConfig = Omit, 'ignorerSelec * @implements {UmbControllerInterface} * @description This controller can make user able to sort items. */ -export class UmbSorterController implements UmbController { +export class UmbSorterController implements UmbController { #host; - #config: INTERNAL_UmbSorterConfig; + #config: INTERNAL_UmbSorterConfig; #observer; #model: Array = []; @@ -122,12 +125,12 @@ export class UmbSorterController implements UmbController { #containerElement!: HTMLElement; - #currentContainerCtrl: UmbSorterController = this; + #currentContainerCtrl: UmbSorterController = this; #currentContainerElement: Element | null = null; #useContainerShadowRoot?: boolean; #scrollElement?: Element | null; - #currentElement?: HTMLElement; + #currentElement?: ElementType; #currentDragElement?: Element; #currentDragRect?: DOMRect; #currentItem?: T | null; @@ -136,13 +139,13 @@ export class UmbSorterController implements UmbController { #dragX = 0; #dragY = 0; - #lastIndicationContainerCtrl: UmbSorterController | null = null; + #lastIndicationContainerCtrl: UmbSorterController | null = null; public get controllerAlias() { return this.#config.identifier; } - constructor(host: UmbControllerHostElement, config: UmbSorterConfig) { + constructor(host: UmbControllerHostElement, config: UmbSorterConfig) { this.#host = host; // Set defaults: @@ -151,7 +154,7 @@ export class UmbSorterController implements UmbController { config.placeholderAttr = 'drag-placeholder'; } - this.#config = config as INTERNAL_UmbSorterConfig; + this.#config = config as INTERNAL_UmbSorterConfig; host.addController(this); //this.#currentContainerElement = host; @@ -278,7 +281,7 @@ export class UmbSorterController implements UmbController { return; } - this.#currentElement = element as HTMLElement; + this.#currentElement = element as ElementType; this.#currentDragRect = this.#currentDragElement.getBoundingClientRect(); this.#currentItem = this.getItemOfElement(this.#currentElement); if (!this.#currentItem) { @@ -392,15 +395,13 @@ export class UmbSorterController implements UmbController { return; } - console.log('this.#currentElement', this.#currentElement); - const currentElementRect = this.#currentElement.getBoundingClientRect(); const insideCurrentRect = isWithinRect(this.#dragX, this.#dragY, currentElementRect); if (insideCurrentRect) { return; } - let toBeCurrentContainerCtrl: UmbSorterController | undefined = undefined; + let toBeCurrentContainerCtrl: UmbSorterController | undefined = undefined; // If we have a boundarySelector, try it. If we didn't get anything fall back to currentContainerElement: const currentBoundaryElement = @@ -602,17 +603,17 @@ export class UmbSorterController implements UmbController { } }; - async #moveElementTo(containerCtrl: UmbSorterController | undefined, newIndex: number) { + async #moveElementTo(containerCtrl: UmbSorterController | undefined, newIndex: number) { if (!this.#currentElement) { return; } - containerCtrl ??= this as UmbSorterController; + containerCtrl ??= this as UmbSorterController; // If same container and same index, do nothing: if (this.#currentContainerCtrl === containerCtrl && this.#currentIndex === newIndex) return; - if (await containerCtrl.updateModel(newIndex, this.#currentElement, this.#currentContainerCtrl)) { + if (await containerCtrl.moveItemInModel(newIndex, this.#currentElement, this.#currentContainerCtrl)) { this.#currentContainerCtrl = containerCtrl; this.#currentIndex = newIndex; } @@ -620,7 +621,7 @@ export class UmbSorterController implements UmbController { /** Management methods: */ - public getItemOfElement(element: HTMLElement) { + public getItemOfElement(element: ElementType) { if (!element) { return null; } @@ -648,7 +649,7 @@ export class UmbSorterController implements UmbController { return this.#model.filter((x) => x !== item).length > 0; } - public async updateModel(newIndex: number, element: HTMLElement, fromCtrl: UmbSorterController) { + public async moveItemInModel(newIndex: number, element: ElementType, fromCtrl: UmbSorterController) { const movingItem = fromCtrl.getItemOfElement(element); if (!movingItem) { console.error('Could not find item of sync item'); @@ -687,7 +688,7 @@ export class UmbSorterController implements UmbController { return true; } - updateAllowIndication(controller: UmbSorterController, item: T) { + updateAllowIndication(controller: UmbSorterController, item: T) { // Remove old indication: if (this.#lastIndicationContainerCtrl !== null && this.#lastIndicationContainerCtrl !== controller) { this.#lastIndicationContainerCtrl.notifyAllowed(); diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/sorter/stories/test-sorter-controller.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/sorter/stories/test-sorter-controller.element.ts index 968dec1b9b..d01ba962c5 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/sorter/stories/test-sorter-controller.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/sorter/stories/test-sorter-controller.element.ts @@ -12,6 +12,7 @@ const SORTER_CONFIG: UmbSorterConfig = { return element.getAttribute('data-sort-entry-id') === model.id; }, querySelectModelToElement: (container: HTMLElement, modelEntry: SortEntryType) => { + // TODO: This selector does not make sense: return container.querySelector('data-sort-entry-id=[' + modelEntry.id + ']'); }, identifier: 'test-sorter',