mirror of
https://github.com/alibaba/lowcode-engine.git
synced 2025-12-14 13:03:07 +00:00
dragon 100%
This commit is contained in:
parent
59818ae3b6
commit
ada3fe12ec
@ -1,7 +1,8 @@
|
||||
// NOTE: 仅用作类型标注,切勿作为实体使用
|
||||
import { SimulatorRenderer } from '../renderer/renderer';
|
||||
import { SimulatorHost } from './host';
|
||||
import { AssetLevel, AssetLevels, AssetList, isAssetBundle, isAssetItem, isCSSUrl, AssetType, assetItem } from '../utils/asset';
|
||||
import { AssetLevel, AssetLevels, AssetList, isAssetBundle, isAssetItem, AssetType, assetItem } from '../utils/asset';
|
||||
import { isCSSUrl } from '../../../utils/is-css-url';
|
||||
|
||||
export function createSimulator(host: SimulatorHost, iframe: HTMLIFrameElement, vendors: AssetList = []): Promise<SimulatorRenderer> {
|
||||
const win: any = iframe.contentWindow;
|
||||
|
||||
@ -12,7 +12,7 @@ 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 { setNativeSelection } from '../../../designer/helper/navtive-selection';
|
||||
import cursor from '../../../designer/helper/cursor';
|
||||
|
||||
export interface SimulatorProps {
|
||||
|
||||
@ -4,66 +4,14 @@ import { host } from './host';
|
||||
import SimulatorRendererView from './renderer-view';
|
||||
import { computed, obx } from '@recore/obx';
|
||||
import { RootSchema, NpmInfo } from '../../../designer/schema';
|
||||
import { isElement, getClientRects } from '../../../utils/dom';
|
||||
import { getClientRects } from '../../../utils/get-client-rects';
|
||||
import { Asset } from '../utils/asset';
|
||||
import loader from '../utils/loader';
|
||||
import { ComponentDescriptionSpec } from '../../../designer/component-config';
|
||||
import { findDOMNodes } from '../utils/react';
|
||||
import { reactFindDOMNodes } from '../utils/react-find-dom-nodes';
|
||||
import { isESModule } from '../../../utils/is-es-module';
|
||||
import { NodeInstance } from '../../../designer/simulator';
|
||||
|
||||
let REACT_KEY = '';
|
||||
function cacheReactKey(el: Element): Element {
|
||||
if (REACT_KEY !== '') {
|
||||
return el;
|
||||
}
|
||||
REACT_KEY = Object.keys(el).find(key => key.startsWith('__reactInternalInstance$')) || '';
|
||||
if (!REACT_KEY && (el as HTMLElement).parentElement) {
|
||||
return cacheReactKey((el as HTMLElement).parentElement!);
|
||||
}
|
||||
return el;
|
||||
}
|
||||
|
||||
const SYMBOL_VNID = Symbol('_LCNodeId');
|
||||
|
||||
function getClosestNodeInstance(element: Element): NodeInstance | null {
|
||||
let el: any = element;
|
||||
if (el) {
|
||||
el = cacheReactKey(el);
|
||||
}
|
||||
while (el) {
|
||||
if (SYMBOL_VNID in el) {
|
||||
return {
|
||||
nodeId: el[SYMBOL_VNID],
|
||||
instance: el,
|
||||
};
|
||||
}
|
||||
// get fiberNode from element
|
||||
if (el[REACT_KEY]) {
|
||||
return getNodeInstance(el[REACT_KEY]);
|
||||
}
|
||||
el = el.parentElement;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function getNodeInstance(fiberNode: any): NodeInstance | null {
|
||||
const instance = fiberNode.stateNode;
|
||||
if (instance && SYMBOL_VNID in instance) {
|
||||
return {
|
||||
nodeId: instance[SYMBOL_VNID],
|
||||
instance,
|
||||
};
|
||||
}
|
||||
return getNodeInstance(fiberNode.return);
|
||||
}
|
||||
|
||||
function checkInstanceMounted(instance: any): boolean {
|
||||
if (isElement(instance)) {
|
||||
return instance.parentElement != null;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
import { isElement } from '../../../utils/is-element';
|
||||
|
||||
export class SimulatorRenderer {
|
||||
readonly isSimulatorRenderer = true;
|
||||
@ -205,7 +153,7 @@ export class SimulatorRenderer {
|
||||
}
|
||||
|
||||
findDOMNodes(instance: ReactInstance): Array<Element | Text> | null {
|
||||
return findDOMNodes(instance);
|
||||
return reactFindDOMNodes(instance);
|
||||
}
|
||||
|
||||
getClientRects(element: Element | Text) {
|
||||
@ -297,4 +245,58 @@ function buildComponents(componentsMap: { [componentName: string]: ComponentDesc
|
||||
return components;
|
||||
}
|
||||
|
||||
|
||||
let REACT_KEY = '';
|
||||
function cacheReactKey(el: Element): Element {
|
||||
if (REACT_KEY !== '') {
|
||||
return el;
|
||||
}
|
||||
REACT_KEY = Object.keys(el).find(key => key.startsWith('__reactInternalInstance$')) || '';
|
||||
if (!REACT_KEY && (el as HTMLElement).parentElement) {
|
||||
return cacheReactKey((el as HTMLElement).parentElement!);
|
||||
}
|
||||
return el;
|
||||
}
|
||||
|
||||
const SYMBOL_VNID = Symbol('_LCNodeId');
|
||||
|
||||
function getClosestNodeInstance(element: Element): NodeInstance | null {
|
||||
let el: any = element;
|
||||
if (el) {
|
||||
el = cacheReactKey(el);
|
||||
}
|
||||
while (el) {
|
||||
if (SYMBOL_VNID in el) {
|
||||
return {
|
||||
nodeId: el[SYMBOL_VNID],
|
||||
instance: el,
|
||||
};
|
||||
}
|
||||
// get fiberNode from element
|
||||
if (el[REACT_KEY]) {
|
||||
return getNodeInstance(el[REACT_KEY]);
|
||||
}
|
||||
el = el.parentElement;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function getNodeInstance(fiberNode: any): NodeInstance | null {
|
||||
const instance = fiberNode.stateNode;
|
||||
if (instance && SYMBOL_VNID in instance) {
|
||||
return {
|
||||
nodeId: instance[SYMBOL_VNID],
|
||||
instance,
|
||||
};
|
||||
}
|
||||
return getNodeInstance(fiberNode.return);
|
||||
}
|
||||
|
||||
function checkInstanceMounted(instance: any): boolean {
|
||||
if (isElement(instance)) {
|
||||
return instance.parentElement != null;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
export default new SimulatorRenderer();
|
||||
|
||||
@ -57,10 +57,6 @@ export function isAssetBundle(obj: any): obj is AssetBundle {
|
||||
return obj && obj.type === AssetType.Bundle;
|
||||
}
|
||||
|
||||
export function isCSSUrl(url: string): boolean {
|
||||
return /\.css$/.test(url);
|
||||
}
|
||||
|
||||
export function assetBundle(assets?: Asset | AssetList | null, level?: AssetLevel): AssetBundle | null {
|
||||
if (!assets) {
|
||||
return null;
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { load, evaluate } from './script';
|
||||
import StylePoint from './style';
|
||||
import { Asset, AssetLevel, AssetLevels, AssetType, AssetList, isAssetBundle, isAssetItem, assetItem, isCSSUrl, AssetItem } from './asset';
|
||||
import { Asset, AssetLevel, AssetLevels, AssetType, AssetList, isAssetBundle, isAssetItem, assetItem, AssetItem } from './asset';
|
||||
import { isCSSUrl } from '../../../utils/is-css-url';
|
||||
|
||||
function parseAssetList(scripts: any, styles: any, assets: AssetList, level?: AssetLevel) {
|
||||
for (let asset of assets) {
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { ReactInstance } from 'react';
|
||||
import { isDOMNode, isElement } from '../../../utils/dom';
|
||||
import { isElement } from '../../../utils/is-element';
|
||||
import { isDOMNode } from '../../../utils/is-dom-node';
|
||||
|
||||
const FIBER_KEY = '_reactInternalFiber';
|
||||
|
||||
@ -18,7 +19,7 @@ function elementsFromFiber(fiber: any, elements: Array<Element | Text>) {
|
||||
}
|
||||
}
|
||||
|
||||
export function findDOMNodes(elem: ReactInstance | null): Array<Element | Text> | null {
|
||||
export function reactFindDOMNodes(elem: ReactInstance | null): Array<Element | Text> | null {
|
||||
if (!elem) {
|
||||
return null;
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
import { createDefer } from '../../../utils/create-defer';
|
||||
import { createDefer } from './create-defer';
|
||||
|
||||
export function evaluate(script: string) {
|
||||
const scriptEl = document.createElement('script');
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { createDefer } from '../../../utils/create-defer';
|
||||
import { createDefer } from './create-defer';
|
||||
|
||||
export default class StylePoint {
|
||||
private lastContent: string | undefined;
|
||||
|
||||
@ -56,6 +56,7 @@ export default class Designer {
|
||||
});
|
||||
|
||||
this.dragon.onDrag(e => {
|
||||
console.info(e);
|
||||
if (this.props?.onDrag) {
|
||||
this.props.onDrag(e);
|
||||
}
|
||||
|
||||
@ -7,7 +7,7 @@ import { ISimulator, ComponentInstance, Component, NodeInstance } from '../simul
|
||||
import { computed, obx } from '@recore/obx';
|
||||
import Location from '../helper/location';
|
||||
import { ComponentConfig } from '../component-config';
|
||||
import { isElement } from '../../utils/dom';
|
||||
import { isElement } from '../../utils/is-element';
|
||||
|
||||
export default class DocumentModel {
|
||||
/**
|
||||
|
||||
@ -6,7 +6,7 @@ import { NodeData } from '../schema';
|
||||
import { ISimulator, isSimulator } from '../simulator';
|
||||
import Node from '../document/node/node';
|
||||
import Designer from '../designer';
|
||||
import { setNativeSelection } from '../../utils/navtive-selection';
|
||||
import { setNativeSelection } from './navtive-selection';
|
||||
import cursor from './cursor';
|
||||
|
||||
export interface LocateEvent {
|
||||
@ -135,8 +135,22 @@ export function setShaken(e: any) {
|
||||
e.shaken = true;
|
||||
}
|
||||
|
||||
function isFromTopDocument(e: MouseEvent) {
|
||||
return e.view!.document === document;
|
||||
function getSourceSensor(dragObject: DragObject): ISimulator | null {
|
||||
if (!isDragNodeObject(dragObject)) {
|
||||
return null;
|
||||
}
|
||||
return dragObject.nodes[0]?.document.simulator || null;
|
||||
}
|
||||
|
||||
function makeSimulatorListener(masterSensors: ISimulator[]): (fn: (sdoc: Document) => void) => void {
|
||||
return (fn: (sdoc: Document) => void) => {
|
||||
masterSensors.forEach(sim => {
|
||||
const sdoc = sim.contentDocument;
|
||||
if (sdoc) {
|
||||
fn(sdoc);
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export default class Dragon {
|
||||
@ -167,12 +181,12 @@ export default class Dragon {
|
||||
}
|
||||
|
||||
// Get a new node to be dragged
|
||||
const dragTarget = boost(e);
|
||||
if (!dragTarget) {
|
||||
const dragObject = boost(e);
|
||||
if (!dragObject) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.boost(dragTarget, e);
|
||||
this.boost(dragObject, e);
|
||||
};
|
||||
shell.addEventListener('mousedown', mousedown as any);
|
||||
return () => {
|
||||
@ -180,24 +194,18 @@ export default class Dragon {
|
||||
};
|
||||
}
|
||||
|
||||
getMasterSensors(): ISimulator[] {
|
||||
return this.designer.project.documents.map(doc => {
|
||||
if (doc.actived && doc.simulator?.sensorAvailable) {
|
||||
return doc.simulator;
|
||||
}
|
||||
return null;
|
||||
}).filter(Boolean) as any;
|
||||
}
|
||||
|
||||
boost(dragObject: DragObject, boostEvent: MouseEvent) {
|
||||
const doc = document;
|
||||
const isFromTop = isFromTopDocument(boostEvent);
|
||||
const sourceDoc = boostEvent.view?.document;
|
||||
const masterSensors = this.getMasterSensors();
|
||||
const listenSimulators = !sourceDoc || sourceDoc === doc ? makeSimulatorListener(masterSensors) : null;
|
||||
const alwaysListen = listenSimulators ? doc : sourceDoc!;
|
||||
const designer = this.designer;
|
||||
const newBie = dragObject.type !== DragObjectType.Node;
|
||||
let lastSensor: ISensor | undefined;
|
||||
|
||||
this._dragging = false;
|
||||
|
||||
// 禁用默认的文稿拖选
|
||||
this.setNativeSelection(false);
|
||||
|
||||
@ -216,10 +224,6 @@ export default class Dragon {
|
||||
}
|
||||
};
|
||||
|
||||
// period one fix:
|
||||
// get evt source-sensor
|
||||
// get globalX and globalY source-sensor
|
||||
|
||||
const drag = (e: MouseEvent) => {
|
||||
checkcopy(e);
|
||||
|
||||
@ -241,10 +245,12 @@ export default class Dragon {
|
||||
}
|
||||
this.setDraggingState(true);
|
||||
// ESC cancel drag
|
||||
doc.addEventListener('keydown', checkesc, false);
|
||||
if (isFromTop) {
|
||||
// topDoc.addEventListener('keydown', checkesc, false);
|
||||
}
|
||||
alwaysListen.addEventListener('keydown', checkesc, false);
|
||||
listenSimulators &&
|
||||
listenSimulators(sdoc => {
|
||||
sdoc.addEventListener('keydown', checkesc, false);
|
||||
});
|
||||
|
||||
this.emitter.emit('dragstart', locateEvent);
|
||||
};
|
||||
|
||||
@ -281,26 +287,21 @@ export default class Dragon {
|
||||
|
||||
this.clearState();
|
||||
|
||||
if (isFromTop) {
|
||||
doc.removeEventListener('mousemove', move, true);
|
||||
doc.removeEventListener('mouseup', over, true);
|
||||
doc.removeEventListener('mousedown', over, true);
|
||||
doc.removeEventListener('keydown', checkesc, false);
|
||||
doc.removeEventListener('keydown', checkcopy as any, false);
|
||||
doc.removeEventListener('keyup', checkcopy as any, false);
|
||||
} else {
|
||||
masterSensors.forEach(item => {
|
||||
const odoc = item.contentDocument;
|
||||
if (odoc) {
|
||||
odoc.removeEventListener('mousemove', move, true);
|
||||
odoc.removeEventListener('mouseup', over, true);
|
||||
odoc.removeEventListener('mousedown', over, true);
|
||||
odoc.removeEventListener('keydown', checkesc, false);
|
||||
odoc.removeEventListener('keydown', checkcopy as any, false);
|
||||
odoc.removeEventListener('keyup', checkcopy as any, false);
|
||||
}
|
||||
alwaysListen.removeEventListener('mousemove', move, true);
|
||||
alwaysListen.removeEventListener('mouseup', over, true);
|
||||
alwaysListen.removeEventListener('mousedown', over, true);
|
||||
alwaysListen.removeEventListener('keydown', checkesc, false);
|
||||
alwaysListen.removeEventListener('keydown', checkcopy as any, false);
|
||||
alwaysListen.removeEventListener('keyup', checkcopy as any, false);
|
||||
listenSimulators &&
|
||||
listenSimulators(sdoc => {
|
||||
sdoc.removeEventListener('mousemove', move, true);
|
||||
sdoc.removeEventListener('mouseup', over, true);
|
||||
sdoc.removeEventListener('mousedown', over, true);
|
||||
sdoc.removeEventListener('keydown', checkesc, false);
|
||||
sdoc.removeEventListener('keydown', checkcopy as any, false);
|
||||
sdoc.removeEventListener('keyup', checkcopy as any, false);
|
||||
});
|
||||
}
|
||||
if (exception) {
|
||||
throw exception;
|
||||
}
|
||||
@ -348,13 +349,6 @@ export default class Dragon {
|
||||
return evt;
|
||||
};
|
||||
|
||||
function getSourceSensor(dragObject: DragObject): ISimulator | null {
|
||||
if (!isDragNodeObject(dragObject)) {
|
||||
return null;
|
||||
}
|
||||
return dragObject.nodes[0]?.document.simulator || null;
|
||||
}
|
||||
|
||||
const sourceSensor = getSourceSensor(dragObject);
|
||||
const sensors: ISensor[] = (masterSensors as ISensor[]).concat(this.sensors);
|
||||
const chooseSensor = (e: LocateEvent) => {
|
||||
@ -380,27 +374,42 @@ export default class Dragon {
|
||||
return sensor;
|
||||
};
|
||||
|
||||
doc.addEventListener('mousemove', move, true);
|
||||
doc.addEventListener('mouseup', over, true);
|
||||
doc.addEventListener('mousedown', over, true);
|
||||
if (isFromTop) {/*
|
||||
topDoc.addEventListener('mousemove', move, true);
|
||||
topDoc.addEventListener('mouseup', over, true);
|
||||
topDoc.addEventListener('mousedown', over, true);
|
||||
*/
|
||||
}
|
||||
alwaysListen.addEventListener('mousemove', move, true);
|
||||
alwaysListen.addEventListener('mouseup', over, true);
|
||||
alwaysListen.addEventListener('mousedown', over, true);
|
||||
listenSimulators &&
|
||||
listenSimulators(sdoc => {
|
||||
// alwaysListen = global document
|
||||
// listen others simulator iframe
|
||||
sdoc.addEventListener('mousemove', move, true);
|
||||
sdoc.addEventListener('mouseup', over, true);
|
||||
sdoc.addEventListener('mousedown', over, true);
|
||||
});
|
||||
|
||||
// future think: drag things from browser-out or a iframe-pane
|
||||
|
||||
if (!newBie) {
|
||||
doc.addEventListener('keydown', checkcopy as any, false);
|
||||
doc.addEventListener('keyup', checkcopy as any, false);
|
||||
if (isFromTop) {/*
|
||||
topDoc.addEventListener('keydown', checkcopy as any, false);
|
||||
topDoc.addEventListener('keyup', checkcopy as any, false);
|
||||
*/
|
||||
}
|
||||
alwaysListen.addEventListener('keydown', checkcopy as any, false);
|
||||
alwaysListen.addEventListener('keyup', checkcopy as any, false);
|
||||
listenSimulators &&
|
||||
listenSimulators(sdoc => {
|
||||
sdoc.addEventListener('keydown', checkcopy as any, false);
|
||||
sdoc.addEventListener('keyup', checkcopy as any, false);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private getMasterSensors(): ISimulator[] {
|
||||
return this.designer.project.documents
|
||||
.map(doc => {
|
||||
if (doc.actived && doc.simulator?.sensorAvailable) {
|
||||
return doc.simulator;
|
||||
}
|
||||
return null;
|
||||
})
|
||||
.filter(Boolean) as any;
|
||||
}
|
||||
|
||||
// #region ======== drag and drop helpers ============
|
||||
private setNativeSelection(enableFlag: boolean) {
|
||||
setNativeSelection(enableFlag);
|
||||
@ -447,7 +456,6 @@ export default class Dragon {
|
||||
}
|
||||
// #endregion
|
||||
|
||||
|
||||
/**
|
||||
* 添加投放感应区
|
||||
*/
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { isElement } from '../../utils/dom';
|
||||
import { isElement } from '../../utils/is-element';
|
||||
|
||||
export class ScrollTarget {
|
||||
get left() {
|
||||
|
||||
@ -1,10 +1,4 @@
|
||||
export function isDOMNode(node: any): node is Element | Text {
|
||||
return node.nodeType && (node.nodeType === Node.ELEMENT_NODE || node.nodeType === Node.TEXT_NODE);
|
||||
}
|
||||
|
||||
export function isElement(node: any): node is Element {
|
||||
return node.nodeType === Node.ELEMENT_NODE;
|
||||
}
|
||||
import { isElement } from './is-element';
|
||||
|
||||
// a range for test TextNode clientRect
|
||||
const cycleRange = document.createRange();
|
||||
3
packages/designer/src/utils/is-dom-node.ts
Normal file
3
packages/designer/src/utils/is-dom-node.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export function isDOMNode(node: any): node is Element | Text {
|
||||
return node.nodeType && (node.nodeType === Node.ELEMENT_NODE || node.nodeType === Node.TEXT_NODE);
|
||||
}
|
||||
3
packages/designer/src/utils/is-element.ts
Normal file
3
packages/designer/src/utils/is-element.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export function isElement(node: any): node is Element {
|
||||
return node.nodeType === Node.ELEMENT_NODE;
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user