This commit is contained in:
kangwei 2020-02-16 23:27:09 +08:00
parent eb7a7f519f
commit 6cb06d3369
12 changed files with 254 additions and 723 deletions

View File

@ -1,105 +0,0 @@
import { Component } from 'react';
import { observer, obx } from '@ali/recore';
import { dragon } from '../../globals/dragon';
import './ghost.less';
import { OutlineBoardID } from '../builtin-panes/outline-pane/outline-board';
// import { INode } from '../../document/node';
type offBinding = () => any;
@observer
export default class Ghost extends Component {
private dispose: offBinding[] = [];
@obx.ref private dragment: any = null;
@obx.ref private x = 0;
@obx.ref private y = 0;
componentWillMount() {
this.dispose = [
dragon.onDragstart(e => {
this.dragment = e.dragTarget;
this.x = e.clientX;
this.y = e.clientY;
}),
dragon.onDrag(e => {
this.x = e.clientX;
this.y = e.clientY;
}),
dragon.onDragend(() => {
this.dragment = null;
this.x = 0;
this.y = 0;
}),
];
}
shouldComponentUpdate() {
return false;
}
componentWillUnmount() {
if (this.dispose) {
this.dispose.forEach(off => off());
}
}
renderGhostGroup() {
const dragment = this.dragment;
if (Array.isArray(dragment)) {
return dragment.map((node: any, index: number) => {
const ghost = (
<div className="my-ghost" key={`ghost-${index}`}>
<div className="my-ghost-title">{node.tagName}</div>
</div>
);
return ghost;
});
} else {
return (
<div className="my-ghost">
<div className="my-ghost-title">{dragment.tagName}</div>
</div>
);
}
}
render() {
if (!this.dragment) {
return null;
}
// let x = this.x;
// let y = this.y;
// todo: 考虑多个图标、title、不同 sensor 区域的形态
if (dragon.activeSensor && dragon.activeSensor.id === OutlineBoardID) {
// const nodeId = (this.dragment as INode).id;
// const elt = document.querySelector(`[data-id="${nodeId}"`) as HTMLDivElement;
//
// if (elt) {
// // do something
// // const target = elt.cloneNode(true) as HTMLDivElement;
// console.log('>>> target', elt);
// elt.classList.remove('hidden');
// elt.classList.add('dragging');
// elt.style.transform = `translate(${this.x}px, ${this.y}px)`;
// }
//
// return null;
// x -= 30;
// y += 30;
}
return (
<div
className="my-ghost-group"
style={{
transform: `translate(${this.x}px, ${this.y}px)`,
}}
>
{this.renderGhostGroup()}
</div>
);
}
}

View File

@ -1,47 +0,0 @@
.my-canvas {
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
margin: 10px;
box-shadow: 0 2px 10px 0 rgba(31,56,88,.15);
}
html.my-show-topbar .my-canvas {
top: var(--topbar-height);
}
html.my-show-toolbar .my-canvas {
top: var(--toolbar-height);
}
html.my-show-topbar.my-show-toolbar .my-canvas {
top: calc(var(--topbar-height) + var(--topbar-height));
}
.my-screen {
top: 0;
bottom: 0;
width: 100%;
left: 0;
position: absolute;
overflow: hidden;
}
.my-doc-shell {
position: absolute;
top: 0;
bottom: 0;
left: 0;
width: 100%;
overflow: hidden;
.my-doc-frame {
border: none;
transform-origin: 0 0;
height: 100%;
width: 100%;
}
}
.my-drag-pane-mode .my-doc-shell {
pointer-events: none;
}

View File

