Skip to content

Commit

Permalink
Fix drag and drop on NativeDnD set to false (#6346)
Browse files Browse the repository at this point in the history
  • Loading branch information
mohamedsalem401 authored Dec 18, 2024
1 parent a85d414 commit b60bf5f
Show file tree
Hide file tree
Showing 5 changed files with 112 additions and 50 deletions.
14 changes: 7 additions & 7 deletions docs/api/datasource.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,14 @@ If the `records` property is not an instance of `DataRecords`, it will be conver

### Parameters

* `props` **DataSourceProps** Properties to initialize the data source.
* `props` **DataSourceProps\<DRProps>** Properties to initialize the data source.
* `opts` **DataSourceOptions** Options to initialize the data source.

## records

Retrieves the collection of records associated with this data source.

Returns **DataRecords** The collection of data records.
Returns **DataRecords\<DRProps>** The collection of data records.

## em

Expand All @@ -67,7 +67,7 @@ Adds a new record to the data source.

### Parameters

* `record` **DataRecordProps** The properties of the record to add.
* `record` **DRProps** The properties of the record to add.
* `opts` **AddOptions?** Options to apply when adding the record.

Returns **DataRecord** The added data record.
Expand All @@ -80,14 +80,14 @@ Retrieves a record from the data source by its ID.

* `id` **([string][6] | [number][7])** The ID of the record to retrieve.

Returns **(DataRecord | [undefined][8])** The data record, or `undefined` if no record is found with the given ID.
Returns **(DataRecord\<DRProps> | [undefined][8])** The data record, or `undefined` if no record is found with the given ID.

## getRecords

Retrieves all records from the data source.
Each record is processed with the `getRecord` method to apply any read transformers.

Returns **[Array][9]<(DataRecord | [undefined][8])>** An array of data records.
Returns **[Array][9]<(DataRecord\<DRProps> | [undefined][8])>** An array of data records.

## removeRecord

Expand All @@ -98,15 +98,15 @@ Removes a record from the data source by its ID.
* `id` **([string][6] | [number][7])** The ID of the record to remove.
* `opts` **RemoveOptions?** Options to apply when removing the record.

Returns **(DataRecord | [undefined][8])** The removed data record, or `undefined` if no record is found with the given ID.
Returns **(DataRecord\<DRProps> | [undefined][8])** The removed data record, or `undefined` if no record is found with the given ID.

## setRecords

Replaces the existing records in the data source with a new set of records.

### Parameters

* `records` **[Array][9]\<DataRecordProps>** An array of data record properties to set.
* `records` **[Array][9]\<DRProps>** An array of data record properties to set.

Returns **[Array][9]\<DataRecord>** An array of the added data records.

Expand Down
60 changes: 45 additions & 15 deletions packages/core/src/block_manager/view/BlockView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<CanvasNewComponentNode>;
}

export default class BlockView extends View<Block> {
Expand Down Expand Up @@ -52,24 +54,30 @@ export default class BlockView extends View<Block> {
} 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;
}
Expand All @@ -80,8 +88,8 @@ export default class BlockView extends View<Block> {
// 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];
Expand All @@ -100,9 +108,11 @@ export default class BlockView extends View<Block> {
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);
}

Expand All @@ -124,9 +134,29 @@ export default class BlockView extends View<Block> {
*/
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() {
Expand Down
41 changes: 25 additions & 16 deletions packages/core/src/block_manager/view/BlocksView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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,
},
});
}

Expand Down
3 changes: 3 additions & 0 deletions packages/core/src/utils/sorter/ComponentSorter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
SorterEventHandlers,
DragSource,
} from './types';
import Block from '../../block_manager/model/Block';

const targetSpotType = CanvasSpotBuiltInTypes.Target;

Expand All @@ -22,6 +23,8 @@ const spotTarget = {

export default class ComponentSorter<NodeType extends BaseComponentNode> extends Sorter<Component, NodeType> {
targetIsText: boolean = false;
// For event triggering
__currentBlock?: Block;
constructor({
em,
treeClass,
Expand Down
44 changes: 32 additions & 12 deletions packages/core/src/utils/sorter/Sorter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,18 +71,7 @@ export default class Sorter<T, NodeType extends SortableTreeNode<T>> {
* @param {HTMLElement[]} sources[]
* */
startSort(sources: { element?: HTMLElement; dragSource?: DragSource<T> }[]) {
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();
Expand All @@ -104,6 +93,37 @@ export default class Sorter<T, NodeType extends SortableTreeNode<T>> {
this.em.trigger('sorter:drag:start', sources[0], sourcesWithModel[0]);
}

validTarget(
targetEl: HTMLElement | undefined,
sources: { element?: HTMLElement; dragSource?: DragSource<T> }[],
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<T> }[]) {
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.
*/
Expand Down

0 comments on commit b60bf5f

Please sign in to comment.