mirror of
https://github.com/alibaba/lowcode-engine.git
synced 2025-12-15 05:36:39 +00:00
fix designer event pipe
This commit is contained in:
parent
b59934eb2f
commit
fe12ab02f9
@ -31,22 +31,12 @@ export default class App extends ViewController {
|
|||||||
SettingsPane
|
SettingsPane
|
||||||
};
|
};
|
||||||
|
|
||||||
editor = {
|
editor = emitter;
|
||||||
on(type: string, fn: any) {
|
|
||||||
emitter.on(type, fn);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$didMount() {
|
$didMount() {
|
||||||
const designer = this.$refs.d.designer;
|
const designer = this.$refs.d.designer;
|
||||||
const pane = this.$refs.pane;
|
const pane = this.$refs.pane;
|
||||||
(window as any).LCDesigner = designer;
|
(window as any).LCDesigner = designer;
|
||||||
if (designer.project.activedDocument) {
|
|
||||||
emitter.emit('designer.actived-document-change', designer.project.activedDocument);
|
|
||||||
}
|
|
||||||
designer.project.onActivedDocumentChange((doc: any) => {
|
|
||||||
emitter.emit('designer.actived-document-change', doc);
|
|
||||||
});
|
|
||||||
(this.editor as any).designer = designer;
|
(this.editor as any).designer = designer;
|
||||||
designer.dragon.from(pane, () => {
|
designer.dragon.from(pane, () => {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
<div className="editor">
|
<div className="editor">
|
||||||
<DesignView
|
<DesignView
|
||||||
|
eventPipe={editor}
|
||||||
defaultSchema={{
|
defaultSchema={{
|
||||||
componentsTree: [
|
componentsTree: [
|
||||||
{
|
{
|
||||||
@ -643,7 +644,7 @@
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
}}
|
}}
|
||||||
componentDescriptionSpecs={[
|
componentsDescription={[
|
||||||
{
|
{
|
||||||
description: 'Button',
|
description: 'Button',
|
||||||
npm: { package: '@ali/deep', subName: 'Button', destructuring: false, exportName: 'Next', version: '1.17.2' },
|
npm: { package: '@ali/deep', subName: 'Button', destructuring: false, exportName: 'Next', version: '1.17.2' },
|
||||||
|
|||||||
@ -14,6 +14,7 @@ import { ComponentDescription, ComponentType } from './component-type';
|
|||||||
import Scroller, { IScrollable } from './helper/scroller';
|
import Scroller, { IScrollable } from './helper/scroller';
|
||||||
import { INodeSelector } from './simulator';
|
import { INodeSelector } from './simulator';
|
||||||
import OffsetObserver, { createOffsetObserver } from './helper/offset-observer';
|
import OffsetObserver, { createOffsetObserver } from './helper/offset-observer';
|
||||||
|
import { EventEmitter } from 'events';
|
||||||
|
|
||||||
export interface DesignerProps {
|
export interface DesignerProps {
|
||||||
className?: string;
|
className?: string;
|
||||||
@ -24,12 +25,12 @@ export interface DesignerProps {
|
|||||||
simulatorComponent?: ReactComponentType<any>;
|
simulatorComponent?: ReactComponentType<any>;
|
||||||
dragGhostComponent?: ReactComponentType<any>;
|
dragGhostComponent?: ReactComponentType<any>;
|
||||||
suspensed?: boolean;
|
suspensed?: boolean;
|
||||||
componentDescriptionSpecs?: ComponentDescription[];
|
componentsDescription?: ComponentDescription[];
|
||||||
|
eventPipe?: EventEmitter;
|
||||||
onMount?: (designer: Designer) => void;
|
onMount?: (designer: Designer) => void;
|
||||||
onDragstart?: (e: LocateEvent) => void;
|
onDragstart?: (e: LocateEvent) => void;
|
||||||
onDrag?: (e: LocateEvent) => void;
|
onDrag?: (e: LocateEvent) => void;
|
||||||
onDragend?: (e: { dragObject: DragObject; copy: boolean }, loc?: Location) => void;
|
onDragend?: (e: { dragObject: DragObject; copy: boolean }, loc?: Location) => void;
|
||||||
// TODO: ...add other events support
|
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,7 +41,21 @@ export default class Designer {
|
|||||||
readonly hovering = new Hovering();
|
readonly hovering = new Hovering();
|
||||||
readonly project: Project;
|
readonly project: Project;
|
||||||
|
|
||||||
|
get currentDocument() {
|
||||||
|
return this.project.currentDocument;
|
||||||
|
}
|
||||||
|
|
||||||
|
get currentHistory() {
|
||||||
|
return this.currentDocument?.history;
|
||||||
|
}
|
||||||
|
|
||||||
|
get currentSelection() {
|
||||||
|
return this.currentDocument?.selection;
|
||||||
|
}
|
||||||
|
|
||||||
constructor(props: DesignerProps) {
|
constructor(props: DesignerProps) {
|
||||||
|
this.setProps(props);
|
||||||
|
|
||||||
this.project = new Project(this, props.defaultSchema);
|
this.project = new Project(this, props.defaultSchema);
|
||||||
|
|
||||||
this.dragon.onDragstart(e => {
|
this.dragon.onDragstart(e => {
|
||||||
@ -53,12 +68,14 @@ export default class Designer {
|
|||||||
if (this.props?.onDragstart) {
|
if (this.props?.onDragstart) {
|
||||||
this.props.onDragstart(e);
|
this.props.onDragstart(e);
|
||||||
}
|
}
|
||||||
|
this.postEvent('dragstart', e);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.dragon.onDrag(e => {
|
this.dragon.onDrag(e => {
|
||||||
if (this.props?.onDrag) {
|
if (this.props?.onDrag) {
|
||||||
this.props.onDrag(e);
|
this.props.onDrag(e);
|
||||||
}
|
}
|
||||||
|
this.postEvent('drag', e);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.dragon.onDragend(e => {
|
this.dragon.onDragend(e => {
|
||||||
@ -84,6 +101,7 @@ export default class Designer {
|
|||||||
if (this.props?.onDragend) {
|
if (this.props?.onDragend) {
|
||||||
this.props.onDragend(e, loc);
|
this.props.onDragend(e, loc);
|
||||||
}
|
}
|
||||||
|
this.postEvent('dragend', e, loc);
|
||||||
this.hovering.enable = true;
|
this.hovering.enable = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -91,7 +109,30 @@ export default class Designer {
|
|||||||
node.document.simulator?.scrollToNode(node, detail);
|
node.document.simulator?.scrollToNode(node, detail);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.setProps(props);
|
let selectionDispose: undefined | (() => void);
|
||||||
|
const setupSelection = () => {
|
||||||
|
if (selectionDispose) {
|
||||||
|
selectionDispose();
|
||||||
|
selectionDispose = undefined;
|
||||||
|
}
|
||||||
|
if (this.currentSelection) {
|
||||||
|
const currentSelection = this.currentSelection;
|
||||||
|
selectionDispose = currentSelection.onSelectionChange(() => {
|
||||||
|
this.postEvent('current-selection-change', currentSelection);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.project.onCurrentDocumentChange(() => {
|
||||||
|
this.postEvent('current-document-change', this.currentDocument);
|
||||||
|
this.postEvent('current-selection-change', this.currentSelection);
|
||||||
|
this.postEvent('current-history-change', this.currentHistory);
|
||||||
|
setupSelection();
|
||||||
|
});
|
||||||
|
setupSelection();
|
||||||
|
}
|
||||||
|
|
||||||
|
postEvent(event: string, ...args: any[]) {
|
||||||
|
this.props?.eventPipe?.emit(`designer.${event}`, ...args);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _dropLocation?: Location;
|
private _dropLocation?: Location;
|
||||||
@ -128,7 +169,7 @@ export default class Designer {
|
|||||||
* 获得合适的插入位置
|
* 获得合适的插入位置
|
||||||
*/
|
*/
|
||||||
getSuitableInsertion() {
|
getSuitableInsertion() {
|
||||||
const activedDoc = this.project.activedDocument;
|
const activedDoc = this.project.currentDocument;
|
||||||
if (!activedDoc) {
|
if (!activedDoc) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -165,8 +206,8 @@ export default class Designer {
|
|||||||
if (props.suspensed !== this.props.suspensed && props.suspensed != null) {
|
if (props.suspensed !== this.props.suspensed && props.suspensed != null) {
|
||||||
this.suspensed = props.suspensed;
|
this.suspensed = props.suspensed;
|
||||||
}
|
}
|
||||||
if (props.componentDescriptionSpecs !== this.props.componentDescriptionSpecs && props.componentDescriptionSpecs != null) {
|
if (props.componentsDescription !== this.props.componentsDescription && props.componentsDescription != null) {
|
||||||
this.buildComponentTypesMap(props.componentDescriptionSpecs);
|
this.buildComponentTypesMap(props.componentsDescription);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// init hotkeys
|
// init hotkeys
|
||||||
@ -182,8 +223,8 @@ export default class Designer {
|
|||||||
if (props.suspensed != null) {
|
if (props.suspensed != null) {
|
||||||
this.suspensed = props.suspensed;
|
this.suspensed = props.suspensed;
|
||||||
}
|
}
|
||||||
if (props.componentDescriptionSpecs != null) {
|
if (props.componentsDescription != null) {
|
||||||
this.buildComponentTypesMap(props.componentDescriptionSpecs);
|
this.buildComponentTypesMap(props.componentsDescription);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.props = props;
|
this.props = props;
|
||||||
|
|||||||
@ -41,11 +41,11 @@ export default class DocumentModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get fileName(): string {
|
get fileName(): string {
|
||||||
return (this.rootNode.extras.get('fileName')?.value as string) || this.id;
|
return (this.rootNode.getExtraProp('fileName')?.getAsString()) || this.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
set fileName(fileName: string) {
|
set fileName(fileName: string) {
|
||||||
this.rootNode.extras.get('fileName', true).value = fileName;
|
this.rootNode.getExtraProp('fileName', true)?.setValue(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(readonly project: Project, schema: RootSchema) {
|
constructor(readonly project: Project, schema: RootSchema) {
|
||||||
@ -290,7 +290,7 @@ export default class DocumentModel {
|
|||||||
return this.designer.getComponentType(componentName);
|
return this.designer.getComponentType(componentName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@obx.ref private _opened: boolean = true;
|
@obx.ref private _opened: boolean = false;
|
||||||
@obx.ref private _suspensed: boolean = false;
|
@obx.ref private _suspensed: boolean = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -341,7 +341,11 @@ export default class DocumentModel {
|
|||||||
* 打开,已载入,默认建立时就打开状态,除非手动关闭
|
* 打开,已载入,默认建立时就打开状态,除非手动关闭
|
||||||
*/
|
*/
|
||||||
open(): void {
|
open(): void {
|
||||||
|
const originState = this._opened;
|
||||||
this._opened = true;
|
this._opened = true;
|
||||||
|
if (originState === false) {
|
||||||
|
this.designer.postEvent('document-open', this);
|
||||||
|
}
|
||||||
if (this._suspensed) {
|
if (this._suspensed) {
|
||||||
this.setSuspense(false);
|
this.setSuspense(false);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { obx, computed, untracked } from '@recore/obx';
|
import { obx, computed, untracked } from '@recore/obx';
|
||||||
import { NodeSchema, NodeData, PropsMap, PropsList } from '../../schema';
|
import { NodeSchema, NodeData, PropsMap, PropsList } from '../../schema';
|
||||||
import Props from './props/props';
|
import Props, { EXTRA_KEY_PREFIX } from './props/props';
|
||||||
import DocumentModel from '../document-model';
|
import DocumentModel from '../document-model';
|
||||||
import NodeChildren from './node-children';
|
import NodeChildren from './node-children';
|
||||||
import Prop from './props/prop';
|
import Prop from './props/prop';
|
||||||
@ -8,8 +8,6 @@ import NodeContent from './node-content';
|
|||||||
import { Component } from '../../simulator';
|
import { Component } from '../../simulator';
|
||||||
import { ComponentType } from '../../component-type';
|
import { ComponentType } from '../../component-type';
|
||||||
|
|
||||||
const DIRECTIVES = ['condition', 'conditionGroup', 'loop', 'loopArgs', 'title', 'ignore', 'hidden', 'locked'];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 基础节点
|
* 基础节点
|
||||||
*
|
*
|
||||||
@ -51,20 +49,11 @@ export default class Node {
|
|||||||
*/
|
*/
|
||||||
readonly componentName: string;
|
readonly componentName: string;
|
||||||
protected _props?: Props;
|
protected _props?: Props;
|
||||||
protected _directives?: Props;
|
|
||||||
protected _extras?: Props;
|
|
||||||
protected _children: NodeChildren | NodeContent;
|
protected _children: NodeChildren | NodeContent;
|
||||||
@obx.ref private _parent: NodeParent | null = null;
|
@obx.ref private _parent: NodeParent | null = null;
|
||||||
@obx.ref private _zLevel = 0;
|
|
||||||
get props(): Props | undefined {
|
get props(): Props | undefined {
|
||||||
return this._props;
|
return this._props;
|
||||||
}
|
}
|
||||||
get directives(): Props | undefined {
|
|
||||||
return this._directives;
|
|
||||||
}
|
|
||||||
get extras(): Props | undefined {
|
|
||||||
return this._extras;
|
|
||||||
}
|
|
||||||
/**
|
/**
|
||||||
* 父级节点
|
* 父级节点
|
||||||
*/
|
*/
|
||||||
@ -88,7 +77,7 @@ export default class Node {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@computed get title(): string {
|
@computed get title(): string {
|
||||||
let t = this.getDirective('x-title');
|
let t = this.getExtraProp('title');
|
||||||
if (!t && this.componentType.descriptor) {
|
if (!t && this.componentType.descriptor) {
|
||||||
t = this.getProp(this.componentType.descriptor, false);
|
t = this.getProp(this.componentType.descriptor, false);
|
||||||
}
|
}
|
||||||
@ -111,15 +100,7 @@ export default class Node {
|
|||||||
this.componentName = componentName;
|
this.componentName = componentName;
|
||||||
this._slotFor = slotFor;
|
this._slotFor = slotFor;
|
||||||
if (isNodeParent(this)) {
|
if (isNodeParent(this)) {
|
||||||
this._props = new Props(this, props);
|
this._props = new Props(this, props, extras);
|
||||||
this._directives = new Props(this, {});
|
|
||||||
Object.keys(extras).forEach(key => {
|
|
||||||
if (DIRECTIVES.indexOf(key) > -1) {
|
|
||||||
this._directives!.add((extras as any)[key], key);
|
|
||||||
delete (extras as any)[key];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this._extras = new Props(this, extras as any);
|
|
||||||
this._children = new NodeChildren(this as NodeParent, children || []);
|
this._children = new NodeChildren(this as NodeParent, children || []);
|
||||||
} else {
|
} else {
|
||||||
this._children = new NodeContent(children);
|
this._children = new NodeContent(children);
|
||||||
@ -197,22 +178,15 @@ export default class Node {
|
|||||||
/**
|
/**
|
||||||
* 节点组件描述
|
* 节点组件描述
|
||||||
*/
|
*/
|
||||||
@obx.ref get componentType(): ComponentType {
|
@computed get componentType(): ComponentType {
|
||||||
return this.document.getComponentType(this.componentName, this.component);
|
return this.document.getComponentType(this.componentName, this.component);
|
||||||
}
|
}
|
||||||
|
|
||||||
@obx.ref get propsData(): PropsMap | PropsList | null {
|
@computed get propsData(): PropsMap | PropsList | null {
|
||||||
if (!this.isNodeParent || this.componentName === 'Fragment') {
|
if (!this.isNodeParent || this.componentName === 'Fragment') {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return this.props?.value || null;
|
return this.props?.export(true).props || null;
|
||||||
}
|
|
||||||
|
|
||||||
get directivesData(): PropsMap | null {
|
|
||||||
if (!this.isNodeParent) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return this.directives?.value as PropsMap || null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private _conditionGroup: string | null = null;
|
private _conditionGroup: string | null = null;
|
||||||
@ -261,6 +235,10 @@ export default class Node {
|
|||||||
return this.props?.query(path, useStash as any) || null;
|
return this.props?.query(path, useStash as any) || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getExtraProp(key: string, useStash: boolean = true): Prop | null {
|
||||||
|
return this.props?.get(EXTRA_KEY_PREFIX + key, useStash) || null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取单个属性值
|
* 获取单个属性值
|
||||||
*/
|
*/
|
||||||
@ -289,10 +267,6 @@ export default class Node {
|
|||||||
this.props?.import(props);
|
this.props?.import(props);
|
||||||
}
|
}
|
||||||
|
|
||||||
getDirective(name: string, useStash: boolean = true): Prop | null {
|
|
||||||
return this.directives?.get(name, useStash as any) || null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取节点在父容器中的索引
|
* 获取节点在父容器中的索引
|
||||||
*/
|
*/
|
||||||
@ -346,16 +320,7 @@ export default class Node {
|
|||||||
const { componentName, id, children, props, ...extras } = data;
|
const { componentName, id, children, props, ...extras } = data;
|
||||||
|
|
||||||
if (isNodeParent(this)) {
|
if (isNodeParent(this)) {
|
||||||
const directives: any = {};
|
this._props!.import(props, extras);
|
||||||
Object.keys(extras).forEach(key => {
|
|
||||||
if (DIRECTIVES.indexOf(key) > -1) {
|
|
||||||
directives[key] = (extras as any)[key];
|
|
||||||
delete (extras as any)[key];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this._props!.import(data.props);
|
|
||||||
this._directives!.import(directives);
|
|
||||||
this._extras!.import(extras as any);
|
|
||||||
this._children.import(children, checkId);
|
this._children.import(children, checkId);
|
||||||
} else {
|
} else {
|
||||||
this._children.import(children);
|
this._children.import(children);
|
||||||
@ -367,12 +332,11 @@ export default class Node {
|
|||||||
* @param serialize 序列化,加 id 标识符,用于储存为操作记录
|
* @param serialize 序列化,加 id 标识符,用于储存为操作记录
|
||||||
*/
|
*/
|
||||||
export(serialize = false): NodeSchema {
|
export(serialize = false): NodeSchema {
|
||||||
// TODO...
|
const { props, extras } = this.props?.export(serialize) || {};
|
||||||
const schema: any = {
|
const schema: any = {
|
||||||
componentName: this.componentName,
|
componentName: this.componentName,
|
||||||
...this.extras?.export(serialize),
|
props,
|
||||||
props: this.props?.export(serialize) || {},
|
...extras,
|
||||||
...this.directives?.export(serialize),
|
|
||||||
};
|
};
|
||||||
if (serialize) {
|
if (serialize) {
|
||||||
schema.id = this.id;
|
schema.id = this.id;
|
||||||
@ -437,8 +401,6 @@ export default class Node {
|
|||||||
this.children.purge();
|
this.children.purge();
|
||||||
}
|
}
|
||||||
this.props?.purge();
|
this.props?.purge();
|
||||||
this.directives?.purge();
|
|
||||||
this.extras?.purge();
|
|
||||||
this.document.internalRemoveAndPurgeNode(this);
|
this.document.internalRemoveAndPurgeNode(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -446,8 +408,6 @@ export default class Node {
|
|||||||
export interface NodeParent extends Node {
|
export interface NodeParent extends Node {
|
||||||
readonly children: NodeChildren;
|
readonly children: NodeChildren;
|
||||||
readonly props: Props;
|
readonly props: Props;
|
||||||
readonly directives: Props;
|
|
||||||
readonly extras: Props;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isNode(node: any): node is Node {
|
export function isNode(node: any): node is Node {
|
||||||
|
|||||||
@ -140,6 +140,10 @@ export default class Prop implements IPropParent {
|
|||||||
this.dispose();
|
this.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getValue(serialize = true) {
|
||||||
|
// todo:
|
||||||
|
}
|
||||||
|
|
||||||
private dispose() {
|
private dispose() {
|
||||||
const items = untracked(() => this._items);
|
const items = untracked(() => this._items);
|
||||||
if (items) {
|
if (items) {
|
||||||
|
|||||||
@ -1,10 +1,12 @@
|
|||||||
import { computed, obx } from '@recore/obx';
|
import { computed, obx } from '@recore/obx';
|
||||||
import { uniqueId } from '../../../../../../utils/unique-id';
|
import { uniqueId } from '../../../../../../utils/unique-id';
|
||||||
import { CompositeValue, PropsList, PropsMap } from '../../../schema';
|
import { CompositeValue, PropsList, PropsMap, CompositeObject } from '../../../schema';
|
||||||
import PropStash from './prop-stash';
|
import PropStash from './prop-stash';
|
||||||
import Prop, { IPropParent, UNSET } from './prop';
|
import Prop, { IPropParent, UNSET } from './prop';
|
||||||
import { NodeParent } from '../node';
|
import { NodeParent } from '../node';
|
||||||
|
|
||||||
|
export const EXTRA_KEY_PREFIX = '__';
|
||||||
|
|
||||||
export default class Props implements IPropParent {
|
export default class Props implements IPropParent {
|
||||||
readonly id = uniqueId('props');
|
readonly id = uniqueId('props');
|
||||||
@obx.val private items: Prop[] = [];
|
@obx.val private items: Prop[] = [];
|
||||||
@ -36,22 +38,23 @@ export default class Props implements IPropParent {
|
|||||||
return this.items.length;
|
return this.items.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
@computed get value(): PropsMap | PropsList | null {
|
|
||||||
return this.export(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
@obx type: 'map' | 'list' = 'map';
|
@obx type: 'map' | 'list' = 'map';
|
||||||
|
|
||||||
constructor(readonly owner: NodeParent, value?: PropsMap | PropsList | null) {
|
constructor(readonly owner: NodeParent, value?: PropsMap | PropsList | null, extras?: object) {
|
||||||
if (Array.isArray(value)) {
|
if (Array.isArray(value)) {
|
||||||
this.type = 'list';
|
this.type = 'list';
|
||||||
this.items = value.map(item => new Prop(this, item.value, item.name, item.spread));
|
this.items = value.map(item => new Prop(this, item.value, item.name, item.spread));
|
||||||
} else if (value != null) {
|
} else if (value != null) {
|
||||||
this.items = Object.keys(value).map(key => new Prop(this, value[key], key));
|
this.items = Object.keys(value).map(key => new Prop(this, value[key], key));
|
||||||
}
|
}
|
||||||
|
if (extras) {
|
||||||
|
Object.keys(extras).forEach(key => {
|
||||||
|
this.items.push(new Prop(this, (extras as any)[key], EXTRA_KEY_PREFIX + key));
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
import(value?: PropsMap | PropsList | null) {
|
import(value?: PropsMap | PropsList | null, extras?: object) {
|
||||||
this.stash.clear();
|
this.stash.clear();
|
||||||
const originItems = this.items;
|
const originItems = this.items;
|
||||||
if (Array.isArray(value)) {
|
if (Array.isArray(value)) {
|
||||||
@ -64,6 +67,11 @@ export default class Props implements IPropParent {
|
|||||||
this.type = 'map';
|
this.type = 'map';
|
||||||
this.items = [];
|
this.items = [];
|
||||||
}
|
}
|
||||||
|
if (extras) {
|
||||||
|
Object.keys(extras).forEach(key => {
|
||||||
|
this.items.push(new Prop(this, (extras as any)[key], EXTRA_KEY_PREFIX + key));
|
||||||
|
});
|
||||||
|
}
|
||||||
originItems.forEach(item => item.purge());
|
originItems.forEach(item => item.purge());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,27 +81,52 @@ export default class Props implements IPropParent {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export(serialize = false): PropsMap | PropsList | null {
|
export(serialize = false): { props?: PropsMap | PropsList; extras?: object} {
|
||||||
if (this.items.length < 1) {
|
if (this.items.length < 1) {
|
||||||
return null;
|
return {};
|
||||||
}
|
}
|
||||||
|
let props: any = {};
|
||||||
|
const extras: any = {};
|
||||||
if (this.type === 'list') {
|
if (this.type === 'list') {
|
||||||
return this.items.map(item => {
|
props = [];
|
||||||
const v = item.export(serialize);
|
this.items.forEach(item => {
|
||||||
return {
|
let value = item.export(serialize);
|
||||||
spread: item.spread,
|
if (value === UNSET) {
|
||||||
name: item.key as string,
|
value = null;
|
||||||
value: v === UNSET ? null : v,
|
}
|
||||||
};
|
let name = item.key as string;
|
||||||
|
if (name && typeof name === 'string' && name.startsWith(EXTRA_KEY_PREFIX)) {
|
||||||
|
name = name.substr(EXTRA_KEY_PREFIX.length);
|
||||||
|
extras[name] = value;
|
||||||
|
} else {
|
||||||
|
props.push({
|
||||||
|
spread: item.spread,
|
||||||
|
name,
|
||||||
|
value,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.items.forEach(item => {
|
||||||
|
let name = item.key as string;
|
||||||
|
if (name == null) {
|
||||||
|
// todo ...spread
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let value = item.export(serialize);
|
||||||
|
if (value === UNSET) {
|
||||||
|
value = null;
|
||||||
|
}
|
||||||
|
if (typeof name === 'string' && name.startsWith(EXTRA_KEY_PREFIX)) {
|
||||||
|
name = name.substr(EXTRA_KEY_PREFIX.length);
|
||||||
|
extras[name] = value;
|
||||||
|
} else {
|
||||||
|
props[name] = value;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
const maps: any = {};
|
|
||||||
this.items.forEach(prop => {
|
return { props, extras };
|
||||||
if (prop.key) {
|
|
||||||
maps[prop.key] = prop.export(serialize);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return maps;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -59,12 +59,6 @@ export default class RootNode extends Node implements NodeParent {
|
|||||||
get props(): Props {
|
get props(): Props {
|
||||||
return this._props as any;
|
return this._props as any;
|
||||||
}
|
}
|
||||||
get extras(): Props {
|
|
||||||
return this._extras as any;
|
|
||||||
}
|
|
||||||
get directives(): Props {
|
|
||||||
return this._directives as any;
|
|
||||||
}
|
|
||||||
internalSetParent(parent: null) {}
|
internalSetParent(parent: null) {}
|
||||||
|
|
||||||
constructor(readonly document: DocumentModel, rootSchema: RootSchema) {
|
constructor(readonly document: DocumentModel, rootSchema: RootSchema) {
|
||||||
|
|||||||
@ -4,57 +4,63 @@ import DocumentModel from './document-model';
|
|||||||
import { EventEmitter } from 'events';
|
import { EventEmitter } from 'events';
|
||||||
|
|
||||||
export class Selection {
|
export class Selection {
|
||||||
@obx.val private selected: string[] = [];
|
|
||||||
private emitter = new EventEmitter();
|
private emitter = new EventEmitter();
|
||||||
|
@obx.val private _selected: string[] = [];
|
||||||
|
/**
|
||||||
|
* 选中的节点 id
|
||||||
|
*/
|
||||||
|
get selected(): string[] {
|
||||||
|
return this._selected;
|
||||||
|
}
|
||||||
|
|
||||||
constructor(private doc: DocumentModel) {}
|
constructor(readonly doc: DocumentModel) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 选中
|
* 选中
|
||||||
*/
|
*/
|
||||||
select(id: string) {
|
select(id: string) {
|
||||||
if (this.selected.length === 1 && this.selected.indexOf(id) > -1) {
|
if (this._selected.length === 1 && this._selected.indexOf(id) > -1) {
|
||||||
// avoid cause reaction
|
// avoid cause reaction
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.selected = [id];
|
this._selected = [id];
|
||||||
this.emitter.emit('selectionchange');
|
this.emitter.emit('selectionchange', this._selected);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 批量选中
|
* 批量选中
|
||||||
*/
|
*/
|
||||||
selectAll(ids: string[]) {
|
selectAll(ids: string[]) {
|
||||||
this.selected = ids;
|
this._selected = ids;
|
||||||
this.emitter.emit('selectionchange');
|
this.emitter.emit('selectionchange', this._selected);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 清除选中
|
* 清除选中
|
||||||
*/
|
*/
|
||||||
clear() {
|
clear() {
|
||||||
if (this.selected.length < 1) {
|
if (this._selected.length < 1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.selected = [];
|
this._selected = [];
|
||||||
this.emitter.emit('selectionchange');
|
this.emitter.emit('selectionchange', this._selected);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 整理选中
|
* 整理选中
|
||||||
*/
|
*/
|
||||||
dispose() {
|
dispose() {
|
||||||
const l = this.selected.length;
|
const l = this._selected.length;
|
||||||
let i = l;
|
let i = l;
|
||||||
while (i-- > 0) {
|
while (i-- > 0) {
|
||||||
const id = this.selected[i];
|
const id = this._selected[i];
|
||||||
if (!this.doc.hasNode(id)) {
|
if (!this.doc.hasNode(id)) {
|
||||||
this.selected.splice(i, 1);
|
this._selected.splice(i, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (this.selected.length !== l) {
|
if (this._selected.length !== l) {
|
||||||
this.emitter.emit('selectionchange');
|
this.emitter.emit('selectionchange', this._selected);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,29 +68,29 @@ export class Selection {
|
|||||||
* 添加选中
|
* 添加选中
|
||||||
*/
|
*/
|
||||||
add(id: string) {
|
add(id: string) {
|
||||||
if (this.selected.indexOf(id) > -1) {
|
if (this._selected.indexOf(id) > -1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.selected.push(id);
|
this._selected.push(id);
|
||||||
this.emitter.emit('selectionchange');
|
this.emitter.emit('selectionchange', this._selected);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 是否选中
|
* 是否选中
|
||||||
*/
|
*/
|
||||||
has(id: string) {
|
has(id: string) {
|
||||||
return this.selected.indexOf(id) > -1;
|
return this._selected.indexOf(id) > -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 移除选中
|
* 移除选中
|
||||||
*/
|
*/
|
||||||
remove(id: string) {
|
remove(id: string) {
|
||||||
let i = this.selected.indexOf(id);
|
let i = this._selected.indexOf(id);
|
||||||
if (i > -1) {
|
if (i > -1) {
|
||||||
this.selected.splice(i, 1);
|
this._selected.splice(i, 1);
|
||||||
this.emitter.emit('selectionchange');
|
this.emitter.emit('selectionchange', this._selected);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,7 +98,7 @@ export class Selection {
|
|||||||
* 选区是否包含节点
|
* 选区是否包含节点
|
||||||
*/
|
*/
|
||||||
containsNode(node: Node) {
|
containsNode(node: Node) {
|
||||||
for (const id of this.selected) {
|
for (const id of this._selected) {
|
||||||
const parent = this.doc.getNode(id);
|
const parent = this.doc.getNode(id);
|
||||||
if (parent?.contains(node)) {
|
if (parent?.contains(node)) {
|
||||||
return true;
|
return true;
|
||||||
@ -106,7 +112,7 @@ export class Selection {
|
|||||||
*/
|
*/
|
||||||
getNodes() {
|
getNodes() {
|
||||||
const nodes = [];
|
const nodes = [];
|
||||||
for (const id of this.selected) {
|
for (const id of this._selected) {
|
||||||
const node = this.doc.getNode(id);
|
const node = this.doc.getNode(id);
|
||||||
if (node) {
|
if (node) {
|
||||||
nodes.push(node);
|
nodes.push(node);
|
||||||
@ -120,7 +126,7 @@ export class Selection {
|
|||||||
*/
|
*/
|
||||||
getTopNodes() {
|
getTopNodes() {
|
||||||
const nodes = [];
|
const nodes = [];
|
||||||
for (const id of this.selected) {
|
for (const id of this._selected) {
|
||||||
const node = this.doc.getNode(id);
|
const node = this.doc.getNode(id);
|
||||||
if (!node) {
|
if (!node) {
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
@ -27,7 +27,7 @@ export default class Project {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@computed get activedDocument() {
|
@computed get currentDocument() {
|
||||||
return this.documents.find(doc => doc.actived);
|
return this.documents.find(doc => doc.actived);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -111,7 +111,7 @@ export default class Project {
|
|||||||
doc.suspense();
|
doc.suspense();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.emitter.emit('actived-document-change', actived);
|
this.emitter.emit('current-document-change', actived);
|
||||||
}
|
}
|
||||||
|
|
||||||
closeOthers(opened: DocumentModel) {
|
closeOthers(opened: DocumentModel) {
|
||||||
@ -122,13 +122,14 @@ export default class Project {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onCurrentDocumentChange(fn: (doc: DocumentModel) => void): () => void {
|
||||||
|
this.emitter.on('current-document-change', fn);
|
||||||
|
return () => {
|
||||||
|
this.emitter.removeListener('current-document-change', fn);
|
||||||
|
};
|
||||||
|
}
|
||||||
// 通知标记删除,需要告知服务端
|
// 通知标记删除,需要告知服务端
|
||||||
// 项目角度编辑不是全量打开所有文档,是按需加载,哪个更新就通知更新谁,
|
// 项目角度编辑不是全量打开所有文档,是按需加载,哪个更新就通知更新谁,
|
||||||
// 哪个删除就
|
// 哪个删除就
|
||||||
onActivedDocumentChange(fn: (doc: DocumentModel) => void): () => void {
|
|
||||||
this.emitter.on('actived-document-change', fn);
|
|
||||||
return () => {
|
|
||||||
this.emitter.removeListener('actived-document-change', fn);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { Component } from "react";
|
import { Component } from "react";
|
||||||
import { FieldConfig } from '../../main';
|
import { FieldConfig, SettingField } from '../../main';
|
||||||
|
|
||||||
class ObjectSetter extends Component<{
|
class ObjectSetter extends Component<{
|
||||||
mode?: 'popup' | 'row' | 'form';
|
mode?: 'popup' | 'row' | 'form';
|
||||||
@ -29,6 +29,7 @@ interface ObjectSetterConfig {
|
|||||||
|
|
||||||
// for table|list row
|
// for table|list row
|
||||||
class RowSetter extends Component<{
|
class RowSetter extends Component<{
|
||||||
|
decriptor?: string | ((rowField: SettingField) => string);
|
||||||
config: ObjectSetterConfig;
|
config: ObjectSetterConfig;
|
||||||
columnsLimit?: number;
|
columnsLimit?: number;
|
||||||
}> {
|
}> {
|
||||||
|
|||||||
@ -4,9 +4,9 @@ import { ComponentType } from '../../designer/src/designer/component-type';
|
|||||||
import Node from '../../designer/src/designer/document/node/node';
|
import Node from '../../designer/src/designer/document/node/node';
|
||||||
import { TitleContent } from './title';
|
import { TitleContent } from './title';
|
||||||
import { ReactElement, ComponentType as ReactComponentType, isValidElement } from 'react';
|
import { ReactElement, ComponentType as ReactComponentType, isValidElement } from 'react';
|
||||||
import DocumentModel from '../../designer/src/designer/document/document-model';
|
|
||||||
import { isReactComponent } from '../../utils/is-react';
|
import { isReactComponent } from '../../utils/is-react';
|
||||||
import Designer from '../../designer/src/designer/designer';
|
import Designer from '../../designer/src/designer/designer';
|
||||||
|
import { Selection } from '../../designer/src/designer/document/selection';
|
||||||
|
|
||||||
export interface SettingTarget {
|
export interface SettingTarget {
|
||||||
// 所设置的节点集,至少一个
|
// 所设置的节点集,至少一个
|
||||||
@ -347,7 +347,6 @@ export function isSettingField(obj: any): obj is SettingField {
|
|||||||
return obj && obj.isSettingField;
|
return obj && obj.isSettingField;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export class SettingsMain implements SettingTarget {
|
export class SettingsMain implements SettingTarget {
|
||||||
private emitter = new EventEmitter();
|
private emitter = new EventEmitter();
|
||||||
|
|
||||||
@ -402,38 +401,26 @@ export class SettingsMain implements SettingTarget {
|
|||||||
|
|
||||||
private _designer?: Designer;
|
private _designer?: Designer;
|
||||||
get designer() {
|
get designer() {
|
||||||
return this._designer;
|
return this._designer || this.editor.designer;
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(readonly editor: any) {
|
constructor(readonly editor: any) {
|
||||||
let selectionChangeDispose: any = null;
|
const setupSelection = (selection?: Selection) => {
|
||||||
const setupDoc = (doc: DocumentModel) => {
|
if (selection) {
|
||||||
if (selectionChangeDispose) {
|
|
||||||
selectionChangeDispose();
|
|
||||||
selectionChangeDispose = null;
|
|
||||||
}
|
|
||||||
if (doc) {
|
|
||||||
if (!this._designer) {
|
if (!this._designer) {
|
||||||
this._designer = doc.designer;
|
this._designer = selection.doc.designer;
|
||||||
}
|
}
|
||||||
const selection = doc.selection;
|
|
||||||
this.setup(selection.getNodes());
|
this.setup(selection.getNodes());
|
||||||
selectionChangeDispose = doc.selection.onSelectionChange(() => {
|
|
||||||
this.setup(selection.getNodes());
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
this.setup([]);
|
this.setup([]);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const activedDispose = editor.on('designer.actived-document-change', setupDoc);
|
editor.on('designer.current-selection-change', setupSelection);
|
||||||
if (editor.designer) {
|
if (editor.designer) {
|
||||||
setupDoc(editor.designer.project.activedDocument);
|
setupSelection(editor.designer.currentSelection);
|
||||||
}
|
}
|
||||||
this.disposeListener = () => {
|
this.disposeListener = () => {
|
||||||
if (selectionChangeDispose) {
|
editor.removeListener('designer.current-selection-change', setupSelection);
|
||||||
selectionChangeDispose();
|
|
||||||
}
|
|
||||||
activedDispose();
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user