diff --git a/src/Umbraco.Web.UI.Client/.vscode/settings.json b/src/Umbraco.Web.UI.Client/.vscode/settings.json index 661106f3d9..8259117d7e 100644 --- a/src/Umbraco.Web.UI.Client/.vscode/settings.json +++ b/src/Umbraco.Web.UI.Client/.vscode/settings.json @@ -7,6 +7,7 @@ "ctrls", "devs", "Elementable", + "iframes", "invariantable", "lucide", "Niels", diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-entries/block-grid-entries.element.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-entries/block-grid-entries.element.ts index 3ca2bff84e..5a0355d060 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-entries/block-grid-entries.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-entries/block-grid-entries.element.ts @@ -143,6 +143,16 @@ export class UmbBlockGridEntriesElement extends UmbLitElement { onChange: ({ model }) => { this.#context.setLayouts(model); }, + onRequestMove: ({ item }) => { + // TODO: implement + return true; + }, + onDisallowed: () => { + this.setAttribute('disallow-drop', ''); + }, + onAllowed: () => { + this.removeAttribute('disallow-drop'); + }, }); #context = new UmbBlockGridEntriesContext(this); @@ -241,9 +251,29 @@ export class UmbBlockGridEntriesElement extends UmbLitElement { UmbTextStyles, css` :host { + position: relative; display: grid; gap: 1px; } + :host([disallow-drop])::before { + content: ''; + position: absolute; + z-index: 1; + inset: 0; + border: 2px solid var(--uui-color-danger); + border-radius: calc(var(--uui-border-radius) * 2); + pointer-events: none; + } + :host([disallow-drop])::after { + content: ''; + position: absolute; + z-index: 1; + inset: 0; + border-radius: calc(var(--uui-border-radius) * 2); + background-color: var(--uui-color-danger); + opacity: 0.2; + pointer-events: none; + } > div { display: flex; flex-direction: column; 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 d41027d667..e73da7be7a 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 @@ -74,27 +74,79 @@ export type resolveVerticalDirectionArgs = { type INTERNAL_UmbSorterConfig = { getUniqueOfElement: (element: ElementType) => string | null | symbol | number; getUniqueOfModel: (modeEntry: T) => string | null | symbol | number; + /** + * Optionally define a unique identifier for each sorter experience, all Sorters that uses the same identifier to connect with other sorters. + */ identifier: string | symbol; + /** + * A query selector for the item element. + */ itemSelector: string; - disabledItemSelector?: string; + //disabledItemSelector?: string; + /** + * A selector for the container element, if not defined the host element will be used as container. + */ containerSelector: string; + /** + * A selector for elements to ignore, elements that should not be draggable when within an draggable item, This defaults to links, images & iframes. + */ ignorerSelector: string; + /** + * An class to set on the placeholder element. + */ placeholderClass?: string; + /** + * An attribute to set on the placeholder element. + */ placeholderAttr?: string; + /** + * The selector to find the draggable element within the item. + */ draggableSelector?: string; - boundarySelector?: string; + //boundarySelector?: string; dataTransferResolver?: (dataTransfer: DataTransfer | null, currentItem: T) => void; onStart?: (argument: { item: T; element: ElementType }) => void; + /** + * This callback is executed every time where is a change to this model, this could be a move, insert or remove. + * But notice its not called if a more specific callback is provided, such would be the performItemMove, performItemInsert or performItemRemove or performItemRemove. + */ onChange?: (argument: { item: T; model: Array }) => void; - onContainerChange?: (argument: { item: T; element: ElementType }) => void; + /** + * This callback is executed when an item is moved from another container to this container. + */ + onContainerChange?: (argument: { item: T; model: Array; from: UmbSorterController }) => void; onEnd?: (argument: { item: T; element: ElementType }) => void; itemHasNestedContainersResolver?: (element: HTMLElement) => boolean; + /** + * Callback when a item move is disallowed. + * This should make a visual indication for the user to understand that the move is not allowed. + */ onDisallowed?: (argument: { item: T; element: ElementType }) => void; + /** + * Callback when a item move is allowed. + * This should remove any visual indication of the disallowing, reverting the work of the onDisallowed callback. + */ onAllowed?: (argument: { item: T; element: ElementType }) => void; - onRequestDrop?: (argument: { item: T }) => boolean; - resolveVerticalDirection?: (argument: resolveVerticalDirectionArgs) => void; + /** + * Callback when user tries to move an item from another Sorter to this Sorter, return true or false to allow or disallow the move. + */ + onRequestMove?: (argument: { item: T }) => boolean; + /** + * This callback is executed when an item is hovered within this container. + * The callback should return true if the item should be placed after based on a vertical logic. Other wise false for horizontal. True is default. + */ + resolveVerticalDirection?: (argument: resolveVerticalDirectionArgs) => boolean; + /** + * This callback is executed when an item is moved within this container. + */ performItemMove?: (argument: { item: T; newIndex: number; oldIndex: number }) => Promise | boolean; + /** + * This callback is executed when an item should be inserted into this container. + */ performItemInsert?: (argument: { item: T; newIndex: number }) => Promise | boolean; + /** + * This callback is executed when an item should be removed from this container. + */ performItemRemove?: (argument: { item: T }) => Promise | boolean; }; @@ -281,12 +333,12 @@ export class UmbSorterController ' + this.#config.disabledItemSelector)) { - // Idea: to make sure on does not get initialized twice: if ((element as HTMLElement).draggable === true) return; - (element as HTMLElement).draggable = true; - element.addEventListener('dragstart', this.#handleDragStart); - element.addEventListener('dragend', this.#handleDragEnd); - } + //if (!this.#config.disabledItemSelector || !element.matches('> ' + this.#config.disabledItemSelector)) { + // Idea: to make sure on does not get initialized twice: if ((element as HTMLElement).draggable === true) return; + (element as HTMLElement).draggable = true; + element.addEventListener('dragstart', this.#handleDragStart); + element.addEventListener('dragend', this.#handleDragEnd); + //} // If we have a currentItem and the element matches, we should set the currentElement to this element. if ( @@ -745,6 +797,11 @@ export class UmbSorterController, + }); this.#config.onChange?.({ model: newModel, item }); } @@ -856,8 +913,8 @@ export class UmbSorterController