mirror of
https://github.com/alibaba/lowcode-engine.git
synced 2025-12-15 05:28:00 +00:00
fix outlines
This commit is contained in:
parent
4d4cfab271
commit
a56faac6ae
@ -72,10 +72,12 @@ export class OutlineHovering extends Component {
|
||||
render() {
|
||||
const host = this.context as SimulatorHost;
|
||||
const current = this.current;
|
||||
if (!current) {
|
||||
console.info('current', current)
|
||||
if (!current || host.viewport.scrolling) {
|
||||
return <Fragment />;
|
||||
}
|
||||
const instances = host.getComponentInstances(current);
|
||||
console.info('current instances', instances)
|
||||
if (!instances || instances.length < 1) {
|
||||
return <Fragment />;
|
||||
}
|
||||
|
||||
@ -5,6 +5,7 @@ import { SimulatorContext } from '../context';
|
||||
import { SimulatorHost } from '../host';
|
||||
import { computed } from '@recore/obx';
|
||||
import OffsetObserver from '../../../../designer/helper/offset-observer';
|
||||
import Node from '../../../../designer/document/node/node';
|
||||
|
||||
@observer
|
||||
export class OutlineSelectingInstance extends Component<{ observed: OffsetObserver; highlight?: boolean }> {
|
||||
@ -12,18 +13,22 @@ export class OutlineSelectingInstance extends Component<{ observed: OffsetObserv
|
||||
return false;
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.props.observed.purge();
|
||||
}
|
||||
|
||||
render() {
|
||||
const { observed, highlight } = this.props;
|
||||
if (!observed.hasOffset) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { scale, width, height, offsetTop, offsetLeft } = observed;
|
||||
const { offsetWidth, offsetHeight, offsetTop, offsetLeft } = observed;
|
||||
|
||||
const style = {
|
||||
width: width * scale,
|
||||
height: height * scale,
|
||||
transform: `translate3d(${offsetLeft * scale}px, ${offsetTop * scale}px, 0)`,
|
||||
width: offsetWidth,
|
||||
height: offsetHeight,
|
||||
transform: `translate3d(${offsetLeft}px, ${offsetTop}px, 0)`,
|
||||
};
|
||||
|
||||
const className = classNames('lc-outlines lc-outlines-selecting', {
|
||||
@ -39,13 +44,54 @@ export class OutlineSelectingInstance extends Component<{ observed: OffsetObserv
|
||||
}
|
||||
|
||||
@observer
|
||||
export class OutlineSelecting extends Component {
|
||||
export class OutlineSelectingForNode extends Component<{ node: Node }> {
|
||||
static contextType = SimulatorContext;
|
||||
|
||||
get host(): SimulatorHost {
|
||||
return this.context;
|
||||
}
|
||||
|
||||
@computed get instances() {
|
||||
return this.host.getComponentInstances(this.props.node);
|
||||
}
|
||||
|
||||
shouldComponentUpdate() {
|
||||
return false;
|
||||
}
|
||||
|
||||
render() {
|
||||
const { instances } = this;
|
||||
const { node } = this.props;
|
||||
const designer = this.host.designer;
|
||||
|
||||
if (!instances || instances.length < 1) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<Fragment key={node.id}>
|
||||
{instances.map((instance) => {
|
||||
const observed = designer.createOffsetObserver({
|
||||
node,
|
||||
instance,
|
||||
});
|
||||
if (!observed) {
|
||||
return null;
|
||||
}
|
||||
return <OutlineSelectingInstance key={observed.id} observed={observed} />;
|
||||
})}
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@observer
|
||||
export class OutlineSelecting extends Component {
|
||||
static contextType = SimulatorContext;
|
||||
|
||||
get host(): SimulatorHost {
|
||||
return this.context;
|
||||
}
|
||||
|
||||
@computed get selecting() {
|
||||
const doc = this.host.document;
|
||||
if (doc.suspensed) {
|
||||
@ -54,8 +100,8 @@ export class OutlineSelecting extends Component {
|
||||
return doc.selection.getNodes();
|
||||
}
|
||||
|
||||
@computed get host(): SimulatorHost {
|
||||
return this.context;
|
||||
shouldComponentUpdate() {
|
||||
return false;
|
||||
}
|
||||
|
||||
render() {
|
||||
@ -65,30 +111,11 @@ export class OutlineSelecting extends Component {
|
||||
return <Fragment />;
|
||||
}
|
||||
|
||||
const designer = this.host.designer;
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
{selecting.map(node => {
|
||||
const instances = this.host.getComponentInstances(node);
|
||||
if (!instances || instances.length < 1) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<Fragment key={node.id}>
|
||||
{instances.map((instance, i) => {
|
||||
const observed = designer.createOffsetObserver({
|
||||
node,
|
||||
instance,
|
||||
});
|
||||
if (!observed) {
|
||||
return null;
|
||||
}
|
||||
return <OutlineSelectingInstance key={`line-s-${i}`} observed={observed} />;
|
||||
})}
|
||||
</Fragment>
|
||||
);
|
||||
})}
|
||||
{selecting.map(node => (
|
||||
<OutlineSelectingForNode key={node.id} node={node} />
|
||||
))}
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
@ -15,6 +15,7 @@ import {
|
||||
DragNodeDataObject,
|
||||
isDragAnyObject,
|
||||
isDragNodeObject,
|
||||
isDragNodeDataObject,
|
||||
} from '../../../designer/helper/dragon';
|
||||
import {
|
||||
LocationData,
|
||||
@ -347,11 +348,20 @@ export class SimulatorHost implements ISimulator<SimulatorProps> {
|
||||
return null;
|
||||
}
|
||||
|
||||
@obx.val private instancesMap = new Map<string, ReactInstance[]>();
|
||||
setInstance(id: string, instances: ReactInstance[] | null) {
|
||||
if (instances == null) {
|
||||
this.instancesMap.delete(id);
|
||||
} else {
|
||||
this.instancesMap.set(id, instances.slice());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ISimulator
|
||||
*/
|
||||
getComponentInstances(node: Node): ReactInstance[] | null {
|
||||
return this._renderer?.getComponentInstances(node.id) || null;
|
||||
return this.instancesMap.get(node.id) || null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -888,12 +898,22 @@ export class SimulatorHost implements ISimulator<SimulatorProps> {
|
||||
*/
|
||||
|
||||
checkNesting(dropTarget: NodeParent, dragObject: DragNodeObject | DragNodeDataObject): boolean {
|
||||
const items: Array<Node | NodeSchema> = dragObject.nodes || (dragObject as DragNodeDataObject).data;
|
||||
let items: Array<Node | NodeSchema>;
|
||||
if (isDragNodeDataObject(dragObject)) {
|
||||
items = Array.isArray(dragObject.data) ? dragObject.data : [dragObject.data];
|
||||
} else {
|
||||
items = dragObject.nodes
|
||||
}
|
||||
return items.every(item => this.checkNestingDown(dropTarget, item));
|
||||
}
|
||||
|
||||
checkDropTarget(dropTarget: NodeParent, dragObject: DragNodeObject | DragNodeDataObject): boolean {
|
||||
const items: Array<Node | NodeSchema> = dragObject.nodes || (dragObject as DragNodeDataObject).data;
|
||||
let items: Array<Node | NodeSchema>;
|
||||
if (isDragNodeDataObject(dragObject)) {
|
||||
items = Array.isArray(dragObject.data) ? dragObject.data : [dragObject.data];
|
||||
} else {
|
||||
items = dragObject.nodes
|
||||
}
|
||||
return items.every(item => this.checkNestingUp(dropTarget, item));
|
||||
}
|
||||
|
||||
|
||||
@ -98,14 +98,28 @@ export default class Viewport implements IViewport {
|
||||
return this._scrollTarget;
|
||||
}
|
||||
|
||||
@obx private _scrolling: boolean = false;
|
||||
get scrolling(): boolean {
|
||||
return this._scrolling;
|
||||
}
|
||||
|
||||
setScrollTarget(target: Window) {
|
||||
const scrollTarget = new ScrollTarget(target);
|
||||
this._scrollX = scrollTarget.left;
|
||||
this._scrollY = scrollTarget.top;
|
||||
target.onscroll = () => {
|
||||
|
||||
let scrollTimer: any;
|
||||
target.addEventListener('scroll', () => {
|
||||
this._scrollX = scrollTarget.left;
|
||||
this._scrollY = scrollTarget.top;
|
||||
};
|
||||
this._scrolling = true;
|
||||
if (scrollTimer) {
|
||||
clearTimeout(scrollTimer);
|
||||
}
|
||||
scrollTimer = setTimeout(() => {
|
||||
this._scrolling = false;
|
||||
}, 80);
|
||||
});
|
||||
this._scrollTarget = scrollTarget;
|
||||
}
|
||||
|
||||
|
||||
@ -40,10 +40,11 @@ class Renderer extends Component<{ renderer: SimulatorRenderer }> {
|
||||
}
|
||||
render() {
|
||||
const { renderer } = this.props;
|
||||
const { components, schemas } = LowCodeRenderer.others
|
||||
return (
|
||||
<LowCodeRenderer
|
||||
schema={renderer.schema}
|
||||
components={renderer.components}
|
||||
components={components /*renderer.components*/}
|
||||
appHelper={renderer.context}
|
||||
// context={renderer.context}
|
||||
designMode={renderer.designMode}
|
||||
@ -60,11 +61,3 @@ class Renderer extends Component<{ renderer: SimulatorRenderer }> {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
class LowCodeRenderer extends Component<any> {
|
||||
render() {
|
||||
const { schema } = this.props;
|
||||
return <div>{JSON.stringify(this.props.schema)}</div>
|
||||
}
|
||||
}*/
|
||||
|
||||
@ -99,7 +99,18 @@ export class SimulatorRenderer {
|
||||
load(asset: Asset): Promise<any> {
|
||||
return loader.load(asset);
|
||||
}
|
||||
|
||||
private instancesMap = new Map<string, ReactInstance[]>();
|
||||
private unmountIntance(id: string, instance: ReactInstance) {
|
||||
const instances = this.instancesMap.get(id);
|
||||
if (instances) {
|
||||
const i = instances.indexOf(instance);
|
||||
if (i > -1) {
|
||||
instances.splice(i, 1);
|
||||
host.setInstance(id, instances);
|
||||
}
|
||||
}
|
||||
}
|
||||
mountInstance(id: string, instance: ReactInstance | null) {
|
||||
const instancesMap = this.instancesMap;
|
||||
if (instance == null) {
|
||||
@ -108,41 +119,57 @@ export class SimulatorRenderer {
|
||||
instances = instances.filter(checkInstanceMounted);
|
||||
if (instances.length > 0) {
|
||||
instancesMap.set(id, instances);
|
||||
host.setInstance(id, instances);
|
||||
} else {
|
||||
instancesMap.delete(id);
|
||||
host.setInstance(id, null);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
const unmountIntance = this.unmountIntance.bind(this);
|
||||
const origId = (instance as any)[SYMBOL_VNID];
|
||||
if (origId && origId !== id) {
|
||||
// 另外一个节点的 instance 在此被复用了,需要从原来地方卸载
|
||||
unmountIntance(origId, instance);
|
||||
}
|
||||
if (isElement(instance)) {
|
||||
cacheReactKey(instance);
|
||||
} else if (!(instance as any)[SYMBOL_VNID]) {
|
||||
const origUnmout = instance.componentWillUnmount;
|
||||
} else if (origId !== id) {
|
||||
// 涵盖 origId == null || origId !== id 的情况
|
||||
let origUnmount: any = instance.componentWillUnmount;
|
||||
if (origUnmount && origUnmount.origUnmount) {
|
||||
origUnmount = origUnmount.origUnmount;
|
||||
}
|
||||
// hack! delete instance from map
|
||||
instance.componentWillUnmount = function() {
|
||||
const instances = instancesMap.get(id);
|
||||
if (instances) {
|
||||
const i = instances.indexOf(instance);
|
||||
if (i > -1) {
|
||||
instances.splice(i, 1);
|
||||
}
|
||||
}
|
||||
origUnmout && origUnmout.call(this);
|
||||
const newUnmount = function (this: any) {
|
||||
unmountIntance(id, instance);
|
||||
origUnmount && origUnmount.call(this);
|
||||
};
|
||||
(newUnmount as any).origUnmount = origUnmount;
|
||||
instance.componentWillUnmount = newUnmount;
|
||||
}
|
||||
|
||||
(instance as any)[SYMBOL_VNID] = id;
|
||||
let instances = this.instancesMap.get(id);
|
||||
if (instances) {
|
||||
const l = instances.length;
|
||||
instances = instances.filter(checkInstanceMounted);
|
||||
let updated = instances.length !== l;
|
||||
if (!instances.includes(instance)) {
|
||||
instances.push(instance);
|
||||
updated = true;
|
||||
}
|
||||
if (!updated) {
|
||||
return;
|
||||
}
|
||||
instancesMap.set(id, instances);
|
||||
} else {
|
||||
instancesMap.set(id, [instance]);
|
||||
instances = [instance];
|
||||
}
|
||||
instancesMap.set(id, instances);
|
||||
host.setInstance(id, instances);
|
||||
}
|
||||
|
||||
private ctxMap = new Map<string, object>();
|
||||
mountContext(id: string, ctx: object) {
|
||||
this.ctxMap.set(id, ctx);
|
||||
@ -167,21 +194,12 @@ export class SimulatorRenderer {
|
||||
setNativeSelection(enableFlag: boolean) {
|
||||
setNativeSelection(enableFlag);
|
||||
}
|
||||
/**
|
||||
* @see ISimulator
|
||||
*/
|
||||
setDraggingState(state: boolean) {
|
||||
cursor.setDragging(state);
|
||||
}
|
||||
/**
|
||||
* @see ISimulator
|
||||
*/
|
||||
setCopyState(state: boolean) {
|
||||
cursor.setCopy(state);
|
||||
}
|
||||
/**
|
||||
* @see ISimulator
|
||||
*/
|
||||
clearState() {
|
||||
cursor.release();
|
||||
}
|
||||
|
||||
@ -56,7 +56,7 @@
|
||||
.lc-document {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
&&-hidden {
|
||||
&-hidden {
|
||||
// todo:
|
||||
display: none;
|
||||
}
|
||||
|
||||
@ -3,11 +3,10 @@ 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, NodeInstance } from '../simulator';
|
||||
import { ISimulator, Component } from '../simulator';
|
||||
import { computed, obx } from '@recore/obx';
|
||||
import Location from '../helper/location';
|
||||
import { ComponentConfig } from '../component-config';
|
||||
import { isElement } from '../../utils/is-element';
|
||||
|
||||
export default class DocumentModel {
|
||||
/**
|
||||
@ -234,8 +233,8 @@ export default class DocumentModel {
|
||||
|
||||
|
||||
|
||||
private _opened: boolean = true;
|
||||
private _suspensed: boolean = false;
|
||||
@obx.ref private _opened: boolean = true;
|
||||
@obx.ref private _suspensed: boolean = false;
|
||||
|
||||
/**
|
||||
* 是否不是激活的
|
||||
@ -288,6 +287,8 @@ export default class DocumentModel {
|
||||
this._opened = true;
|
||||
if (this._suspensed) {
|
||||
this.setSuspense(false);
|
||||
} else {
|
||||
this.project.checkExclusive(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -220,7 +220,10 @@ export default class Dragon {
|
||||
};
|
||||
|
||||
const checkcopy = (e: MouseEvent) => {
|
||||
if (newBie || e.altKey || e.ctrlKey) {
|
||||
if (newBie) {
|
||||
return;
|
||||
}
|
||||
if (e.altKey || e.ctrlKey) {
|
||||
this.setCopyState(true);
|
||||
} else {
|
||||
this.setCopyState(false);
|
||||
@ -243,7 +246,9 @@ export default class Dragon {
|
||||
|
||||
const dragstart = () => {
|
||||
const locateEvent = createLocateEvent(boostEvent);
|
||||
if (!newBie) {
|
||||
if (newBie) {
|
||||
this.setCopyState(true);
|
||||
} else {
|
||||
chooseSensor(locateEvent);
|
||||
}
|
||||
this.setDraggingState(true);
|
||||
@ -277,19 +282,19 @@ export default class Dragon {
|
||||
lastSensor.deactiveSensor();
|
||||
}
|
||||
this.setNativeSelection(true);
|
||||
const copy = !newBie && this.isCopyState();
|
||||
this.clearState();
|
||||
|
||||
let exception;
|
||||
if (this._dragging) {
|
||||
this._dragging = false;
|
||||
try {
|
||||
this.emitter.emit('dragend', { dragObject, copy: this.isCopyState() });
|
||||
this.emitter.emit('dragend', { dragObject, copy });
|
||||
} catch (ex) {
|
||||
exception = ex;
|
||||
}
|
||||
}
|
||||
|
||||
this.clearState();
|
||||
|
||||
alwaysListen.removeEventListener('mousemove', move, true);
|
||||
alwaysListen.removeEventListener('mouseup', over, true);
|
||||
alwaysListen.removeEventListener('mousedown', over, true);
|
||||
|
||||
@ -1,21 +1,44 @@
|
||||
import { obx, computed } from '@recore/obx';
|
||||
import { INodeSelector, IViewport } from '../simulator';
|
||||
import Viewport from '../../builtins/simulator/host/viewport';
|
||||
import { uniqueId } from '../../utils/unique-id';
|
||||
|
||||
export default class OffsetObserver {
|
||||
@obx.ref hasOffset = false;
|
||||
readonly id = uniqueId('oobx');
|
||||
|
||||
private lastOffsetLeft?: number;
|
||||
private lastOffsetTop?: number;
|
||||
private lastOffsetHeight?: number;
|
||||
private lastOffsetWidth?: number;
|
||||
@obx private height = 0;
|
||||
@obx private width = 0;
|
||||
@obx private left = 0;
|
||||
@obx private top = 0;
|
||||
|
||||
@obx hasOffset = false;
|
||||
@computed get offsetLeft() {
|
||||
return this.left + this.viewport.scrollX;
|
||||
if (!this.viewport.scrolling || this.lastOffsetLeft == null) {
|
||||
this.lastOffsetLeft = (this.left + this.viewport.scrollX) * this.scale;
|
||||
}
|
||||
return this.lastOffsetLeft;
|
||||
}
|
||||
@computed get offsetTop() {
|
||||
return this.top + this.viewport.scrollY;
|
||||
if (!this.viewport.scrolling || this.lastOffsetTop == null) {
|
||||
this.lastOffsetTop = (this.top + this.viewport.scrollY) * this.scale;
|
||||
}
|
||||
return this.lastOffsetTop;
|
||||
}
|
||||
@computed get offsetHeight() {
|
||||
if (!this.viewport.scrolling || this.lastOffsetHeight == null) {
|
||||
this.lastOffsetHeight = this.height * this.scale;
|
||||
}
|
||||
return this.lastOffsetHeight;
|
||||
}
|
||||
@computed get offsetWidth() {
|
||||
if (!this.viewport.scrolling || this.lastOffsetWidth == null) {
|
||||
this.lastOffsetWidth = this.width * this.scale;
|
||||
}
|
||||
return this.lastOffsetWidth;
|
||||
}
|
||||
|
||||
@obx.ref height = 0;
|
||||
@obx.ref width = 0;
|
||||
@obx.ref left = 0;
|
||||
@obx.ref top = 0;
|
||||
|
||||
@computed get scale() {
|
||||
return this.viewport.scale;
|
||||
@ -44,11 +67,13 @@ export default class OffsetObserver {
|
||||
if (!rect) {
|
||||
this.hasOffset = false;
|
||||
} else {
|
||||
this.hasOffset = true;
|
||||
this.height = rect.height;
|
||||
this.width = rect.width;
|
||||
this.left = rect.left;
|
||||
this.top = rect.top;
|
||||
if (!this.viewport.scrolling || !this.hasOffset) {
|
||||
this.height = rect.height;
|
||||
this.width = rect.width;
|
||||
this.left = rect.left;
|
||||
this.top = rect.top;
|
||||
this.hasOffset = true;
|
||||
}
|
||||
}
|
||||
this.pid = pid = (window as any).requestIdleCallback(compute);
|
||||
};
|
||||
@ -59,7 +84,7 @@ export default class OffsetObserver {
|
||||
this.pid = pid = (window as any).requestIdleCallback(compute);
|
||||
}
|
||||
|
||||
destroy() {
|
||||
purge() {
|
||||
if (this.pid) {
|
||||
(window as any).cancelIdleCallback(this.pid);
|
||||
}
|
||||
|
||||
@ -8,6 +8,7 @@ export default class ProjectView extends Component<{ designer: Designer }> {
|
||||
render() {
|
||||
const { designer } = this.props;
|
||||
// TODO: support splitview
|
||||
console.info(designer.project.documents);
|
||||
return (
|
||||
<div className="lc-project">
|
||||
{designer.project.documents.map(doc => {
|
||||
|
||||
@ -34,7 +34,14 @@ export interface IViewport extends IScrollable {
|
||||
* 内容矩形维度
|
||||
*/
|
||||
readonly contentBounds: DOMRect;
|
||||
/**
|
||||
* 视口滚动对象
|
||||
*/
|
||||
readonly scrollTarget?: ScrollTarget;
|
||||
/**
|
||||
* 是否滚动中
|
||||
*/
|
||||
readonly scrolling: boolean;
|
||||
/**
|
||||
* 内容当前滚动 X
|
||||
*/
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user