@ -1,76 +0,0 @@
import { Component } from 'react';
import { observer } from '@ali/recore';
import { getCurrentDocument, screen, progressing } from '../../globals';
import { AutoFit } from '../../document/viewport';
import { AuxiliaryView } from '../auxiliary';
import { PreLoaderView } from '../widgets/pre-loader';
import DocumentContext from '../../document/document-context';
import FocusingArea from '../widgets/focusing-area';
import './canvas.less';
const Canvas = () => (
<FocusingArea
className="my-canvas"
id="canvas"
onEsc={() => {
const doc = getCurrentDocument();
if (doc) {
doc.selection.clear();
}
return false;
}}
>
<Screen />
</FocusingArea>
);
export default Canvas;
@observer
class Screen extends Component {
render() {
const doc = getCurrentDocument();
// TODO: thinkof multi documents
return (
<div ref={elmt => screen.mount(elmt)} className="my-screen">
{progressing.visible ? <PreLoaderView /> : null}
<AuxiliaryView />
{doc ? <DocumentView key={doc.id} doc={doc} /> : null}
</div>
);
}
}
@observer
class DocumentView extends Component<{ doc: DocumentContext }> {
componentWillUnmount() {
this.props.doc.sleep();
}
render() {
const { doc } = this.props;
const viewport = doc.viewport;
let shellStyle = {};
let frameStyle = {};
if (viewport.width !== AutoFit && viewport.height !== AutoFit) {
const shellWidth = viewport.width * viewport.scale;
const screenWidth = screen.width;
const shellLeft = shellWidth < screenWidth ? `calc((100% - ${shellWidth}px) / 2)` : 0;
shellStyle = {
width: shellWidth,
left: shellLeft,
};
frameStyle = {
transform: `scale(${viewport.scale})`,
height: viewport.height,
width: viewport.width,
};
}
return (
<div className="my-doc-shell" style={shellStyle}>
<iframe className="my-doc-frame" style={frameStyle} ref={frame => doc.mountRuntimeFrame(frame)} />
</div>
);
}
}

View File

@ -1,17 +1,149 @@
class Designer {
id: string = guid();
hotkey: Hotkey;
import Dragon, { isDragNodeObject, isDragNodeDataObject } from "./dragon";
import Project from './project';
import { ProjectSchema } from './schema';
import DocumentModel from './document/document-model';
import BuiltinSimulatorView from '../builtins/simulator/master';
import { Component } from 'react';
import { obx, computed } from '@recore/obx';
import ActiveTracker from './active-tracker';
import Location, { LocationData, isLocationChildrenDetail } from './location';
import Node, { insertChildren } from './document/node/node';
constructor(options: BuilderOptions): Builder;
export interface DesignerProps {
className?: string;
style?: object;
defaultSchema?: ProjectSchema;
hotkeys?: object;
simulatorProps?: object | ((document: DocumentModel) => object);
simulatorComponent?: Component<any>;
dragGhostComponent?: Component<any>;
suspensed?: boolean;
onMount?: (designer: Designer) => void;
onDragstart?: (designer: Designer) => void;
onDrag?: (designer: Designer) => void;
onDragend?: (designer: Designer) => void;
// TODO: ...add other events support
[key: string]: any;
}
getValue(): ProjectSchema;
setValue(schema: ProjectSchema): void;
project: Project;
dragboost(locateEvent: LocateEvent): void;
addDropSensor(dropSensor: DropSensor): void;
export default class Designer {
// readonly hotkey: Hotkey;
readonly dragon = new Dragon(this);
readonly activeTracker = new ActiveTracker();
readonly project: Project;
constructor(props: DesignerProps) {
this.project = new Project(this, props.defaultSchema);
private _suspensed: boolean = false;
this.dragon.onDragstart(({ dragObject }) => {
if (isDragNodeObject(dragObject) && dragObject.nodes.length === 1) {
// ensure current selecting
dragObject.nodes[0].select();
}
});
this.dragon.onDragend(({ dragObject, copy }) => {
const loc = this._dropLocation;
if (loc) {
if (isLocationChildrenDetail(loc.detail)) {
let nodes: Node[] | undefined;
if (isDragNodeObject(dragObject)) {
nodes = insertChildren(loc.target, dragObject.nodes, loc.detail.index, copy);
} else if (isDragNodeDataObject(dragObject)) {
// process nodeData
const nodeData = Array.isArray(dragObject.data) ? dragObject.data : [dragObject.data];
nodes = insertChildren(loc.target, nodeData, loc.detail.index);
}
if (nodes) {
loc.document.selection.selectAll(nodes.map(o => o.id));
setTimeout(() => this.activeTracker.track(nodes![0]), 10);
}
}
}
this.clearLocation();
// this.enableEdging();
});
this.activeTracker.onChange(({ node, detail }) => {
node.document.simulator?.scrollToNode(node, detail);
});
this.setProps(props);
}
private _dropLocation?: Location;
/**
*
*/
createLocation(locationData: LocationData): Location {
const loc = new Location(locationData);
this._dropLocation = loc;
loc.document.internalSetDropLocation(loc);
this.activeTracker.track({ node: loc.target, detail: loc.detail });
return loc;
}
/**
*
*/
private clearLocation() {
if (this._dropLocation) {
this._dropLocation.document.internalSetDropLocation(null);
}
this._dropLocation = undefined;
}
private props?: DesignerProps;
setProps(props: DesignerProps) {
if (this.props) {
// check hotkeys
// TODO:
// check simulatorConfig
if (props.simulatorComponent !== this.props.simulatorComponent) {
this._simulatorComponent = props.simulatorComponent;
}
if (props.simulatorProps !== this.props.simulatorProps) {
this._simulatorProps = props.simulatorProps;
}
if (props.suspensed !== this.props.suspensed && props.suspensed != null) {
this.suspensed = props.suspensed;
}
} else {
// init hotkeys
// todo:
// init simulatorConfig
if (props.simulatorComponent) {
this._simulatorComponent = props.simulatorComponent;
}
if (props.simulatorProps) {
this._simulatorProps = props.simulatorProps;
}
// init suspensed
if (props.suspensed != null) {
this.suspensed = props.suspensed;
}
}
this.props = props;
}
get(key: string): any {
return this.props ? this.props[key] : null;
}
@obx.ref private _simulatorComponent?: Component<any>;
@obx.ref private _simulatorProps?: object;
@computed get simulatorConfig(): {
Component: Component<any>;
props: object;
} {
const config: any = {
Component: this._simulatorComponent || BuiltinSimulatorView,
props: this._simulatorProps || {},
};
return config;
}
@obx.ref private _suspensed: boolean = false;
get suspensed(): boolean {
return this._suspensed;
@ -25,9 +157,15 @@ class Designer {
}
}
// 事件 & 消息
onActiveChange(): () => void;
onDragstart(): void;
onDragend(): void;
//....
get schema(): ProjectSchema {
return this.project.schema;
}
set schema(schema: ProjectSchema) {
// todo:
}
purge() {
}
}

View File

@ -1,300 +0,0 @@
import Project from '../project';
import { RootSchema, NodeData, isDOMText, isJSExpression, NodeSchema } from '../schema';
import Node, { isNodeParent, insertChildren, insertChild, NodeParent } from './node/node';
import { Selection } from './selection';
import RootNode from './node/root-node';
import { SimulatorInterface } from '../simulator-interface';
import { computed } from '@recore/obx';
export default class DocumentContext {
/**
* Page/Component/Block
*/
readonly rootNode: RootNode;
/**
*
*/
readonly id: string;
/**
*
*/
readonly selection: Selection = new Selection(this);
/**
*
*/
// TODO
// readonly history: History = new History(this);
private nodesMap = new Map<string, Node>();
private nodes = new Set<Node>();
private seqId = 0;
private _simulator?: SimulatorInterface;
/**
*
*/
get simulator(): SimulatorInterface | null {
return this._simulator || null;
}
get fileName() {
return this.rootNode.extras.get('fileName')?.value as string;
}
set fileName(fileName: string) {
this.rootNode.extras.get('fileName', true).value = fileName;
}
constructor(readonly project: Project, schema: RootSchema) {
this.rootNode = new RootNode(this, schema);
this.id = this.rootNode.id;
}
/**
* id
*/
nextId() {
return (++this.seqId).toString(36).toLocaleLowerCase();
}
/**
* id
*/
getNode(id: string): Node | null {
return this.nodesMap.get(id) || null;
}
/**
*
*/
hasNode(id: string): boolean {
const node = this.getNode(id);
return node ? !node.isPurged : false;
}
/**
* schema
*/
createNode(data: NodeData): Node {
let schema: any;
if (isDOMText(data) || isJSExpression(data)) {
schema = {
componentName: '#frag',
children: data,
};
} else {
schema = data;
}
const node = new Node(this, schema);
this.nodesMap.set(node.id, node);
this.nodes.add(node);
return node;
}
/**
*
*/
insertNode(parent: NodeParent, thing: Node | NodeData, at?: number | null, copy?: boolean): Node {
return insertChild(parent, thing, at, copy);
}
/**
*
*/
insertNodes(parent: NodeParent, thing: Node[] | NodeData[], at?: number | null, copy?: boolean) {
return insertChildren(parent, thing, at, copy);
}
/**
*
*/
removeNode(idOrNode: string | Node) {
let id: string;
let node: Node | null;
if (typeof idOrNode === 'string') {
id = idOrNode;
node = this.getNode(id);
} else {
node = idOrNode;
id = node.id;
}
if (!node) {
return;
}
this.internalRemoveAndPurgeNode(node);
}
/**
*
*/
internalRemoveAndPurgeNode(node: Node) {
if (!this.nodes.has(node)) {
return;
}
this.nodesMap.delete(node.id);
this.nodes.delete(node);
node.remove();
}
/**
*
*/
wrapWith(schema: NodeSchema): Node | null {
const nodes = this.selection.getTopNodes();
if (nodes.length < 1) {
return null;
}
const wrapper = this.createNode(schema);
if (isNodeParent(wrapper)) {
const first = nodes[0];
// TODO: check nesting rules x 2
insertChild(first.parent!, wrapper, first.index);
insertChildren(wrapper, nodes);
this.selection.select(wrapper.id);
return wrapper;
}
this.removeNode(wrapper);
return null;
}
/**
* schema
*/
get schema(): NodeSchema {
return this.rootNode.schema;
}
/**
*
*/
getNodeSchema(id: string): NodeData | null {
const node = this.getNode(id);
if (node) {
return node.schema;
}
return null;
}
/**
*
*/
isModified() {
// return !this.history.isSavePoint();
}
/**
*
*/
@computed get simulatorProps(): object {
let simulatorProps = this.project.simulatorProps;
if (typeof simulatorProps === 'function') {
simulatorProps = simulatorProps(this);
}
return {
...simulatorProps,
documentContext: this,
onMount: this.mountSimulator.bind(this),
};
}
private mountSimulator(simulator: SimulatorInterface) {
this._simulator = simulator;
// TODO: emit simulator mounted
}
/**
* simulator
*/
getViewInstance(node: Node): ViewInstance[] | null {
if (this.simulator) {
this.simulator.getViewInstance(node.id);
}
return null;
}
/**
* DOM simulator
*/
getNodeFromElement(target: Element | null): Node | null {
if (!this.simulator || !target) {
return null;
}
const id = this.simulator.getClosestNodeId(target);
if (!id) {
return null;
}
return this.getNode(id) as Node;
}
/**
*
* DOM simulator
*/
getDOMNodes(viewInstance: ViewInstance): Array<Element | Text> | null {
if (!this.simulator) {
return null;
}
if (isElement(viewInstance)) {
return [viewInstance];
}
return this.simulator.findDOMNodes(viewInstance);
}
getComponent(componentName: string): any {
return this.simulator!.getCurrentComponent(componentName);
}
private _opened: boolean = true;
private _suspensed: boolean = false;
/**
* 
*/
get suspensed(): boolean {
return this._suspensed;
}
/**
* suspensed 
*/
get actived(): boolean {
return !this._suspensed;
}
/**
*
* tab
*/
set suspensed(flag: boolean) {
if (!this._opened && !flag) {
return;
}
this._suspensed = flag;
}
/**
*
*/
open(): void {
this._opened = true;
}
/**
* sleep
*/
close(): void {
this.suspensed = true;
this._opened = false;
}
/**
*
*/
remove() {
}
}

View File

@ -0,0 +1,27 @@
import { Component, createContext } from 'react';
import DocumentModel from './document-model';
export const DocumentContext = createContext<DocumentModel>(null as any);
export default class DocumentView extends Component<{ documentModel: DocumentModel }> {
render() {
const { documentModel } = this.props;
return (
<div className="lc-document">
<DocumentContext.Provider value={documentModel}>
{/* 这一层将来做缩放用途 */}
<div className="lc-simulator-shell">
</div>
<DocumentInfoView />
</DocumentContext.Provider>
</div>
)
}
}
class DocumentInfoView extends Component {
render() {
return null;
}
}

View File

@ -1,123 +0,0 @@
import ComponentNode, { NodeParent } from './node/node';
import DocumentContext from './document-context';
export interface LocationData {
target: NodeParent; // shadowNode | ConditionFlow | ElementNode | RootNode
detail: LocationDetail;
}
export enum LocationDetailType {
Children = 'Children',
Prop = 'Prop',
}
export interface LocationChildrenDetail {
type: LocationDetailType.Children;
index: number;
near?: {
node: ComponentNode;
pos: 'before' | 'after';
rect?: Rect;
align?: 'V' | 'H';
};
}
export interface LocationPropDetail {
// cover 形态,高亮 domNode如果 domNode 为空,取 container 的值
type: LocationDetailType.Prop;
name: string;
domNode?: HTMLElement;
}
export type LocationDetail = LocationChildrenDetail | LocationPropDetail | { type: string; [key: string]: any };
export interface Point {
clientX: number;
clientY: number;
}
export type Rects = Array<ClientRect | DOMRect> & {
elements: Array<Element | Text>;
};
export type Rect = (ClientRect | DOMRect) & {
elements: Array<Element | Text>;
computed?: boolean;
};
export function isLocationData(obj: any): obj is LocationData {
return obj && obj.target && obj.detail;
}
export function isLocationChildrenDetail(obj: any): obj is LocationChildrenDetail {
return obj && obj.type === LocationDetailType.Children;
}
export function isRowContainer(container: Element | Text, win?: Window) {
if (isText(container)) {
return true;
}
const style = (win || getWindow(container)).getComputedStyle(container);
const display = style.getPropertyValue('display');
if (/flex$/.test(display)) {
const direction = style.getPropertyValue('flex-direction') || 'row';
if (direction === 'row' || direction === 'row-reverse') {
return true;
}
}
return false;
}
export function isChildInline(child: Element | Text, win?: Window) {
if (isText(child)) {
return true;
}
const style = (win || getWindow(child)).getComputedStyle(child);
return /^inline/.test(style.getPropertyValue('display'));
}
export function getRectTarget(rect: Rect | null) {
if (!rect || rect.computed) {
return null;
}
const els = rect.elements;
return els && els.length > 0 ? els[0]! : null;
}
export function isVerticalContainer(rect: Rect | null) {
const el = getRectTarget(rect);
if (!el) {
return false;
}
return isRowContainer(el);
}
export function isVertical(rect: Rect | null) {
const el = getRectTarget(rect);
if (!el) {
return false;
}
return isChildInline(el) || (el.parentElement ? isRowContainer(el.parentElement) : false);
}
function isText(elem: any): elem is Text {
return elem.nodeType === Node.TEXT_NODE;
}
function isDocument(elem: any): elem is Document {
return elem.nodeType === Node.DOCUMENT_NODE;
}
export function getWindow(elem: Element | Document): Window {
return (isDocument(elem) ? elem : elem.ownerDocument!).defaultView!;
}
export default class Location {
readonly target: NodeParent;
readonly detail: LocationDetail;
constructor(readonly document: DocumentContext, { target, detail }: LocationData) {
this.target = target;
this.detail = detail;
}
}

View File

@ -1,11 +1,11 @@
import Node, { comparePosition } from './node/node';
import { obx } from '@recore/obx';
import DocumentContext from './document-context';
import DocumentModel from './document-model';
export class Selection {
@obx.val private selected: string[] = [];
constructor(private doc: DocumentContext) {}
constructor(private doc: DocumentModel) {}
/**
*

View File

@ -1,11 +1,11 @@
import { EventEmitter } from 'events';
import { obx } from '@recore/obx';
import Location from './document/location';
import Project from './project';
import DocumentContext from './document/document-context';
import Location from './location';
import DocumentModel from './document/document-model';
import { NodeData } from './schema';
import { SimulatorInterface } from './simulator-interface';
import Node from './document/node/node';
import Designer from './designer';
export interface LocateEvent {
readonly type: 'LocateEvent';
@ -34,7 +34,7 @@ export interface LocateEvent {
/**
*
*/
document?: DocumentContext;
document?: DocumentModel;
/**
* canvasX,canvasY,
*/
@ -44,7 +44,7 @@ export interface LocateEvent {
/**
*
*/
export interface SensorInterface {
export interface ISensor {
/**
* false
*/
@ -76,7 +76,7 @@ export enum DragObjectType {
export interface DragNodeObject {
type: DragObjectType.Node;
node: Node | Node[];
nodes: Node[];
}
export interface DragNodeDataObject {
type: DragObjectType.NodeData;
@ -126,32 +126,25 @@ export function setShaken(e: any) {
e.shaken = true;
}
function getTopDocument(e: MouseEvent, local: Document) {
return e.view!.document === local ? null : document;
function isFromTopDocument(e: MouseEvent) {
return e.view!.document === document;
}
export default class Dragon {
private sensors: ISenseAble[] = [];
private sensors: ISensor[] = [];
/**
* current actived sensor
*/
private _activeSensor: ISenseAble | undefined;
get activeSensor(): ISenseAble | undefined {
private _activeSensor: ISensor | undefined;
get activeSensor(): ISensor | undefined {
return this._activeSensor;
}
@obx.ref dragging = false;
private emitter = new EventEmitter();
private get master(): MasterBoard | undefined {
const doc = getCurrentDocument();
if (!doc) {
return undefined;
}
return doc.masterBoard;
}
constructor(readonly project: Project) {}
constructor(readonly designer: Designer) {}
from(shell: Element, boost: (e: MouseEvent) => DragObject | null) {
const mousedown = (e: MouseEvent) => {
@ -174,22 +167,28 @@ export default class Dragon {
};
}
getMasterSensors(): SimulatorInterface[] {
return this.designer.project.documents.map(doc => (doc.actived && doc.simulator) || null).filter(Boolean);
}
get master(): DocumentModel {
}
/**
* dragTarget should be a INode | INode[] | NodeData | NodeData[]
*/
boost(dragObject: DragObject, boostEvent: MouseEvent) {
if (!this.master) {
return;
}
const master = this.master;
const doc = master.contentDocument;
const viewport = master.document.viewport;
const topDoc = getTopDocument(boostEvent, doc);
const newBie = dragObject.type !== DragTargetType.Nodes;
const doc = document;
const fromTop = isFromTopDocument(boostEvent);
let lastLocation: any = null;
let lastSensor: ISenseAble | undefined;
let lastSensor: ISensor | undefined;
this.dragging = false;
master.setNativeSelection(false);
const masterSensors = this.getMasterSensors();
masterSensors.forEach((sensor) => {
sensor.setNativeSelection(false);
});
//
const checkesc = (e: KeyboardEvent) => {
if (e.keyCode === 27) {
@ -269,19 +268,25 @@ export default class Dragon {
master.releaseCursor();
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);
if (topDoc) {
topDoc.removeEventListener('mousemove', move, true);
topDoc.removeEventListener('mouseup', over, true);
topDoc.removeEventListener('mousedown', over, true);
topDoc.removeEventListener('keydown', checkesc, false);
topDoc.removeEventListener('keydown', checkcopy as any, false);
topDoc.removeEventListener('keyup', checkcopy as any, false);
if (fromTop) {
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.ownerDocument;
if (odoc && odoc !== doc) {
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);
}
});
}
if (exception) {
throw exception;
@ -318,13 +323,13 @@ export default class Dragon {
if (!isDragNodeObject(dragObject)) {
return null;
}
return (Array.isArray(dragObject.node) ? dragObject.node[0] : dragObject.node)?.document.simulator || null;
return (Array.isArray(dragObject.nodes) ? dragObject.nodes[0] : dragObject.nodes)?.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: SensorInterface[] = simSensors.concat(this.sensors);
const sensors: ISensor[] = simSensors.concat(this.sensors);
const chooseSensor = (e: LocateEvent) => {
let sensor = sensors.find(s => s.sensorAvailable && s.isEnter(e));
if (!sensor) {
@ -390,7 +395,7 @@ export default class Dragon {
};
}
onDragend(func: (x: { dragTarget: DragObject; copy: boolean }, location: Location) => any) {
onDragend(func: (x: { dragObject: DragObject; copy: boolean }, location: Location) => any) {
this.emitter.on('dragend', func);
return () => {
this.emitter.removeListener('dragend', func);

View File

@ -1,17 +1,25 @@
import { obx } from '@recore/obx';
import { DocumentSchema, ProjectSchema } from './schema';
import { ProjectSchema } from './schema';
import { EventEmitter } from 'events';
import Designer from './designer';
import DocumentModel from './document/document-model';
export default class Project {
@obx documents: DocumentContext[];
displayMode: 'exclusive' | 'tabbed' | 'split'; // P2
private emitter = new EventEmitter();
@obx.val readonly documents: DocumentModel[] = [];
private data: ProjectSchema = {};
@obx.ref displayMode: 'exclusive' | 'tabbed' | 'split' = 'exclusive';
// 考虑项目级别 History
constructor(schema: ProjectSchema) {
this.data = { ...schema };
constructor(readonly designer: Designer, schema?: ProjectSchema) {
this.data = {
version: '1.0.0',
componentsMap: [],
componentsTree: [],
...schema
};
}
getDocument(fileName: string): DocumentContext {}
@ -23,16 +31,19 @@ export default class Project {
/**
* schema
*/
getSchema(): ProjectSchema {
get schema(): ProjectSchema {
return {
...this.data,
componentsTree: this.documents.map(doc => doc.getSchema()),
};
}
/**
* schema
*/
setSchema(schema: ProjectSchema): void {}
set schema(schema: ProjectSchema) {
}
/**
*

View File

@ -1,9 +1,10 @@
import { NpmInfo } from './schema';
import { ComponentClass as ReactComponentClass, Component } from 'react';
import { LocateEvent, SensorInterface } from './dragon';
import { Point } from './document/location';
import { LocateEvent, ISensor } from './dragon';
import { Point } from './location';
import Node from './document/node/node';
export interface SimulatorInterface<P = object> extends SensorInterface {
export interface SimulatorInterface<P = object> extends ISensor {
/**
*
*/