Browse Source

Fix drag and drop on NativeDnD set to false (#6346)

script-events
mohamed yahia 1 year ago
committed by GitHub
parent
commit
b60bf5fdd6
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 14
      docs/api/datasource.md
  2. 60
      packages/core/src/block_manager/view/BlockView.ts
  3. 41
      packages/core/src/block_manager/view/BlocksView.ts
  4. 3
      packages/core/src/utils/sorter/ComponentSorter.ts
  5. 44
      packages/core/src/utils/sorter/Sorter.ts

14
docs/api/datasource.md

@ -46,14 +46,14 @@ If the `records` property is not an instance of `DataRecords`, it will be conver
### Parameters ### 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. * `opts` **DataSourceOptions** Options to initialize the data source.
## records ## records
Retrieves the collection of records associated with this data source. 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 ## em
@ -67,7 +67,7 @@ Adds a new record to the data source.
### Parameters ### 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. * `opts` **AddOptions?** Options to apply when adding the record.
Returns **DataRecord** The added data record. Returns **DataRecord** The added data record.
@ -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. * `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 ## getRecords
Retrieves all records from the data source. Retrieves all records from the data source.
Each record is processed with the `getRecord` method to apply any read transformers. 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 ## removeRecord
@ -98,7 +98,7 @@ Removes a record from the data source by its ID.
* `id` **([string][6] | [number][7])** The ID of the record to remove. * `id` **([string][6] | [number][7])** The ID of the record to remove.
* `opts` **RemoveOptions?** Options to apply when removing the record. * `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 ## setRecords
@ -106,7 +106,7 @@ Replaces the existing records in the data source with a new set of records.
### Parameters ### 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. Returns **[Array][9]\<DataRecord>** An array of the added data records.

60
packages/core/src/block_manager/view/BlockView.ts

@ -5,12 +5,14 @@ import { on, off } from '../../utils/dom';
import { hasDnd } from '../../utils/mixins'; import { hasDnd } from '../../utils/mixins';
import { BlockManagerConfig } from '../config/config'; import { BlockManagerConfig } from '../config/config';
import Block from '../model/Block'; import Block from '../model/Block';
import ComponentSorter from '../../utils/sorter/ComponentSorter';
import CanvasNewComponentNode from '../../utils/sorter/CanvasNewComponentNode';
export interface BlockViewConfig { export interface BlockViewConfig {
em?: EditorModel; em?: EditorModel;
pStylePrefix?: string; pStylePrefix?: string;
appendOnClick?: BlockManagerConfig['appendOnClick']; appendOnClick?: BlockManagerConfig['appendOnClick'];
getSorter?: any; getSorter?: () => ComponentSorter<CanvasNewComponentNode>;
} }
export default class BlockView extends View<Block> { export default class BlockView extends View<Block> {
@ -52,24 +54,30 @@ export default class BlockView extends View<Block> {
} else if (isFunction(onClick)) { } else if (isFunction(onClick)) {
return onClick(model, em?.getEditor(), { event: ev }); return onClick(model, em?.getEditor(), { event: ev });
} }
const sorter = config.getSorter(); const sorter = config.getSorter?.();
if (!sorter) return;
const content = model.get('content')!; 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(); const selected = em.getSelected();
sorter.setDropContent(content); let target,
let target, valid, insertAt; valid,
insertAt,
index = 0;
// If there is a selected component, try first to append // If there is a selected component, try first to append
// the block inside, otherwise, try to place it as a next sibling // the block inside, otherwise, try to place it as a next sibling
if (selected) { if (selected) {
valid = sorter.validTarget(selected.getEl(), content); valid = sorter.validTarget(selected.getEl(), sources, index);
if (valid.valid) { if (valid) {
target = selected; target = selected;
} else { } else {
const parent = selected.parent(); const parent = selected.parent();
if (parent) { if (parent) {
valid = sorter.validTarget(parent.getEl(), content); valid = sorter.validTarget(parent.getEl(), sources, index);
if (valid.valid) { if (valid) {
target = parent; target = parent;
insertAt = parent.components().indexOf(selected) + 1; insertAt = parent.components().indexOf(selected) + 1;
} }
@ -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 no target found yet, try to append the block to the wrapper
if (!target) { if (!target) {
const wrapper = em.getWrapper()!; const wrapper = em.getWrapper()!;
valid = sorter.validTarget(wrapper.getEl(), content); valid = sorter.validTarget(wrapper.getEl(), sources, index);
if (valid.valid) target = wrapper; if (valid) target = wrapper;
} }
const result = target && target.append(content, { at: insertAt })[0]; const result = target && target.append(content, { at: insertAt })[0];
@ -100,9 +108,11 @@ export default class BlockView extends View<Block> {
em.refreshCanvas(); em.refreshCanvas();
const sorter = config.getSorter(); const sorter = config.getSorter();
sorter.__currentBlock = model; sorter.__currentBlock = model;
sorter.setDragHelper(this.el, e); const content = this.model.get('content');
sorter.setDropContent(this.model.get('content')); let dropModel = this.getTempDropModel(content);
sorter.startSort([this.el]); const el = dropModel.view?.el;
const sources = el ? [{ element: el, dragSource: { content } }] : [];
sorter.startSort(sources);
on(document, 'mouseup', this.endDrag); on(document, 'mouseup', this.endDrag);
} }
@ -124,9 +134,29 @@ export default class BlockView extends View<Block> {
*/ */
endDrag() { endDrag() {
off(document, 'mouseup', this.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() { render() {

41
packages/core/src/block_manager/view/BlocksView.ts

@ -7,6 +7,8 @@ import Block from '../model/Block';
import Categories from '../../abstract/ModuleCategories'; import Categories from '../../abstract/ModuleCategories';
import BlockView from './BlockView'; import BlockView from './BlockView';
import CategoryView from '../../abstract/ModuleCategoryView'; import CategoryView from '../../abstract/ModuleCategoryView';
import CanvasNewComponentNode from '../../utils/sorter/CanvasNewComponentNode';
import { DragDirection } from '../../utils/sorter/types';
export interface BlocksViewConfig { export interface BlocksViewConfig {
em: EditorModel; em: EditorModel;
@ -71,23 +73,30 @@ export default class BlocksView extends View {
if (!this.sorter) { if (!this.sorter) {
const utils = em.Utils; const utils = em.Utils;
const canvas = em.Canvas; const canvas = em.Canvas;
this.sorter = new utils.ComponentSorter({
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,
em, 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,
},
}); });
} }

3
packages/core/src/utils/sorter/ComponentSorter.ts

@ -12,6 +12,7 @@ import {
SorterEventHandlers, SorterEventHandlers,
DragSource, DragSource,
} from './types'; } from './types';
import Block from '../../block_manager/model/Block';
const targetSpotType = CanvasSpotBuiltInTypes.Target; const targetSpotType = CanvasSpotBuiltInTypes.Target;
@ -22,6 +23,8 @@ const spotTarget = {
export default class ComponentSorter<NodeType extends BaseComponentNode> extends Sorter<Component, NodeType> { export default class ComponentSorter<NodeType extends BaseComponentNode> extends Sorter<Component, NodeType> {
targetIsText: boolean = false; targetIsText: boolean = false;
// For event triggering
__currentBlock?: Block;
constructor({ constructor({
em, em,
treeClass, treeClass,

44
packages/core/src/utils/sorter/Sorter.ts

@ -71,18 +71,7 @@ export default class Sorter<T, NodeType extends SortableTreeNode<T>> {
* @param {HTMLElement[]} sources[] * @param {HTMLElement[]} sources[]
* */ * */
startSort(sources: { element?: HTMLElement; dragSource?: DragSource<T> }[]) { startSort(sources: { element?: HTMLElement; dragSource?: DragSource<T> }[]) {
const validSources = sources.filter((source) => !!source.dragSource || this.findValidSourceElement(source.element)); const { sourceNodes, sourcesWithModel } = this.getSourceNodes(sources);
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));
this.sourceNodes = sourceNodes; this.sourceNodes = sourceNodes;
this.dropLocationDeterminer.startSort(sourceNodes); this.dropLocationDeterminer.startSort(sourceNodes);
this.bindDragEventHandlers(); this.bindDragEventHandlers();
@ -104,6 +93,37 @@ export default class Sorter<T, NodeType extends SortableTreeNode<T>> {
this.em.trigger('sorter:drag:start', sources[0], sourcesWithModel[0]); 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. * This method is should be called when the user scrolls within the container.
*/ */

Loading…
Cancel
Save