Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix drag and drop on NativeDnD set to false #6346

Merged
merged 3 commits into from
Dec 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading