in packages/designer/src/designer/dragon.ts [173:541]
boost(dragObject: IPublicModelDragObject, boostEvent: MouseEvent | DragEvent, fromRglNode?: INode | IPublicModelNode) {
const { designer } = this;
const masterSensors = this.getMasterSensors();
const handleEvents = makeEventsHandler(boostEvent, masterSensors);
const newBie = !isDragNodeObject(dragObject);
const forceCopyState =
isDragNodeObject(dragObject) && dragObject.nodes.some((node: Node | IPublicModelNode) => (typeof node.isSlot === 'function' ? node.isSlot() : node.isSlot));
const isBoostFromDragAPI = isDragEvent(boostEvent);
let lastSensor: IPublicModelSensor | undefined;
this._dragging = false;
const getRGL = (e: MouseEvent | DragEvent) => {
const locateEvent = createLocateEvent(e);
const sensor = chooseSensor(locateEvent);
if (!sensor || !sensor.getNodeInstanceFromElement) return {};
const nodeInst = sensor.getNodeInstanceFromElement(e.target as Element);
return nodeInst?.node?.getRGL() || {};
};
const checkesc = (e: KeyboardEvent) => {
if (e.keyCode === 27) {
designer.clearLocation();
over();
}
};
let copy = false;
const checkcopy = (e: MouseEvent | DragEvent | KeyboardEvent) => {
/* istanbul ignore next */
if (isDragEvent(e) && e.dataTransfer) {
if (newBie || forceCopyState) {
e.dataTransfer.dropEffect = 'copy';
}
return;
}
if (newBie) {
return;
}
if (e.altKey || e.ctrlKey) {
copy = true;
this.setCopyState(true);
/* istanbul ignore next */
if (isDragEvent(e) && e.dataTransfer) {
e.dataTransfer.dropEffect = 'copy';
}
} else {
copy = false;
if (!forceCopyState) {
this.setCopyState(false);
/* istanbul ignore next */
if (isDragEvent(e) && e.dataTransfer) {
e.dataTransfer.dropEffect = 'move';
}
}
}
};
let lastArrive: any;
const drag = (e: MouseEvent | DragEvent) => {
// FIXME: donot setcopy when: newbie & no location
checkcopy(e);
if (isInvalidPoint(e, lastArrive)) return;
if (lastArrive && isSameAs(e, lastArrive)) {
lastArrive = e;
return;
}
lastArrive = e;
const { isRGL, rglNode } = getRGL(e);
const locateEvent = createLocateEvent(e);
const sensor = chooseSensor(locateEvent);
/* istanbul ignore next */
if (isRGL) {
// 禁止被拖拽元素的阻断
const nodeInst = dragObject.nodes[0].getDOMNode();
if (nodeInst && nodeInst.style) {
this.nodeInstPointerEvents = true;
nodeInst.style.pointerEvents = 'none';
}
// 原生拖拽
this.emitter.emit('rgl.sleeping', false);
if (fromRglNode && fromRglNode.id === rglNode.id) {
designer.clearLocation();
this.clearState();
this.emitter.emit('drag', locateEvent);
return;
}
this._canDrop = !!sensor?.locate(locateEvent);
if (this._canDrop) {
this.emitter.emit('rgl.add.placeholder', {
rglNode,
fromRglNode,
node: locateEvent.dragObject?.nodes[0],
event: e,
});
designer.clearLocation();
this.clearState();
this.emitter.emit('drag', locateEvent);
return;
}
} else {
this._canDrop = false;
this.emitter.emit('rgl.remove.placeholder');
this.emitter.emit('rgl.sleeping', true);
}
if (sensor) {
sensor.fixEvent(locateEvent);
sensor.locate(locateEvent);
} else {
designer.clearLocation();
}
this.emitter.emit('drag', locateEvent);
};
const dragstart = () => {
this._dragging = true;
setShaken(boostEvent);
const locateEvent = createLocateEvent(boostEvent);
if (newBie || forceCopyState) {
this.setCopyState(true);
} else {
chooseSensor(locateEvent);
}
this.setDraggingState(true);
// ESC cancel drag
if (!isBoostFromDragAPI) {
handleEvents((doc) => {
doc.addEventListener('keydown', checkesc, false);
});
}
this.emitter.emit('dragstart', locateEvent);
};
// route: drag-move
const move = (e: MouseEvent | DragEvent) => {
/* istanbul ignore next */
if (isBoostFromDragAPI) {
e.preventDefault();
}
if (this._dragging) {
// process dragging
drag(e);
return;
}
// first move check is shaken
if (isShaken(boostEvent, e)) {
// is shaken dragstart
dragstart();
drag(e);
}
};
let didDrop = true;
/* istanbul ignore next */
const drop = (e: DragEvent) => {
e.preventDefault();
e.stopPropagation();
didDrop = true;
};
// end-tail drag process
const over = (e?: any) => {
// 禁止被拖拽元素的阻断
if (this.nodeInstPointerEvents) {
const nodeInst = dragObject.nodes[0].getDOMNode();
if (nodeInst && nodeInst.style) {
nodeInst.style.pointerEvents = '';
}
this.nodeInstPointerEvents = false;
}
// 发送drop事件
if (e) {
const { isRGL, rglNode } = getRGL(e);
/* istanbul ignore next */
if (isRGL && this._canDrop && this._dragging) {
const tarNode = dragObject.nodes[0];
if (rglNode.id !== tarNode.id) {
// 避免死循环
this.emitter.emit('rgl.drop', {
rglNode,
node: tarNode,
});
const selection = designer.project.currentDocument?.selection;
selection?.select(tarNode.id);
}
}
}
// 移除磁帖占位消息
this.emitter.emit('rgl.remove.placeholder');
/* istanbul ignore next */
if (e && isDragEvent(e)) {
e.preventDefault();
}
if (lastSensor) {
lastSensor.deactiveSensor();
}
/* istanbul ignore next */
if (isBoostFromDragAPI) {
if (!didDrop) {
designer.clearLocation();
}
} else {
this.setNativeSelection(true);
}
this.clearState();
let exception;
if (this._dragging) {
this._dragging = false;
try {
this.emitter.emit('dragend', { dragObject, copy });
} catch (ex) /* istanbul ignore next */ {
exception = ex;
}
}
designer.clearLocation();
handleEvents((doc) => {
/* istanbul ignore next */
if (isBoostFromDragAPI) {
doc.removeEventListener('dragover', move, true);
doc.removeEventListener('dragend', over, true);
doc.removeEventListener('drop', drop, true);
} else {
doc.removeEventListener('mousemove', move, true);
doc.removeEventListener('mouseup', over, true);
}
doc.removeEventListener('mousedown', over, true);
doc.removeEventListener('keydown', checkesc, false);
doc.removeEventListener('keydown', checkcopy, false);
doc.removeEventListener('keyup', checkcopy, false);
});
/* istanbul ignore next */
if (exception) {
throw exception;
}
};
// create drag locate event
const createLocateEvent = (e: MouseEvent | DragEvent): ILocateEvent => {
const evt: any = {
type: 'LocateEvent',
dragObject,
target: e.target,
originalEvent: e,
};
const sourceDocument = e.view?.document;
// event from current document
if (!sourceDocument || sourceDocument === document) {
evt.globalX = e.clientX;
evt.globalY = e.clientY;
} else /* istanbul ignore next */ {
// event from simulator sandbox
let srcSim: ISimulatorHost | undefined;
const lastSim = lastSensor && isSimulatorHost(lastSensor) ? lastSensor : null;
// check source simulator
if (lastSim && lastSim.contentDocument === sourceDocument) {
srcSim = lastSim;
} else {
srcSim = masterSensors.find((sim) => sim.contentDocument === sourceDocument);
if (!srcSim && lastSim) {
srcSim = lastSim;
}
}
if (srcSim) {
// transform point by simulator
const g = srcSim.viewport.toGlobalPoint(e);
evt.globalX = g.clientX;
evt.globalY = g.clientY;
evt.canvasX = e.clientX;
evt.canvasY = e.clientY;
evt.sensor = srcSim;
} else {
// this condition will not happen, just make sure ts ok
evt.globalX = e.clientX;
evt.globalY = e.clientY;
}
}
return evt;
};
const sourceSensor = getSourceSensor(dragObject);
/* istanbul ignore next */
const chooseSensor = (e: ILocateEvent) => {
// this.sensors will change on dragstart
const sensors: IPublicModelSensor[] = this.sensors.concat(masterSensors as IPublicModelSensor[]);
let sensor =
e.sensor && e.sensor.isEnter(e)
? e.sensor
: sensors.find((s) => s.sensorAvailable && s.isEnter(e));
if (!sensor) {
// TODO: enter some area like componentspanel cancel
if (lastSensor) {
sensor = lastSensor;
} else if (e.sensor) {
sensor = e.sensor;
} else if (sourceSensor) {
sensor = sourceSensor;
}
}
if (sensor !== lastSensor) {
if (lastSensor) {
lastSensor.deactiveSensor();
}
lastSensor = sensor;
}
if (sensor) {
e.sensor = sensor;
sensor.fixEvent(e);
}
this._activeSensor = sensor;
return sensor;
};
/* istanbul ignore next */
if (isDragEvent(boostEvent)) {
const { dataTransfer } = boostEvent;
if (dataTransfer) {
dataTransfer.effectAllowed = 'all';
try {
dataTransfer.setData('application/json', '{}');
} catch (ex) {
// ignore
}
}
dragstart();
} else {
this.setNativeSelection(false);
}
handleEvents((doc) => {
/* istanbul ignore next */
if (isBoostFromDragAPI) {
doc.addEventListener('dragover', move, true);
// dragexit
didDrop = false;
doc.addEventListener('drop', drop, true);
doc.addEventListener('dragend', over, true);
} else {
doc.addEventListener('mousemove', move, true);
doc.addEventListener('mouseup', over, true);
}
doc.addEventListener('mousedown', over, true);
});
// future think: drag things from browser-out or a iframe-pane
if (!newBie && !isBoostFromDragAPI) {
handleEvents((doc) => {
doc.addEventListener('keydown', checkcopy, false);
doc.addEventListener('keyup', checkcopy, false);
});
}
}