From 84a6926a0ad6a51a05744dd4956521dd42d54b4b Mon Sep 17 00:00:00 2001 From: mohamedsalem401 Date: Fri, 6 Dec 2024 11:35:45 +0200 Subject: [PATCH] Fix drag and drop on NativeDnD set to false --- .../core/src/block_manager/view/BlockView.ts | 57 ++++++++++++++----- .../core/src/block_manager/view/BlocksView.ts | 41 +++++++------ .../core/src/utils/sorter/ComponentSorter.ts | 3 + packages/core/src/utils/sorter/Sorter.ts | 40 +++++++++---- 4 files changed, 98 insertions(+), 43 deletions(-) diff --git a/packages/core/src/block_manager/view/BlockView.ts b/packages/core/src/block_manager/view/BlockView.ts index 9f215671f6..49fcc1e2ed 100644 --- a/packages/core/src/block_manager/view/BlockView.ts +++ b/packages/core/src/block_manager/view/BlockView.ts @@ -5,12 +5,14 @@ import { on, off } from '../../utils/dom'; import { hasDnd } from '../../utils/mixins'; import { BlockManagerConfig } from '../config/config'; import Block from '../model/Block'; +import ComponentSorter from '../../utils/sorter/ComponentSorter'; +import CanvasNewComponentNode from '../../utils/sorter/CanvasNewComponentNode'; export interface BlockViewConfig { em?: EditorModel; pStylePrefix?: string; appendOnClick?: BlockManagerConfig['appendOnClick']; - getSorter?: any; + getSorter?: () => ComponentSorter; } export default class BlockView extends View { @@ -52,24 +54,27 @@ export default class BlockView extends View { } else if (isFunction(onClick)) { return onClick(model, em?.getEditor(), { event: ev }); } - const sorter = config.getSorter(); + const sorter = config.getSorter?.(); + if (!sorter) return; const content = model.get('content')!; + let dropModel = this.getTempDropModel(content); + const el = dropModel.view?.el; + const sources = el ? [{ element: el, dragSource: { content } }] : []; const selected = em.getSelected(); - sorter.setDropContent(content); - let target, valid, insertAt; + let target, valid, insertAt, index = 0; // If there is a selected component, try first to append // the block inside, otherwise, try to place it as a next sibling if (selected) { - valid = sorter.validTarget(selected.getEl(), content); + valid = sorter.validTarget(selected.getEl(), sources, index); - if (valid.valid) { + if (valid) { target = selected; } else { const parent = selected.parent(); if (parent) { - valid = sorter.validTarget(parent.getEl(), content); - if (valid.valid) { + valid = sorter.validTarget(parent.getEl(), sources, index); + if (valid) { target = parent; insertAt = parent.components().indexOf(selected) + 1; } @@ -80,8 +85,8 @@ export default class BlockView extends View { // If no target found yet, try to append the block to the wrapper if (!target) { const wrapper = em.getWrapper()!; - valid = sorter.validTarget(wrapper.getEl(), content); - if (valid.valid) target = wrapper; + valid = sorter.validTarget(wrapper.getEl(), sources, index); + if (valid) target = wrapper; } const result = target && target.append(content, { at: insertAt })[0]; @@ -100,9 +105,11 @@ export default class BlockView extends View { em.refreshCanvas(); const sorter = config.getSorter(); sorter.__currentBlock = model; - sorter.setDragHelper(this.el, e); - sorter.setDropContent(this.model.get('content')); - sorter.startSort([this.el]); + const content = this.model.get('content'); + let dropModel = this.getTempDropModel(content); + const el = dropModel.view?.el; + const sources = el ? [{ element: el, dragSource: { content } }] : []; + sorter.startSort(sources); on(document, 'mouseup', this.endDrag); } @@ -124,9 +131,29 @@ export default class BlockView extends View { */ endDrag() { off(document, 'mouseup', this.endDrag); - const sorter = this.config.getSorter(); + const sorter = this.config.getSorter?.(); + if (sorter) { + sorter.endDrag(); + } + } - sorter.cancelDrag(); + /** + * Generates a temporary model of the content being dragged for use with the sorter. + * @returns The temporary model representing the dragged content. + */ + private getTempDropModel(content?: any) { + const comps = this.em.Components.getComponents(); + const opts = { + avoidChildren: 1, + avoidStore: 1, + avoidUpdateStyle: 1, + }; + const tempModel = comps.add(content, { ...opts, temporary: true }); + let dropModel = comps.remove(tempModel, { ...opts, temporary: true } as any); + // @ts-ignore + dropModel = dropModel instanceof Array ? dropModel[0] : dropModel; + dropModel.view?.$el.data('model', dropModel); + return dropModel; } render() { diff --git a/packages/core/src/block_manager/view/BlocksView.ts b/packages/core/src/block_manager/view/BlocksView.ts index 87ec697f54..b298a17c20 100644 --- a/packages/core/src/block_manager/view/BlocksView.ts +++ b/packages/core/src/block_manager/view/BlocksView.ts @@ -7,6 +7,8 @@ import Block from '../model/Block'; import Categories from '../../abstract/ModuleCategories'; import BlockView from './BlockView'; import CategoryView from '../../abstract/ModuleCategoryView'; +import CanvasNewComponentNode from '../../utils/sorter/CanvasNewComponentNode'; +import { DragDirection } from '../../utils/sorter/types'; export interface BlocksViewConfig { em: EditorModel; @@ -71,23 +73,30 @@ export default class BlocksView extends View { if (!this.sorter) { const utils = em.Utils; const canvas = em.Canvas; - - this.sorter = new utils.Sorter({ - // @ts-ignore - container: canvas.getBody(), - placer: canvas.getPlacerEl(), - containerSel: '*', - itemSel: '*', - pfx: this.ppfx, - onStart: this.onDrag, - onEndMove: this.onDrop, - onMove: this.onMove, - document: canvas.getFrameEl().contentDocument, - direction: 'a', - wmargin: 1, - nested: 1, + this.sorter = new utils.ComponentSorter({ em, - canvasRelative: 1, + treeClass: CanvasNewComponentNode, + containerContext: { + container: canvas.getBody(), + containerSel: '*', + itemSel: '*', + pfx: this.ppfx, + placeholderElement: canvas.getPlacerEl()!, + document: canvas.getBody().ownerDocument, + }, + dragBehavior: { + dragDirection: DragDirection.BothDirections, + nested: true, + }, + positionOptions: { + windowMargin: 1, + canvasRelative: true, + }, + eventHandlers: { + legacyOnStartSort: this.onDrag, + legacyOnEndMove: this.onDrop, + legacyOnMoveClb: this.onMove, + }, }); } diff --git a/packages/core/src/utils/sorter/ComponentSorter.ts b/packages/core/src/utils/sorter/ComponentSorter.ts index 833ee245e9..c5f05185f8 100644 --- a/packages/core/src/utils/sorter/ComponentSorter.ts +++ b/packages/core/src/utils/sorter/ComponentSorter.ts @@ -12,6 +12,7 @@ import { SorterEventHandlers, DragSource, } from './types'; +import Block from '../../block_manager/model/Block'; const targetSpotType = CanvasSpotBuiltInTypes.Target; @@ -22,6 +23,8 @@ const spotTarget = { export default class ComponentSorter extends Sorter { targetIsText: boolean = false; + // For event triggering + __currentBlock?: Block; constructor({ em, treeClass, diff --git a/packages/core/src/utils/sorter/Sorter.ts b/packages/core/src/utils/sorter/Sorter.ts index 747c7d02f6..63a50c7d84 100644 --- a/packages/core/src/utils/sorter/Sorter.ts +++ b/packages/core/src/utils/sorter/Sorter.ts @@ -71,18 +71,7 @@ export default class Sorter> { * @param {HTMLElement[]} sources[] * */ startSort(sources: { element?: HTMLElement; dragSource?: DragSource }[]) { - const validSources = sources.filter((source) => !!source.dragSource || this.findValidSourceElement(source.element)); - - const sourcesWithModel: { model: T; content?: any }[] = validSources.map((source) => { - return { - model: $(source.element)?.data('model'), - content: source.dragSource, - }; - }); - const sortedSources = sourcesWithModel.sort((a, b) => { - return sortDom(a.model, b.model); - }); - const sourceNodes = sortedSources.map((source) => new this.treeClass(source.model, source.content)); + const { sourceNodes, sourcesWithModel } = this.getSourceNodes(sources); this.sourceNodes = sourceNodes; this.dropLocationDeterminer.startSort(sourceNodes); this.bindDragEventHandlers(); @@ -104,6 +93,33 @@ export default class Sorter> { this.em.trigger('sorter:drag:start', sources[0], sourcesWithModel[0]); } + validTarget(targetEl: HTMLElement | undefined, sources: { element?: HTMLElement; dragSource?: DragSource }[], index: number): boolean { + if (!targetEl) return false; + const targetModel = $(targetEl).data('model'); + if (!targetModel) return false; + + const targetNode = new this.treeClass(targetModel) + const { sourceNodes } = this.getSourceNodes(sources); + const canMove = sourceNodes.some((node) => targetNode.canMove(node, index)); + return canMove; + } + + private getSourceNodes(sources: { element?: HTMLElement; dragSource?: DragSource; }[]) { + const validSources = sources.filter((source) => !!source.dragSource || this.findValidSourceElement(source.element)); + + const sourcesWithModel: { model: T; content?: any; }[] = validSources.map((source) => { + return { + model: $(source.element)?.data('model'), + content: source.dragSource, + }; + }); + const sortedSources = sourcesWithModel.sort((a, b) => { + return sortDom(a.model, b.model); + }); + const sourceNodes = sortedSources.map((source) => new this.treeClass(source.model, source.content)); + return { sourceNodes, sourcesWithModel }; + } + /** * This method is should be called when the user scrolls within the container. */