mirror of
https://github.com/alibaba/lowcode-engine.git
synced 2025-12-15 13:46:33 +00:00
drag&drop logic 80%
This commit is contained in:
parent
f7c69af6c4
commit
59818ae3b6
@ -1,5 +1,5 @@
|
||||
import { obx, autorun, computed } from '@recore/obx';
|
||||
import { ISimulator, ComponentInstance, Component } from '../../../designer/simulator';
|
||||
import { ISimulator, ComponentInstance, Component, NodeInstance } from '../../../designer/simulator';
|
||||
import Viewport from './viewport';
|
||||
import { createSimulator } from './create-simulator';
|
||||
import { SimulatorRenderer } from '../renderer/renderer';
|
||||
@ -12,6 +12,8 @@ import { LocationData } from '../../../designer/helper/location';
|
||||
import { NodeData } from '../../../designer/schema';
|
||||
import { ComponentDescriptionSpec } from '../../../designer/component-config';
|
||||
import { ReactInstance } from 'react';
|
||||
import { setNativeSelection } from '../../../utils/navtive-selection';
|
||||
import cursor from '../../../designer/helper/cursor';
|
||||
|
||||
export interface SimulatorProps {
|
||||
// 从 documentModel 上获取
|
||||
@ -50,6 +52,7 @@ const defaultDepends = [
|
||||
];
|
||||
|
||||
export class SimulatorHost implements ISimulator<SimulatorProps> {
|
||||
readonly isSimulator = true;
|
||||
constructor(readonly document: DocumentModel) {}
|
||||
|
||||
readonly designer = this.document.designer;
|
||||
@ -126,6 +129,7 @@ export class SimulatorHost implements ISimulator<SimulatorProps> {
|
||||
get contentWindow() {
|
||||
return this._contentWindow;
|
||||
}
|
||||
|
||||
@obx.ref private _contentDocument?: Document;
|
||||
get contentDocument() {
|
||||
return this._contentDocument;
|
||||
@ -192,8 +196,8 @@ export class SimulatorHost implements ISimulator<SimulatorProps> {
|
||||
// TODO: think of lock when edit a node
|
||||
// 事件路由
|
||||
doc.addEventListener('mousedown', (downEvent: MouseEvent) => {
|
||||
const target = documentModel.getNodeFromElement(downEvent.target as Element);
|
||||
if (!target) {
|
||||
const nodeInst = documentModel.getNodeInstanceFromElement(downEvent.target as Element);
|
||||
if (!nodeInst?.node) {
|
||||
selection.clear();
|
||||
return;
|
||||
}
|
||||
@ -202,7 +206,7 @@ export class SimulatorHost implements ISimulator<SimulatorProps> {
|
||||
const isLeftButton = downEvent.which === 1 || downEvent.button === 0;
|
||||
|
||||
if (isLeftButton) {
|
||||
let node: Node = target;
|
||||
let node: Node = nodeInst.node;
|
||||
let nodes: Node[] = [node];
|
||||
let ignoreUpSelected = false;
|
||||
if (isMulti) {
|
||||
@ -214,7 +218,7 @@ export class SimulatorHost implements ISimulator<SimulatorProps> {
|
||||
}
|
||||
// 获得顶层 nodes
|
||||
nodes = selection.getTopNodes();
|
||||
} else if (selection.containsNode(target)) {
|
||||
} else if (selection.containsNode(node)) {
|
||||
nodes = selection.getTopNodes();
|
||||
} else {
|
||||
// will clear current selection & select dragment in dragstart
|
||||
@ -236,7 +240,7 @@ export class SimulatorHost implements ISimulator<SimulatorProps> {
|
||||
doc.removeEventListener('mouseup', checkSelect, true);
|
||||
if (!isShaken(downEvent, e)) {
|
||||
// const node = hasConditionFlow(target) ? target.conditionFlow : target;
|
||||
const node = target;
|
||||
const node = nodeInst.node!;
|
||||
const id = node.id;
|
||||
designer.activeTracker.track(node);
|
||||
if (isMulti && selection.has(id)) {
|
||||
@ -252,6 +256,7 @@ export class SimulatorHost implements ISimulator<SimulatorProps> {
|
||||
// cause edit
|
||||
doc.addEventListener('dblclick', (e: MouseEvent) => {
|
||||
// TODO:
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@ -266,9 +271,9 @@ export class SimulatorHost implements ISimulator<SimulatorProps> {
|
||||
if (!hovering.enable) {
|
||||
return;
|
||||
}
|
||||
const node = this.document.getNodeFromElement(e.target as Element);
|
||||
const nodeInst = this.document.getNodeInstanceFromElement(e.target as Element);
|
||||
// TODO: enhance only hover one instance
|
||||
hovering.hover(node);
|
||||
hovering.hover(nodeInst?.node || null);
|
||||
e.stopPropagation();
|
||||
};
|
||||
const leave = () => hovering.leave(this.document);
|
||||
@ -293,15 +298,6 @@ export class SimulatorHost implements ISimulator<SimulatorProps> {
|
||||
};
|
||||
}
|
||||
|
||||
setDraggingState(state: boolean): void {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
isDraggingState(): boolean {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
setCopyState(state: boolean): void {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
setSuspense(suspended: boolean) {
|
||||
if (suspended) {
|
||||
if (this.disableHovering) {
|
||||
@ -315,15 +311,10 @@ export class SimulatorHost implements ISimulator<SimulatorProps> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setDesignMode(mode: string): void {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
isCopyState(): boolean {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
clearState(): void {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
describeComponent(component: Component): ComponentDescriptionSpec {
|
||||
throw new Error('Method not implemented.');
|
||||
@ -345,8 +336,8 @@ export class SimulatorHost implements ISimulator<SimulatorProps> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
getClosestNodeId(elem: Element): string | null {
|
||||
return this.renderer?.getClosestNodeId(elem) || null;
|
||||
getClosestNodeInstance(elem: Element): NodeInstance | null {
|
||||
return this.renderer?.getClosestNodeInstance(elem) || null;
|
||||
}
|
||||
|
||||
computeComponentInstanceRect(instance: ReactInstance): DOMRect | null {
|
||||
@ -459,6 +450,20 @@ export class SimulatorHost implements ISimulator<SimulatorProps> {
|
||||
}
|
||||
}
|
||||
|
||||
// #region ========= drag and drop helpers =============
|
||||
setNativeSelection(enableFlag: boolean) {
|
||||
setNativeSelection(enableFlag);
|
||||
}
|
||||
setDraggingState(state: boolean) {
|
||||
cursor.setDragging(state);
|
||||
}
|
||||
setCopyState(state: boolean) {
|
||||
cursor.setCopy(state);
|
||||
}
|
||||
clearState() {
|
||||
cursor.release();
|
||||
}
|
||||
|
||||
fixEvent(e: LocateEvent): LocateEvent {
|
||||
/*
|
||||
if (e.fixed) {
|
||||
@ -483,7 +488,7 @@ export class SimulatorHost implements ISimulator<SimulatorProps> {
|
||||
this.scroller.cancel();
|
||||
}
|
||||
|
||||
//#region drag locate logic
|
||||
// ========= drag location logic start ==========
|
||||
getDropTarget(e: LocateEvent): NodeParent | LocationData | null {
|
||||
/*
|
||||
const { target, dragTarget } = e;
|
||||
@ -841,5 +846,5 @@ export class SimulatorHost implements ISimulator<SimulatorProps> {
|
||||
}*/
|
||||
return false;
|
||||
}
|
||||
//#endregion
|
||||
// #endregion
|
||||
}
|
||||
|
||||
@ -9,6 +9,8 @@ import { Asset } from '../utils/asset';
|
||||
import loader from '../utils/loader';
|
||||
import { ComponentDescriptionSpec } from '../../../designer/component-config';
|
||||
import { findDOMNodes } from '../utils/react';
|
||||
import { isESModule } from '../../../utils/is-es-module';
|
||||
import { NodeInstance } from '../../../designer/simulator';
|
||||
|
||||
let REACT_KEY = '';
|
||||
function cacheReactKey(el: Element): Element {
|
||||
@ -24,28 +26,36 @@ function cacheReactKey(el: Element): Element {
|
||||
|
||||
const SYMBOL_VNID = Symbol('_LCNodeId');
|
||||
|
||||
function getClosestNodeId(element: Element): string | null {
|
||||
function getClosestNodeInstance(element: Element): NodeInstance | null {
|
||||
let el: any = element;
|
||||
if (el) {
|
||||
el = cacheReactKey(el);
|
||||
}
|
||||
while (el) {
|
||||
if (SYMBOL_VNID in el) {
|
||||
return el[SYMBOL_VNID];
|
||||
return {
|
||||
nodeId: el[SYMBOL_VNID],
|
||||
instance: el,
|
||||
};
|
||||
}
|
||||
// get fiberNode from element
|
||||
if (el[REACT_KEY]) {
|
||||
return getNodeId(el[REACT_KEY]);
|
||||
return getNodeInstance(el[REACT_KEY]);
|
||||
}
|
||||
el = el.parentElement;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function getNodeId(instance: any): string {
|
||||
if (instance.stateNode && SYMBOL_VNID in instance.stateNode) {
|
||||
return instance.stateNode[SYMBOL_VNID];
|
||||
function getNodeInstance(fiberNode: any): NodeInstance | null {
|
||||
const instance = fiberNode.stateNode;
|
||||
if (instance && SYMBOL_VNID in instance) {
|
||||
return {
|
||||
nodeId: instance[SYMBOL_VNID],
|
||||
instance,
|
||||
};
|
||||
}
|
||||
return getNodeId(instance.return);
|
||||
return getNodeInstance(fiberNode.return);
|
||||
}
|
||||
|
||||
function checkInstanceMounted(instance: any): boolean {
|
||||
@ -190,8 +200,8 @@ export class SimulatorRenderer {
|
||||
return this.instancesMap.get(id) || null;
|
||||
}
|
||||
|
||||
getClosestNodeId(element: Element): string | null {
|
||||
return getClosestNodeId(element);
|
||||
getClosestNodeInstance(element: Element): NodeInstance | null {
|
||||
return getClosestNodeInstance(element);
|
||||
}
|
||||
|
||||
findDOMNodes(instance: ReactInstance): Array<Element | Text> | null {
|
||||
@ -228,22 +238,31 @@ function accessLibrary(library: string | object) {
|
||||
return (window as any)[library];
|
||||
}
|
||||
|
||||
function getSubComponent(component: any, paths: string[]) {
|
||||
function getSubComponent(library: any, paths: string[]) {
|
||||
const l = paths.length;
|
||||
if (l < 1) {
|
||||
return component;
|
||||
if (l < 1 || !library) {
|
||||
return library;
|
||||
}
|
||||
let i = 0;
|
||||
let component: any;
|
||||
while (i < l) {
|
||||
const key = paths[i]!;
|
||||
let ex: any;
|
||||
try {
|
||||
component = (component as any)[key];
|
||||
component = library[key];
|
||||
} catch (e) {
|
||||
ex = e;
|
||||
component = null;
|
||||
}
|
||||
if (i === 0 && component == null && key === 'default') {
|
||||
if (ex) {
|
||||
return l === 1 ? library : null;
|
||||
}
|
||||
component = library;
|
||||
} else if (component == null) {
|
||||
return null;
|
||||
}
|
||||
if (!component) {
|
||||
return null;
|
||||
}
|
||||
library = component;
|
||||
i++;
|
||||
}
|
||||
return component;
|
||||
@ -253,13 +272,21 @@ function findComponent(componentName: string, npm?: NpmInfo) {
|
||||
if (!npm) {
|
||||
return accessLibrary(componentName);
|
||||
}
|
||||
// libraryName the key access to global
|
||||
// export { exportName } from xxx exportName === global.libraryName.exportName
|
||||
// export exportName from xxx exportName === global.libraryName.default || global.libraryName
|
||||
// export { exportName as componentName } from package
|
||||
// if exportName == null exportName === componentName;
|
||||
// const componentName = exportName.subName, if exportName empty subName donot use
|
||||
const libraryName = npm.exportName || npm.componentName || componentName;
|
||||
const component = accessLibrary(libraryName);
|
||||
const paths = npm.subName ? npm.subName.split('.') : [];
|
||||
const library = accessLibrary(libraryName);
|
||||
const paths = npm.exportName && npm.subName ? npm.subName.split('.') : [];
|
||||
if (npm.destructuring) {
|
||||
paths.unshift(libraryName);
|
||||
} else if (isESModule(library)) {
|
||||
paths.unshift('default');
|
||||
}
|
||||
return getSubComponent(component, paths);
|
||||
return getSubComponent(library, paths);
|
||||
}
|
||||
|
||||
function buildComponents(componentsMap: { [componentName: string]: ComponentDescriptionSpec }) {
|
||||
|
||||
@ -1,15 +0,0 @@
|
||||
html.my-cursor-dragging, html.my-cursor-dragging * {
|
||||
cursor: move !important
|
||||
}
|
||||
|
||||
html.my-cursor-x-resizing, html.my-cursor-x-resizing * {
|
||||
cursor: col-resize;
|
||||
}
|
||||
|
||||
html.my-cursor-y-resizing, html.my-cursor-y-resizing * {
|
||||
cursor: row-resize;
|
||||
}
|
||||
|
||||
html.my-cursor-copy, html.my-cursor-copy * {
|
||||
cursor: copy !important
|
||||
}
|
||||
@ -12,7 +12,7 @@ import Node, { insertChildren } from './document/node/node';
|
||||
import { isRootNode } from './document/node/root-node';
|
||||
import { ComponentDescriptionSpec, ComponentConfig } from './component-config';
|
||||
import Scroller, { IScrollable } from './helper/scroller';
|
||||
import { INodeInstance } from './simulator';
|
||||
import { INodeSelector } from './simulator';
|
||||
import OffsetObserver, { createOffsetObserver } from './helper/offset-observer';
|
||||
|
||||
export interface DesignerProps {
|
||||
@ -96,7 +96,7 @@ export default class Designer {
|
||||
|
||||
private _dropLocation?: Location;
|
||||
/**
|
||||
* 创建插入位置
|
||||
* 创建插入位置,考虑放到 dragon 中
|
||||
*/
|
||||
createLocation(locationData: LocationData): Location {
|
||||
const loc = new Location(locationData);
|
||||
@ -109,7 +109,7 @@ export default class Designer {
|
||||
/**
|
||||
* 清除插入位置
|
||||
*/
|
||||
private clearLocation() {
|
||||
clearLocation() {
|
||||
if (this._dropLocation) {
|
||||
this._dropLocation.document.internalSetDropLocation(null);
|
||||
}
|
||||
@ -120,7 +120,7 @@ export default class Designer {
|
||||
return new Scroller(scrollable);
|
||||
}
|
||||
|
||||
createOffsetObserver(nodeInstance: INodeInstance): OffsetObserver | null {
|
||||
createOffsetObserver(nodeInstance: INodeSelector): OffsetObserver | null {
|
||||
return createOffsetObserver(nodeInstance);
|
||||
}
|
||||
|
||||
|
||||
@ -3,7 +3,7 @@ import { RootSchema, NodeData, isDOMText, isJSExpression, NodeSchema } from '../
|
||||
import Node, { isNodeParent, insertChildren, insertChild, NodeParent } from './node/node';
|
||||
import { Selection } from './selection';
|
||||
import RootNode from './node/root-node';
|
||||
import { ISimulator, ComponentInstance, Component } from '../simulator';
|
||||
import { ISimulator, ComponentInstance, Component, NodeInstance } from '../simulator';
|
||||
import { computed, obx } from '@recore/obx';
|
||||
import Location from '../helper/location';
|
||||
import { ComponentConfig } from '../component-config';
|
||||
@ -245,16 +245,20 @@ export default class DocumentModel {
|
||||
/**
|
||||
* 通过 DOM 节点获取节点,依赖 simulator 的接口
|
||||
*/
|
||||
getNodeFromElement(target: Element | null): Node | null {
|
||||
getNodeInstanceFromElement(target: Element | null): NodeInstance | null {
|
||||
if (!this.simulator || !target) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const id = this.simulator.getClosestNodeId(target);
|
||||
if (!id) {
|
||||
const nodeIntance = this.simulator.getClosestNodeInstance(target);
|
||||
if (!nodeIntance) {
|
||||
return null;
|
||||
}
|
||||
return this.getNode(id) as Node;
|
||||
const node = this.getNode(nodeIntance.nodeId);
|
||||
return {
|
||||
...nodeIntance,
|
||||
node,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
15
packages/designer/src/designer/helper/cursor.less
Normal file
15
packages/designer/src/designer/helper/cursor.less
Normal file
@ -0,0 +1,15 @@
|
||||
html.lc-cursor-dragging, html.lc-cursor-dragging * {
|
||||
cursor: move !important
|
||||
}
|
||||
|
||||
html.lc-cursor-x-resizing, html.lc-cursor-x-resizing * {
|
||||
cursor: col-resize;
|
||||
}
|
||||
|
||||
html.lc-cursor-y-resizing, html.lc-cursor-y-resizing * {
|
||||
cursor: row-resize;
|
||||
}
|
||||
|
||||
html.lc-cursor-copy, html.lc-cursor-copy * {
|
||||
cursor: copy !important
|
||||
}
|
||||
@ -45,14 +45,14 @@ export class Cursor {
|
||||
private addState(state: string) {
|
||||
if (!this.states.has(state)) {
|
||||
this.states.add(state);
|
||||
document.documentElement.classList.add(`my-cursor-${state}`);
|
||||
document.documentElement.classList.add(`lc-cursor-${state}`);
|
||||
}
|
||||
}
|
||||
|
||||
private removeState(state: string) {
|
||||
if (this.states.has(state)) {
|
||||
this.states.delete(state);
|
||||
document.documentElement.classList.remove(`my-cursor-${state}`);
|
||||
document.documentElement.classList.remove(`lc-cursor-${state}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3,9 +3,11 @@ import { obx } from '@recore/obx';
|
||||
import Location from './location';
|
||||
import DocumentModel from '../document/document-model';
|
||||
import { NodeData } from '../schema';
|
||||
import { ISimulator } from '../simulator';
|
||||
import { ISimulator, isSimulator } from '../simulator';
|
||||
import Node from '../document/node/node';
|
||||
import Designer from '../designer';
|
||||
import { setNativeSelection } from '../../utils/navtive-selection';
|
||||
import cursor from './cursor';
|
||||
|
||||
export interface LocateEvent {
|
||||
readonly type: 'LocateEvent';
|
||||
@ -22,10 +24,17 @@ export interface LocateEvent {
|
||||
* 拖拽对象
|
||||
*/
|
||||
readonly dragObject: DragObject;
|
||||
|
||||
/**
|
||||
* 激活的感应器
|
||||
*/
|
||||
sensor?: ISensor;
|
||||
|
||||
// ======= 以下是 激活的 sensor 将填充的值 ========
|
||||
/**
|
||||
* 浏览器事件响应目标
|
||||
*/
|
||||
target: Element | null;
|
||||
target?: Element | null;
|
||||
/**
|
||||
* 当前激活文档画布坐标系
|
||||
*/
|
||||
@ -134,14 +143,18 @@ export default class Dragon {
|
||||
private sensors: ISensor[] = [];
|
||||
|
||||
/**
|
||||
* current actived sensor
|
||||
* current actived sensor, 可用于感应区高亮
|
||||
*/
|
||||
private _activeSensor: ISensor | undefined;
|
||||
@obx.ref private _activeSensor: ISensor | undefined;
|
||||
get activeSensor(): ISensor | undefined {
|
||||
return this._activeSensor;
|
||||
}
|
||||
|
||||
@obx.ref dragging = false;
|
||||
@obx.ref private _dragging: boolean = false;
|
||||
get dragging(): boolean {
|
||||
return this._dragging;
|
||||
}
|
||||
|
||||
private emitter = new EventEmitter();
|
||||
|
||||
constructor(readonly designer: Designer) {}
|
||||
@ -168,66 +181,69 @@ export default class Dragon {
|
||||
}
|
||||
|
||||
getMasterSensors(): ISimulator[] {
|
||||
return this.designer.project.documents.map(doc => (doc.actived && doc.simulator) || null).filter(Boolean) as any;
|
||||
return this.designer.project.documents.map(doc => {
|
||||
if (doc.actived && doc.simulator?.sensorAvailable) {
|
||||
return doc.simulator;
|
||||
}
|
||||
return null;
|
||||
}).filter(Boolean) as any;
|
||||
}
|
||||
|
||||
/**
|
||||
* dragTarget should be a INode | INode[] | NodeData | NodeData[]
|
||||
*/
|
||||
boost(dragObject: DragObject, boostEvent: MouseEvent) {
|
||||
/*
|
||||
const doc = document;
|
||||
const fromTop = isFromTopDocument(boostEvent);
|
||||
let lastLocation: any = null;
|
||||
let lastSensor: ISensor | undefined;
|
||||
this.dragging = false;
|
||||
const isFromTop = isFromTopDocument(boostEvent);
|
||||
const masterSensors = this.getMasterSensors();
|
||||
masterSensors.forEach((sensor) => {
|
||||
sensor.setNativeSelection(false);
|
||||
});
|
||||
//
|
||||
const designer = this.designer;
|
||||
const newBie = dragObject.type !== DragObjectType.Node;
|
||||
let lastSensor: ISensor | undefined;
|
||||
|
||||
this._dragging = false;
|
||||
// 禁用默认的文稿拖选
|
||||
this.setNativeSelection(false);
|
||||
|
||||
const checkesc = (e: KeyboardEvent) => {
|
||||
if (e.keyCode === 27) {
|
||||
lastLocation = null;
|
||||
master.document.clearLocation();
|
||||
designer.clearLocation();
|
||||
over();
|
||||
}
|
||||
};
|
||||
|
||||
const checkcopy = (e: MouseEvent) => {
|
||||
if (newBie || e.altKey || e.ctrlKey) {
|
||||
master.setCopy(true);
|
||||
this.setCopyState(true);
|
||||
} else {
|
||||
master.setCopy(false);
|
||||
this.setCopyState(false);
|
||||
}
|
||||
};
|
||||
|
||||
// period one fix:
|
||||
// get evt source-sensor
|
||||
// get globalX and globalY source-sensor
|
||||
|
||||
const drag = (e: MouseEvent) => {
|
||||
checkcopy(e);
|
||||
|
||||
const locateEvent = fixEvent(e);
|
||||
const locateEvent = createLocateEvent(e);
|
||||
const sensor = chooseSensor(locateEvent);
|
||||
if (sensor) {
|
||||
sensor.fixEvent(locateEvent);
|
||||
lastLocation = sensor.locate(locateEvent);
|
||||
sensor.locate(locateEvent);
|
||||
} else {
|
||||
master.document.clearLocation();
|
||||
lastLocation = null;
|
||||
designer.clearLocation();
|
||||
}
|
||||
this.emitter.emit('drag', locateEvent, lastLocation);
|
||||
this.emitter.emit('drag', locateEvent);
|
||||
};
|
||||
|
||||
const dragstart = () => {
|
||||
const locateEvent = fixEvent(boostEvent);
|
||||
const locateEvent = createLocateEvent(boostEvent);
|
||||
if (!newBie) {
|
||||
chooseSensor(locateEvent);
|
||||
}
|
||||
master.setDragging(true);
|
||||
this.setDraggingState(true);
|
||||
// ESC cancel drag
|
||||
doc.addEventListener('keydown', checkesc, false);
|
||||
if (topDoc) {
|
||||
topDoc.addEventListener('keydown', checkesc, false);
|
||||
if (isFromTop) {
|
||||
// topDoc.addEventListener('keydown', checkesc, false);
|
||||
}
|
||||
this.emitter.emit('dragstart', locateEvent);
|
||||
};
|
||||
@ -239,7 +255,7 @@ export default class Dragon {
|
||||
}
|
||||
|
||||
if (isShaken(boostEvent, e)) {
|
||||
this.dragging = true;
|
||||
this._dragging = true;
|
||||
|
||||
setShaken(boostEvent);
|
||||
dragstart();
|
||||
@ -249,23 +265,23 @@ export default class Dragon {
|
||||
|
||||
const over = (e?: any) => {
|
||||
if (lastSensor) {
|
||||
lastSensor.deactive();
|
||||
lastSensor.deactiveSensor();
|
||||
}
|
||||
master.setNativeSelection(true);
|
||||
this.setNativeSelection(true);
|
||||
|
||||
let exception;
|
||||
if (this.dragging) {
|
||||
this.dragging = false;
|
||||
if (this._dragging) {
|
||||
this._dragging = false;
|
||||
try {
|
||||
this.emitter.emit('dragend', { dragTarget: dragObject, copy: master.isCopy() }, lastLocation);
|
||||
this.emitter.emit('dragend', { dragTarget: dragObject, copy: this.isCopyState() });
|
||||
} catch (ex) {
|
||||
exception = ex;
|
||||
}
|
||||
}
|
||||
|
||||
master.releaseCursor();
|
||||
this.clearState();
|
||||
|
||||
if (fromTop) {
|
||||
if (isFromTop) {
|
||||
doc.removeEventListener('mousemove', move, true);
|
||||
doc.removeEventListener('mouseup', over, true);
|
||||
doc.removeEventListener('mousedown', over, true);
|
||||
@ -290,28 +306,44 @@ export default class Dragon {
|
||||
}
|
||||
};
|
||||
|
||||
const fixEvent = (e: MouseEvent): LocateEvent => {
|
||||
const createLocateEvent = (e: MouseEvent): LocateEvent => {
|
||||
if (isLocateEvent(e)) {
|
||||
return e;
|
||||
}
|
||||
|
||||
const evt: any = {
|
||||
type: 'LocateEvent',
|
||||
dragObject,
|
||||
target: e.target,
|
||||
dragTarget: dragObject,
|
||||
originalEvent: e,
|
||||
};
|
||||
if (e.view!.document === document) {
|
||||
const l = viewport.toLocalPoint(e);
|
||||
evt.clientX = l.clientX;
|
||||
evt.clientY = l.clientY;
|
||||
|
||||
const sourceDocument = e.view?.document;
|
||||
|
||||
if (!sourceDocument || sourceDocument === document) {
|
||||
evt.globalX = e.clientX;
|
||||
evt.globalY = e.clientY;
|
||||
} else {
|
||||
const g = viewport.toGlobalPoint(e);
|
||||
evt.clientX = e.clientX;
|
||||
evt.clientY = e.clientY;
|
||||
evt.globalX = g.clientX;
|
||||
evt.globalY = g.clientY;
|
||||
let srcSim: ISimulator | undefined;
|
||||
let lastSim = lastSensor && isSimulator(lastSensor) ? lastSensor : null;
|
||||
if (lastSim && lastSim.contentDocument === sourceDocument) {
|
||||
srcSim = lastSim;
|
||||
} else {
|
||||
srcSim = masterSensors.find(sim => sim.contentDocument === sourceDocument);
|
||||
if (!srcSim && lastSim) {
|
||||
srcSim = lastSim;
|
||||
}
|
||||
}
|
||||
if (srcSim) {
|
||||
const g = srcSim.viewport.toGlobalPoint(e);
|
||||
evt.globalX = g.clientX;
|
||||
evt.globalY = g.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;
|
||||
};
|
||||
@ -320,15 +352,13 @@ export default class Dragon {
|
||||
if (!isDragNodeObject(dragObject)) {
|
||||
return null;
|
||||
}
|
||||
return (Array.isArray(dragObject.nodes) ? dragObject.nodes[0] : dragObject.nodes)?.document.simulator || null;
|
||||
return dragObject.nodes[0]?.document.simulator || null;
|
||||
}
|
||||
|
||||
const simSensors = this.project.documents.map(doc => (doc.actived && doc.simulator) || null).filter(Boolean);
|
||||
const sourceSensor = getSourceSensor(dragObject);
|
||||
// check simulator is empty
|
||||
const sensors: ISensor[] = simSensors.concat(this.sensors);
|
||||
const sensors: ISensor[] = (masterSensors as ISensor[]).concat(this.sensors);
|
||||
const chooseSensor = (e: LocateEvent) => {
|
||||
let sensor = sensors.find(s => s.sensorAvailable && s.isEnter(e));
|
||||
let sensor = e.sensor || sensors.find(s => s.sensorAvailable && s.isEnter(e));
|
||||
if (!sensor) {
|
||||
if (lastSensor) {
|
||||
sensor = lastSensor;
|
||||
@ -343,6 +373,7 @@ export default class Dragon {
|
||||
lastSensor = sensor;
|
||||
}
|
||||
if (sensor) {
|
||||
e.sensor = sensor;
|
||||
sensor.fixEvent(e);
|
||||
}
|
||||
this._activeSensor = sensor;
|
||||
@ -352,26 +383,81 @@ export default class Dragon {
|
||||
doc.addEventListener('mousemove', move, true);
|
||||
doc.addEventListener('mouseup', over, true);
|
||||
doc.addEventListener('mousedown', over, true);
|
||||
if (topDoc) {
|
||||
if (isFromTop) {/*
|
||||
topDoc.addEventListener('mousemove', move, true);
|
||||
topDoc.addEventListener('mouseup', over, true);
|
||||
topDoc.addEventListener('mousedown', over, true);
|
||||
*/
|
||||
}
|
||||
|
||||
if (!newBie) {
|
||||
doc.addEventListener('keydown', checkcopy as any, false);
|
||||
doc.addEventListener('keyup', checkcopy as any, false);
|
||||
if (topDoc) {
|
||||
if (isFromTop) {/*
|
||||
topDoc.addEventListener('keydown', checkcopy as any, false);
|
||||
topDoc.addEventListener('keyup', checkcopy as any, false);
|
||||
*/
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
// #region ======== drag and drop helpers ============
|
||||
private setNativeSelection(enableFlag: boolean) {
|
||||
setNativeSelection(enableFlag);
|
||||
this.designer.project.documents.forEach(doc => {
|
||||
doc.simulator?.setNativeSelection(enableFlag);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置拖拽态
|
||||
*/
|
||||
private setDraggingState(state: boolean) {
|
||||
cursor.setDragging(state);
|
||||
this.designer.project.documents.forEach(doc => {
|
||||
doc.simulator?.setDraggingState(state);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置拷贝态
|
||||
*/
|
||||
private setCopyState(state: boolean) {
|
||||
cursor.setCopy(state);
|
||||
this.designer.project.documents.forEach(doc => {
|
||||
doc.simulator?.setCopyState(state);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否拷贝态
|
||||
*/
|
||||
private isCopyState(): boolean {
|
||||
return cursor.isCopy();
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除所有态:拖拽态、拷贝态
|
||||
*/
|
||||
private clearState() {
|
||||
cursor.release();
|
||||
this.designer.project.documents.forEach(doc => {
|
||||
doc.simulator?.clearState();
|
||||
});
|
||||
}
|
||||
// #endregion
|
||||
|
||||
|
||||
/**
|
||||
* 添加投放感应区
|
||||
*/
|
||||
addSensor(sensor: any) {
|
||||
this.sensors.push(sensor);
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除投放感应
|
||||
*/
|
||||
removeSensor(sensor: any) {
|
||||
const i = this.sensors.indexOf(sensor);
|
||||
if (i > -1) {
|
||||
@ -386,14 +472,14 @@ export default class Dragon {
|
||||
};
|
||||
}
|
||||
|
||||
onDrag(func: (e: LocateEvent, location: Location) => any) {
|
||||
onDrag(func: (e: LocateEvent) => any) {
|
||||
this.emitter.on('drag', func);
|
||||
return () => {
|
||||
this.emitter.removeListener('drag', func);
|
||||
};
|
||||
}
|
||||
|
||||
onDragend(func: (x: { dragObject: DragObject; copy: boolean }, location: Location) => any) {
|
||||
onDragend(func: (x: { dragObject: DragObject; copy: boolean }) => any) {
|
||||
this.emitter.on('dragend', func);
|
||||
return () => {
|
||||
this.emitter.removeListener('dragend', func);
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { obx, computed } from '@recore/obx';
|
||||
import { INodeInstance, IViewport } from '../simulator';
|
||||
import { INodeSelector, IViewport } from '../simulator';
|
||||
import Viewport from '../../builtins/simulator/host/viewport';
|
||||
|
||||
export default class OffsetObserver {
|
||||
@ -24,7 +24,7 @@ export default class OffsetObserver {
|
||||
private pid: number | undefined;
|
||||
private viewport: IViewport;
|
||||
|
||||
constructor(readonly nodeInstance: INodeInstance) {
|
||||
constructor(readonly nodeInstance: INodeSelector) {
|
||||
const { node, instance } = nodeInstance;
|
||||
const doc = node.document;
|
||||
const host = doc.simulator!;
|
||||
@ -67,7 +67,7 @@ export default class OffsetObserver {
|
||||
}
|
||||
}
|
||||
|
||||
export function createOffsetObserver(nodeInstance: INodeInstance): OffsetObserver | null {
|
||||
export function createOffsetObserver(nodeInstance: INodeSelector): OffsetObserver | null {
|
||||
if (!nodeInstance.instance) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -59,6 +59,7 @@ export interface IViewport extends IScrollable {
|
||||
* 模拟器控制进程协议
|
||||
*/
|
||||
export interface ISimulator<P = object> extends ISensor {
|
||||
readonly isSimulator: true;
|
||||
/**
|
||||
* 获得边界维度等信息
|
||||
*/
|
||||
@ -77,26 +78,23 @@ export interface ISimulator<P = object> extends ISensor {
|
||||
// 获取区块代码, 通过 components 传递,可异步获取
|
||||
setProps(props: P): void;
|
||||
|
||||
|
||||
setSuspense(suspensed: boolean): void;
|
||||
|
||||
// #region ========= drag and drop helpers =============
|
||||
|
||||
/**
|
||||
* 设置文字拖选
|
||||
*/
|
||||
setNativeSelection(enableFlag: boolean): void;
|
||||
/**
|
||||
* 设置拖拽态
|
||||
*/
|
||||
setDraggingState(state: boolean): void;
|
||||
|
||||
/**
|
||||
* 是否拖拽态
|
||||
*/
|
||||
isDraggingState(): boolean;
|
||||
|
||||
/**
|
||||
* 设置拷贝态
|
||||
*/
|
||||
setCopyState(state: boolean): void;
|
||||
|
||||
/**
|
||||
* 是否拷贝态
|
||||
*/
|
||||
isCopyState(): boolean;
|
||||
|
||||
/**
|
||||
* 清除所有态:拖拽态、拷贝态
|
||||
*/
|
||||
@ -107,16 +105,18 @@ export interface ISimulator<P = object> extends ISensor {
|
||||
*/
|
||||
locate(e: LocateEvent): any;
|
||||
|
||||
/**
|
||||
* 滚动视口到节点
|
||||
*/
|
||||
scrollToNode(node: Node, detail?: any): void;
|
||||
|
||||
/**
|
||||
* 给 event 打补丁,添加 canvasX, globalX 等信息,用于拖拽
|
||||
*/
|
||||
fixEvent(e: LocateEvent): LocateEvent;
|
||||
|
||||
// #endregion
|
||||
|
||||
/**
|
||||
* 滚动视口到节点
|
||||
*/
|
||||
scrollToNode(node: Node, detail?: any): void;
|
||||
|
||||
/**
|
||||
* 描述组件
|
||||
*/
|
||||
@ -134,19 +134,28 @@ export interface ISimulator<P = object> extends ISensor {
|
||||
*/
|
||||
getComponentContext(node: Node): object | null;
|
||||
|
||||
getClosestNodeId(elem: Element): string | null;
|
||||
getClosestNodeInstance(elem: Element): NodeInstance | null;
|
||||
|
||||
computeComponentInstanceRect(instance: ComponentInstance): DOMRect | null;
|
||||
|
||||
findDOMNodes(instance: ComponentInstance): Array<Element | Text> | null;
|
||||
|
||||
setSuspense(suspensed: boolean): void;
|
||||
/**
|
||||
* 销毁
|
||||
*/
|
||||
purge(): void;
|
||||
}
|
||||
|
||||
export function isSimulator(obj: any): obj is ISimulator {
|
||||
return obj && obj.isSimulator;
|
||||
}
|
||||
|
||||
export interface NodeInstance {
|
||||
nodeId: string;
|
||||
instance: ComponentInstance;
|
||||
node?: Node | null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 组件类定义
|
||||
*/
|
||||
@ -157,7 +166,7 @@ export type Component = ComponentType<any> | object;
|
||||
*/
|
||||
export type ComponentInstance = Element | ReactComponent<any> | object;
|
||||
|
||||
export interface INodeInstance {
|
||||
export interface INodeSelector {
|
||||
node: Node;
|
||||
instance?: ComponentInstance;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user