refactor: 将 obx 换成 mobx

This commit is contained in:
lihao.ylh 2021-09-06 14:19:08 +08:00
parent 7f7140a31f
commit f8584e101a
72 changed files with 363 additions and 424 deletions

View File

@ -6,7 +6,7 @@ module.exports = {
// // '^.+\\.(ts|tsx)$': 'ts-jest',
// // '^.+\\.(js|jsx)$': 'babel-jest',
// },
// testMatch: ['**/node.test.ts'],
// testMatch: ['**/prop.test.ts'],
// testMatch: ['(/tests?/.*(test))\\.[jt]s$'],
transformIgnorePatterns: [
`/node_modules/(?!${esModules})/`,

View File

@ -46,10 +46,6 @@ export class BorderDetectingInstance extends PureComponent<{
@observer
export class BorderDetecting extends Component<{ host: BuiltinSimulatorHost }> {
shouldComponentUpdate() {
return false;
}
@computed get scale() {
return this.props.host.viewport.scale;
}

View File

@ -28,10 +28,6 @@ export default class BoxResizing extends Component<{ host: BuiltinSimulatorHost
return this.dragging ? selection.getTopNodes() : selection.getNodes();
}
shouldComponentUpdate() {
return false;
}
componentDidUpdate() {
// this.hoveringCapture.setBoundary(this.outline);
// this.willBind();
@ -73,10 +69,6 @@ export class BoxResizingForNode extends Component<{ host: BuiltinSimulatorHost;
return this.host.getComponentInstances(this.props.node);
}
shouldComponentUpdate() {
return false;
}
render() {
const { instances } = this;
const { node } = this.props;

View File

@ -9,7 +9,7 @@ import {
ComponentType,
} from 'react';
import classNames from 'classnames';
import { observer, computed, Tip, globalContext, Editor } from '@ali/lowcode-editor-core';
import { observer, computed, Tip, globalContext, makeObservable } from '@ali/lowcode-editor-core';
import { createIcon, isReactComponent } from '@ali/lowcode-utils';
import { ActionContentObject, isActionContentObject } from '@ali/lowcode-types';
import { BuiltinSimulatorHost } from '../host';
@ -62,10 +62,6 @@ export class BorderSelectingInstance extends Component<{
@observer
class Toolbar extends Component<{ observed: OffsetObserver }> {
shouldComponentUpdate() {
return false;
}
render() {
const { observed } = this.props;
const { height, width } = observed.viewport;
@ -169,10 +165,6 @@ export class BorderSelectingForNode extends Component<{ host: BuiltinSimulatorHo
return this.host.getComponentInstances(this.props.node);
}
shouldComponentUpdate() {
return false;
}
render() {
const { instances } = this;
const { node } = this.props;
@ -217,10 +209,6 @@ export class BorderSelecting extends Component<{ host: BuiltinSimulatorHost }> {
return this.dragging ? selection.getTopNodes() : selection.getNodes();
}
shouldComponentUpdate() {
return false;
}
render() {
const { selecting } = this;
if (!selecting || selecting.length < 1) {

View File

@ -11,10 +11,6 @@ import './borders.less';
@observer
export class BemTools extends Component<{ host: BuiltinSimulatorHost }> {
shouldComponentUpdate() {
return false;
}
render() {
const { host } = this.props;
const { designMode } = host;

View File

@ -116,10 +116,6 @@ function processDetail({ target, detail, document }: DropLocation): InsertionDat
@observer
export class InsertionView extends Component<{ host: BuiltinSimulatorHost }> {
shouldComponentUpdate() {
return false;
}
render() {
const { host } = this.props;
const loc = host.currentDocument?.dropLocation;

View File

@ -1,11 +1,16 @@
import {
obx,
autorun,
reaction,
computed,
getPublicPath,
hotkey,
focusTracker,
engineConfig,
IReactionPublic,
IReactionOptions,
IReactionDisposer,
makeObservable,
} from '@ali/lowcode-editor-core';
import { EventEmitter } from 'events';
import {
@ -164,16 +169,27 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
readonly emitter: EventEmitter = new EventEmitter();
readonly componentsConsumer: ResourceConsumer;
readonly injectionConsumer: ResourceConsumer;
/**
*
*/
autoRender = true;
constructor(project: Project) {
makeObservable(this);
this.project = project;
this.designer = project?.designer;
this.scroller = this.designer.createScroller(this.viewport);
this.autoRender = !engineConfig.get('disableAutoRender', false);
this.componentsConsumer = new ResourceConsumer<Asset | undefined>(() => this.componentsAsset);
this.injectionConsumer = new ResourceConsumer(() => {
return {
i18n: this.project.i18n,
};
});
}
get currentDocument() {
@ -256,22 +272,25 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
*/
connect(
renderer: BuiltinSimulatorRenderer,
fn: (context: { dispose: () => void; firstRun: boolean }) => void,
effect: (reaction: IReactionPublic) => void, options?: IReactionOptions,
) {
this._renderer = renderer;
return autorun(fn as any, true);
return autorun(effect, options);
}
autorun(fn: (context: { dispose: () => void; firstRun: boolean }) => void) {
return autorun(fn as any, true);
reaction(expression: (reaction: IReactionPublic) => unknown, effect: (value: unknown, prev: unknown, reaction: IReactionPublic) => void,
opts?: IReactionOptions | undefined): IReactionDisposer {
return reaction(expression, effect, opts);
}
autorun(effect: (reaction: IReactionPublic) => void, options?: IReactionOptions): IReactionDisposer {
return autorun(effect, options);
}
purge(): void {
// todo
}
readonly viewport = new Viewport();
mountViewport(viewport: Element | null) {
this.viewport.mount(viewport);
}
@ -294,14 +313,6 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
return this._renderer;
}
readonly componentsConsumer = new ResourceConsumer<Asset | undefined>(() => this.componentsAsset);
readonly injectionConsumer = new ResourceConsumer(() => {
return {
i18n: this.project.i18n,
};
});
readonly asyncLibraryMap: { [key: string]: {} } = {};
readonly libraryMap: { [key: string]: string } = {};

View File

@ -1,4 +1,4 @@
import { obx, computed } from '@ali/lowcode-editor-core';
import { obx, computed, makeObservable } from '@ali/lowcode-editor-core';
import { Point, ScrollTarget } from '../designer';
import { AutoFit, IViewport } from '../simulator';
@ -25,6 +25,10 @@ export default class Viewport implements IViewport {
private viewportElement?: HTMLElement;
constructor() {
makeObservable(this);
}
mount(viewportElement: HTMLElement | null) {
if (!viewportElement || this.viewportElement === viewportElement) {
return;

View File

@ -1,5 +1,5 @@
import { ComponentType } from 'react';
import { obx, computed, autorun } from '@ali/lowcode-editor-core';
import { obx, computed, autorun, makeObservable, IReactionPublic, IReactionOptions, IReactionDisposer } from '@ali/lowcode-editor-core';
import {
ProjectSchema,
ComponentMetadata,
@ -71,6 +71,7 @@ export class Designer {
}
constructor(props: DesignerProps) {
makeObservable(this);
const { editor } = props;
this.editor = editor;
this.setProps(props);
@ -563,12 +564,12 @@ export class Designer {
}
}
autorun(action: (context: { firstRun: boolean }) => void, sync = false): () => void {
return autorun(action, sync as true);
autorun(effect: (reaction: IReactionPublic) => void, options?: IReactionOptions): IReactionDisposer {
return autorun(effect, options);
}
purge() {
// todo:
// TODO:
}
}

View File

@ -1,4 +1,4 @@
import { obx } from '@ali/lowcode-editor-core';
import { makeObservable, obx } from '@ali/lowcode-editor-core';
import { Node, DocumentModel } from '../document';
export class Detecting {
@ -19,6 +19,10 @@ export class Detecting {
@obx.ref private _current: Node | null = null;
constructor() {
makeObservable(this);
}
get current() {
return this._current;
}

View File

@ -1,5 +1,5 @@
import { Component } from 'react';
import { observer, obx, Title } from '@ali/lowcode-editor-core';
import { observer, obx, Title, makeObservable } from '@ali/lowcode-editor-core';
import { Designer } from '../designer';
import { DragObject, isDragNodeObject, isDragNodeDataObject } from '../dragon';
import { isSimulatorHost } from '../../simulator';
@ -23,6 +23,7 @@ export default class DragGhost extends Component<{ designer: Designer }> {
constructor(props: any) {
super(props);
makeObservable(this);
this.dispose = [
this.dragon.onDragstart(e => {
if (e.originalEvent.type.substr(0, 4) === 'drag') {
@ -52,10 +53,6 @@ export default class DragGhost extends Component<{ designer: Designer }> {
];
}
shouldComponentUpdate() {
return false;
}
componentWillUnmount() {
if (this.dispose) {
this.dispose.forEach(off => off());

View File

@ -1,5 +1,5 @@
import { EventEmitter } from 'events';
import { obx } from '@ali/lowcode-editor-core';
import { obx, makeObservable } from '@ali/lowcode-editor-core';
import { NodeSchema } from '@ali/lowcode-types';
import { setNativeSelection, cursor } from '@ali/lowcode-utils';
import { DropLocation } from './location';
@ -213,7 +213,9 @@ export class Dragon {
private emitter = new EventEmitter();
constructor(readonly designer: Designer) {}
constructor(readonly designer: Designer) {
makeObservable(this);
}
/**
* Quick listen a shell(container element) drag behavior

View File

@ -1,4 +1,4 @@
import { obx, computed } from '@ali/lowcode-editor-core';
import { obx, computed, makeObservable } from '@ali/lowcode-editor-core';
import { uniqueId } from '@ali/lowcode-utils';
import { INodeSelector, IViewport } from '../simulator';
import { isRootNode, Node } from '../document';
@ -106,8 +106,9 @@ export class OffsetObserver {
const doc = node.document;
const host = doc.simulator!;
const focusNode = doc.focusNode;
this.isRoot = node.contains(focusNode);
this.isRoot = node.contains(focusNode!);
this.viewport = host.viewport;
makeObservable(this);
if (this.isRoot) {
this.hasOffset = true;
return;

View File

@ -2,7 +2,7 @@ import { TitleContent, isDynamicSetter, SetterType, DynamicSetter, FieldExtraPro
import { Transducer } from './utils';
import { SettingPropEntry } from './setting-prop-entry';
import { SettingEntry } from './setting-entry';
import { computed, obx } from '@ali/lowcode-editor-core';
import { computed, obx, makeObservable, action } from '@ali/lowcode-editor-core';
import { cloneDeep } from '@ali/lowcode-utils';
import { ISetValueOptions } from '../../types';
@ -63,7 +63,7 @@ export class SettingField extends SettingPropEntry implements SettingEntry {
constructor(parent: SettingEntry, config: FieldConfig, settingFieldCollector?: (name: string | number, field: SettingField) => void) {
super(parent, config.name, config.type);
makeObservable(this);
const { title, items, setter, extraProps, ...rest } = config;
this.parent = parent;
this._config = config;
@ -141,6 +141,7 @@ export class SettingField extends SettingPropEntry implements SettingEntry {
private hotValue: any;
@action
setValue(val: any, isHotValue?: boolean, force?: boolean, extraOptions?: ISetValueOptions) {
if (isHotValue) {
this.setHotValue(val, extraOptions);
@ -162,6 +163,7 @@ export class SettingField extends SettingPropEntry implements SettingEntry {
}
/* istanbul ignore next */
@action
setMiniAppDataSourceValue(data: any, options?: any) {
this.hotValue = data;
const v = this.transducer.toNative(data);
@ -174,6 +176,7 @@ export class SettingField extends SettingPropEntry implements SettingEntry {
this.valueChange();
}
@action
setHotValue(data: any, options?: ISetValueOptions) {
this.hotValue = data;
const value = this.transducer.toNative(data);

View File

@ -1,4 +1,4 @@
import { obx, computed } from '@ali/lowcode-editor-core';
import { obx, computed, makeObservable, runInAction } from '@ali/lowcode-editor-core';
import { IEditor, isJSExpression } from '@ali/lowcode-types';
import { uniqueId } from '@ali/lowcode-utils';
import { SettingEntry } from './setting-entry';
@ -53,6 +53,7 @@ export class SettingPropEntry implements SettingEntry {
extraProps: any = {};
constructor(readonly parent: SettingEntry, name: string | number, type?: 'field' | 'group') {
makeObservable(this);
if (type == null) {
const c = typeof name === 'string' ? name.substr(0, 1) : '';
if (c === '#') {
@ -120,34 +121,36 @@ export class SettingPropEntry implements SettingEntry {
*/
/* istanbul ignore next */
@computed get valueState(): number {
if (this.type !== 'field') {
const { getValue } = this.extraProps;
return getValue ? (getValue(this, undefined) === undefined ? 0 : 1) : 0;
}
const propName = this.path.join('.');
const first = this.nodes[0].getProp(propName)!;
let l = this.nodes.length;
let state = 2;
while (l-- > 1) {
const next = this.nodes[l].getProp(propName, false);
const s = first.compare(next);
if (s > 1) {
return -1;
return runInAction(() => {
if (this.type !== 'field') {
const { getValue } = this.extraProps;
return getValue ? (getValue(this, undefined) === undefined ? 0 : 1) : 0;
}
if (s === 1) {
state = 1;
const propName = this.path.join('.');
const first = this.nodes[0].getProp(propName)!;
let l = this.nodes.length;
let state = 2;
while (--l > 0) {
const next = this.nodes[l].getProp(propName, false);
const s = first.compare(next);
if (s > 1) {
return -1;
}
if (s === 1) {
state = 1;
}
}
}
if (state === 2 && first.isUnset()) {
return 0;
}
return state;
if (state === 2 && first.isUnset()) {
return 0;
}
return state;
});
}
/**
*
*/
@computed getValue(): any {
getValue(): any {
let val: any;
if (this.type === 'field') {
val = this.parent.getPropValue(this.name);

View File

@ -1,4 +1,4 @@
import { computed, obx } from '@ali/lowcode-editor-core';
import { computed, makeObservable, obx, action } from '@ali/lowcode-editor-core';
import { NodeData, isJSExpression, isDOMText, NodeSchema, isNodeSchema, RootSchema, PageSchema } from '@ali/lowcode-types';
import { EventEmitter } from 'events';
import { Project } from '../project';
@ -61,7 +61,7 @@ export class DocumentModel {
readonly designer: Designer;
@obx.val private nodes = new Set<Node>();
@obx.shallow private nodes = new Set<Node>();
private seqId = 0;
@ -117,13 +117,7 @@ export class DocumentModel {
private inited = false;
constructor(project: Project, schema?: RootSchema) {
/*
// TODO
// use special purge process
autorun(() => {
console.info(this.willPurgeSpace);
}, true);
*/
makeObservable(this);
this.project = project;
this.designer = this.project?.designer;
this.emitter = new EventEmitter();
@ -161,7 +155,7 @@ export class DocumentModel {
this.inited = true;
}
@obx.val private willPurgeSpace: Node[] = [];
@obx.shallow private willPurgeSpace: Node[] = [];
get modalNode() {
return this._modalNode;
@ -182,7 +176,7 @@ export class DocumentModel {
}
}
@computed isBlank() {
isBlank() {
return this._blank && !this.isModified();
}
@ -213,7 +207,7 @@ export class DocumentModel {
return node ? !node.isPurged : false;
}
@obx.val private activeNodes?: Node[];
@obx.shallow private activeNodes?: Node[];
/**
* schema
@ -362,6 +356,7 @@ export class DocumentModel {
return this.rootNode?.schema as any;
}
@action
import(schema: RootSchema, checkId = false) {
const drillDownNodeId = this._drillDownNode?.id;
// TODO: 暂时用饱和式删除,原因是 Slot 节点并不是树节点,无法正常递归删除

View File

@ -6,10 +6,6 @@ import { BuiltinSimulatorHostView } from '../builtin-simulator';
@observer
export class DocumentView extends Component<{ document: DocumentModel }> {
shouldComponentUpdate() {
return false;
}
render() {
const { document } = this.props;
const { simulatorProps } = document;

View File

@ -31,20 +31,16 @@ export class History {
private emitter = new EventEmitter();
private obx: Reaction;
private justWokeup = false;
private asleep = false;
constructor(logger: () => any, private redoer: (data: NodeSchema) => void, private timeGap: number = 1000) {
this.session = new Session(0, null, this.timeGap);
this.records = [this.session];
this.obx = autorun(() => {
autorun(() => {
if (this.asleep) return;
const data = logger();
if (this.justWokeup) {
this.justWokeup = false;
return;
}
untracked(() => {
const log = currentSerialization.serialize(data);
if (this.session.cursor === 0 && this.session.isActive()) {
@ -68,7 +64,7 @@ export class History {
}
}
});
}, true).$obx;
});
}
get hotData() {
@ -79,6 +75,14 @@ export class History {
return this.point !== this.session.cursor;
}
private sleep() {
this.asleep = true;
}
private wakeup() {
this.asleep = false;
}
go(cursor: number) {
this.session.end();
@ -96,7 +100,7 @@ export class History {
const session = this.records[cursor];
const hotData = session.data;
this.obx.sleep();
this.sleep();
try {
this.redoer(currentSerialization.unserialize(hotData));
this.emitter.emit('cursor', hotData);
@ -104,8 +108,7 @@ export class History {
//
}
this.justWokeup = true;
this.obx.wakeup();
this.wakeup();
this.session = session;
this.emitter.emit('statechange', this.getState());

View File

@ -1,4 +1,4 @@
import { obx, computed } from '@ali/lowcode-editor-core';
import { obx, computed, makeObservable } from '@ali/lowcode-editor-core';
import { uniqueId } from '@ali/lowcode-utils';
import { TitleContent } from '@ali/lowcode-types';
import { Node } from './node';
@ -11,7 +11,7 @@ export class ExclusiveGroup {
readonly id = uniqueId('exclusive');
@obx.val readonly children: Node[] = [];
@obx.shallow readonly children: Node[] = [];
@obx private visibleIndex = 0;
@ -75,6 +75,7 @@ export class ExclusiveGroup {
readonly title: TitleContent;
constructor(readonly name: string, title?: TitleContent) {
makeObservable(this);
this.title = title || {
type: 'i18n',
intl: intl('Condition Group'),

View File

@ -1,4 +1,4 @@
import { obx, computed, globalContext } from '@ali/lowcode-editor-core';
import { obx, computed, globalContext, makeObservable } from '@ali/lowcode-editor-core';
import { Node, ParentalNode } from './node';
import { TransformStage } from './transform-stage';
import { NodeData, isNodeSchema } from '@ali/lowcode-types';
@ -8,11 +8,12 @@ import { foreachReverse } from '../../utils/tree';
import { NodeRemoveOptions } from '../../types';
export class NodeChildren {
@obx.val private children: Node[];
@obx.shallow private children: Node[];
private emitter = new EventEmitter();
constructor(readonly owner: ParentalNode, data: NodeData | NodeData[], options: any = {}) {
makeObservable(this);
this.children = (Array.isArray(data) ? data : [data]).map(child => {
return this.owner.document.createNode(child, options.checkId);
});
@ -83,11 +84,11 @@ export class NodeChildren {
/**
*
*/
@computed isEmpty() {
isEmpty() {
return this.size < 1;
}
@computed notEmpty() {
notEmpty() {
return this.size > 0;
}
@ -387,7 +388,7 @@ export class NodeChildren {
}
if (owner.parent && !owner.parent.isRoot()) {
this.reportModified(node, owner.parent, { ...options, propagated: true, });
this.reportModified(node, owner.parent, { ...options, propagated: true });
}
}
}

View File

@ -1,6 +1,6 @@
import { ReactElement } from 'react';
import { EventEmitter } from 'events';
import { obx, computed, autorun } from '@ali/lowcode-editor-core';
import { obx, computed, autorun, makeObservable, runInAction, getObserverTree } from '@ali/lowcode-editor-core';
import {
isDOMText,
isJSExpression,
@ -160,6 +160,7 @@ export class Node<Schema extends NodeSchema = NodeSchema> {
isInited = false;
constructor(readonly document: DocumentModel, nodeSchema: Schema, options: any = {}) {
makeObservable(this);
const { componentName, id, children, props, ...extras } = nodeSchema;
this.id = document.nextId(id);
this.componentName = componentName;
@ -208,7 +209,7 @@ export class Node<Schema extends NodeSchema = NodeSchema> {
this.autoruns = autoruns.map((item) => {
return autorun(() => {
item.autorun(this.props.get(item.name, true) as any);
}, true);
});
});
}
@ -382,7 +383,7 @@ export class Node<Schema extends NodeSchema = NodeSchema> {
*
*/
get isLocked(): boolean {
return !!this.getExtraProp('isLocked', false)?.getValue();
return !!this.getExtraProp('isLocked')?.getValue();
}
/**
@ -417,9 +418,9 @@ export class Node<Schema extends NodeSchema = NodeSchema> {
return this.props.export(TransformStage.Serilize).props || null;
}
@obx.val _slots: Node[] = [];
@obx.shallow _slots: Node[] = [];
@computed hasSlots() {
hasSlots() {
return this._slots.length > 0;
}
@ -465,7 +466,7 @@ export class Node<Schema extends NodeSchema = NodeSchema> {
}
/* istanbul ignore next */
@computed isConditionalVisible(): boolean | undefined {
isConditionalVisible(): boolean | undefined {
return this._conditionGroup?.isVisible(this);
}
@ -474,7 +475,7 @@ export class Node<Schema extends NodeSchema = NodeSchema> {
this._conditionGroup?.setVisible(this);
}
@computed hasCondition() {
hasCondition() {
const v = this.getExtraProp('condition', false)?.getValue();
return v != null && v !== '' && v !== true;
}
@ -483,7 +484,7 @@ export class Node<Schema extends NodeSchema = NodeSchema> {
* has loop when 1. loop is validArray with length > 1 ; OR 2. loop is variable object
* @return boolean, has loop config or not
*/
@computed hasLoop() {
hasLoop() {
const value = this.getExtraProp('loop', false)?.getValue();
if (value === undefined || value === null) {
return false;
@ -550,12 +551,12 @@ export class Node<Schema extends NodeSchema = NodeSchema> {
};
}
getProp(path: string, stash = true): Prop | null {
return this.props.query(path, stash as any) || null;
getProp(path: string, createIfNone = true): Prop | null {
return this.props.query(path, createIfNone) || null;
}
getExtraProp(key: string, stash = true): Prop | null {
return this.props.get(getConvertedExtraKey(key), stash) || null;
getExtraProp(key: string, createIfNone = true): Prop | null {
return this.props.get(getConvertedExtraKey(key), createIfNone) || null;
}
setExtraProp(key: string, value: CompositeValue) {
@ -647,7 +648,7 @@ export class Node<Schema extends NodeSchema = NodeSchema> {
}
set schema(data: Schema) {
this.import(data);
runInAction(() => this.import(data));
}
import(data: Schema, checkId = false) {
@ -865,9 +866,6 @@ export class Node<Schema extends NodeSchema = NodeSchema> {
return this.componentName;
}
/**
* @deprecated
*/
insert(node: Node, ref?: Node, useMutator = true) {
this.insertAfter(node, ref, useMutator);
}
@ -918,7 +916,7 @@ export class Node<Schema extends NodeSchema = NodeSchema> {
this.children?.mergeChildren(remover, adder, sorter);
}
@obx.val status: NodeStatus = {
@obx.shallow status: NodeStatus = {
inPlaceEditing: false,
locking: false,
pseudo: false,

View File

@ -1,4 +1,4 @@
import { obx, autorun, untracked, computed } from '@ali/lowcode-editor-core';
import { obx, autorun, untracked, computed, makeObservable, action } from '@ali/lowcode-editor-core';
import { Prop, IPropParent, UNSET } from './prop';
import { Props } from './props';
import { Node } from '../node';
@ -7,7 +7,7 @@ export type PendingItem = Prop[];
export class PropStash implements IPropParent {
readonly isPropStash = true;
@obx.val private space: Set<Prop> = new Set();
@obx.shallow private space: Set<Prop> = new Set();
@computed private get maps(): Map<string | number, Prop> {
const maps = new Map();
@ -24,6 +24,7 @@ export class PropStash implements IPropParent {
readonly owner: Node;
constructor(readonly props: Props, write: (item: Prop) => void) {
makeObservable(this);
this.owner = props.owner;
this.willPurge = autorun(() => {
if (this.space.size < 1) {
@ -46,6 +47,7 @@ export class PropStash implements IPropParent {
});
}
@action
get(key: string | number): Prop {
let prop = this.maps.get(key);
if (!prop) {
@ -55,16 +57,19 @@ export class PropStash implements IPropParent {
return prop;
}
@action
delete(prop: Prop) {
this.space.delete(prop);
prop.purge();
}
@action
clear() {
this.space.forEach(item => item.purge());
this.space.clear();
}
@action
purge() {
this.willPurge();
this.space.clear();

View File

@ -1,12 +1,12 @@
import { untracked, computed, obx, engineConfig } from '@ali/lowcode-editor-core';
import { untracked, computed, obx, engineConfig, action, makeObservable, mobx } from '@ali/lowcode-editor-core';
import { CompositeValue, FieldConfig, isJSExpression, isJSSlot, JSSlot, SlotSchema } from '@ali/lowcode-types';
import { uniqueId, isPlainObject, hasOwnProperty, compatStage } from '@ali/lowcode-utils';
import { PropStash } from './prop-stash';
import { valueToSource } from './value-to-source';
import { Props } from './props';
import { SlotNode, Node } from '../node';
import { TransformStage } from '../transform-stage';
const { set: mobxSet, isObservableArray } = mobx;
export const UNSET = Symbol.for('unset');
export type UNSET = typeof UNSET;
@ -24,8 +24,6 @@ export class Prop implements IPropParent {
readonly owner: Node;
private stash: PropStash | undefined;
/**
*
*/
@ -47,6 +45,7 @@ export class Prop implements IPropParent {
spread = false,
options = {},
) {
makeObservable(this);
this.owner = parent.owner;
this.props = parent.props;
this.key = key;
@ -59,6 +58,7 @@ export class Prop implements IPropParent {
}
// TODO: 先用调用方式触发子 prop 的初始化,后续须重构
@action
private setupItems() {
return this.items;
}
@ -66,6 +66,7 @@ export class Prop implements IPropParent {
/**
* @see SettingTarget
*/
@action
getPropValue(propName: string | number): any {
return this.get(propName)!.getValue();
}
@ -73,6 +74,7 @@ export class Prop implements IPropParent {
/**
* @see SettingTarget
*/
@action
setPropValue(propName: string | number, value: any): void {
this.set(propName, value);
}
@ -80,6 +82,7 @@ export class Prop implements IPropParent {
/**
* @see SettingTarget
*/
@action
clearPropValue(propName: string | number): void {
this.get(propName, false)?.unset();
}
@ -95,7 +98,7 @@ export class Prop implements IPropParent {
return this._type;
}
@obx.ref private _value: any = UNSET;
@obx private _value: any = UNSET;
/**
*
@ -213,7 +216,7 @@ export class Prop implements IPropParent {
this._code = code;
}
@computed getAsString(): string {
getAsString(): string {
if (this.type === 'literal') {
return this._value ? String(this._value) : '';
}
@ -223,6 +226,7 @@ export class Prop implements IPropParent {
/**
* set value, val should be JSON Object
*/
@action
setValue(val: CompositeValue) {
if (val === this._value) return;
const editor = this.owner.document?.designer.editor;
@ -267,10 +271,11 @@ export class Prop implements IPropParent {
}
}
@computed getValue(): CompositeValue {
getValue(): CompositeValue {
return this.export(TransformStage.Serilize);
}
@action
private dispose() {
const items = untracked(() => this._items);
if (items) {
@ -278,9 +283,6 @@ export class Prop implements IPropParent {
}
this._items = null;
this._maps = null;
if (this.stash) {
this.stash.clear();
}
if (this._type !== 'slot' && this._slotNode) {
this._slotNode.remove();
this._slotNode = undefined;
@ -293,6 +295,7 @@ export class Prop implements IPropParent {
return this._slotNode;
}
@action
setAsSlot(data: JSSlot) {
this._type = 'slot';
const slotSchema: SlotSchema = {
@ -315,6 +318,7 @@ export class Prop implements IPropParent {
/**
*
*/
@action
unset() {
this._type = 'unset';
}
@ -322,6 +326,7 @@ export class Prop implements IPropParent {
/**
*
*/
@action
isUnset() {
return this._type === 'unset';
}
@ -352,9 +357,9 @@ export class Prop implements IPropParent {
return this.code === other.code ? 0 : 2;
}
@obx.val private _items: Prop[] | null = null;
@obx.shallow private _items: Prop[] | null = null;
@obx.val private _maps: Map<string | number, Prop> | null = null;
@obx.shallow private _maps: Map<string | number, Prop> | null = null;
get path(): string[] {
return (this.parent.path || []).concat(this.key as string);
@ -401,11 +406,12 @@ export class Prop implements IPropParent {
/**
*
* @param stash
* @param createIfNone
*/
get(path: string | number, stash = true): Prop | null {
@action
get(path: string | number, createIfNone = true): Prop | null {
const type = this._type;
if (type !== 'map' && type !== 'list' && type !== 'unset' && !stash) {
if (type !== 'map' && type !== 'list' && type !== 'unset' && !createIfNone) {
return null;
}
@ -434,20 +440,12 @@ export class Prop implements IPropParent {
}
if (prop) {
return nest ? prop.get(nest, stash) : prop;
return nest ? prop.get(nest, createIfNone) : prop;
}
if (stash) {
if (!this.stash) {
this.stash = new PropStash(this.props, (item) => {
// item take effect
if (item.key) {
this.set(item.key, item, true);
}
item.parent = this;
});
}
prop = this.stash.get(entry);
if (createIfNone) {
prop = new Prop(this, UNSET, entry);
this.set(entry, prop, true);
if (nest) {
return prop.get(nest, true);
}
@ -461,6 +459,7 @@ export class Prop implements IPropParent {
/**
*
*/
@action
remove() {
this.parent.delete(this);
}
@ -468,6 +467,7 @@ export class Prop implements IPropParent {
/**
*
*/
@action
delete(prop: Prop): void {
if (this.items) {
const i = this.items.indexOf(prop);
@ -484,6 +484,7 @@ export class Prop implements IPropParent {
/**
* key
*/
@action
deleteKey(key: string): void {
if (this.maps) {
const prop = this.maps.get(key);
@ -505,6 +506,7 @@ export class Prop implements IPropParent {
*
* @param force
*/
@action
add(value: CompositeValue, force = false): Prop | null {
const type = this._type;
if (type !== 'list' && type !== 'unset' && !force) {
@ -523,6 +525,7 @@ export class Prop implements IPropParent {
*
* @param force
*/
@action
set(key: string | number, value: CompositeValue | Prop, force = false) {
const type = this._type;
if (type !== 'map' && type !== 'list' && type !== 'unset' && !force) {
@ -543,7 +546,11 @@ export class Prop implements IPropParent {
if (!isValidArrayIndex(key)) {
return null;
}
items[key] = prop;
if (isObservableArray(items)) {
mobxSet(items, key, prop);
} else {
items[key] = prop;
}
} else if (this.maps) {
const { maps } = this;
const orig = maps.get(key);
@ -584,14 +591,12 @@ export class Prop implements IPropParent {
/**
*
*/
@action
purge() {
if (this.purged) {
return;
}
this.purged = true;
if (this.stash) {
this.stash.purge();
}
if (this._items) {
this._items.forEach((item) => item.purge());
}
@ -627,6 +632,7 @@ export class Prop implements IPropParent {
/**
*
*/
@action
forEach(fn: (item: Prop, key: number | string | undefined) => void): void {
const { items } = this;
if (!items) {
@ -641,6 +647,7 @@ export class Prop implements IPropParent {
/**
*
*/
@action
map<T>(fn: (item: Prop, key: number | string | undefined) => T): T[] | null {
const { items } = this;
if (!items) {

View File

@ -1,7 +1,6 @@
import { computed, obx } from '@ali/lowcode-editor-core';
import { computed, makeObservable, obx, action } from '@ali/lowcode-editor-core';
import { PropsMap, PropsList, CompositeValue } from '@ali/lowcode-types';
import { uniqueId, compatStage } from '@ali/lowcode-utils';
import { PropStash } from './prop-stash';
import { Prop, IPropParent, UNSET } from './prop';
import { Node } from '../node';
import { TransformStage } from '../transform-stage';
@ -23,7 +22,7 @@ export function getOriginalExtraKey(key: string): string {
export class Props implements IPropParent {
readonly id = uniqueId('props');
@obx.val private items: Prop[] = [];
@obx.shallow private items: Prop[] = [];
@computed private get maps(): Map<string, Prop> {
const maps = new Map();
@ -45,8 +44,6 @@ export class Props implements IPropParent {
readonly owner: Node;
private stash: PropStash;
/**
*
*/
@ -57,11 +54,8 @@ export class Props implements IPropParent {
@obx type: 'map' | 'list' = 'map';
constructor(owner: Node, value?: PropsMap | PropsList | null, extras?: object) {
makeObservable(this);
this.owner = owner;
this.stash = new PropStash(this, prop => {
this.items.push(prop);
prop.parent = this;
});
if (Array.isArray(value)) {
this.type = 'list';
this.items = value.map(item => new Prop(this, item.value, item.name, item.spread));
@ -75,8 +69,8 @@ export class Props implements IPropParent {
}
}
@action
import(value?: PropsMap | PropsList | null, extras?: object) {
this.stash.clear();
const originItems = this.items;
if (Array.isArray(value)) {
this.type = 'list';
@ -96,6 +90,7 @@ export class Props implements IPropParent {
originItems.forEach(item => item.purge());
}
@action
merge(value: PropsMap, extras?: PropsMap) {
Object.keys(value).forEach(key => {
this.query(key, true)!.setValue(value[key]);
@ -119,9 +114,6 @@ export class Props implements IPropParent {
props = [];
this.items.forEach(item => {
let value = item.export(stage);
if (value === UNSET) {
value = undefined;
}
let name = item.key as string;
if (name && typeof name === 'string' && name.startsWith(EXTRA_KEY_PREFIX)) {
name = getOriginalExtraKey(name);
@ -142,9 +134,6 @@ export class Props implements IPropParent {
return;
}
let value = item.export(stage);
if (value === UNSET) {
value = undefined;
}
allProps[name] = value;
});
// compatible vision
@ -187,53 +176,19 @@ export class Props implements IPropParent {
/**
* path
*
* @param stash
* @param createIfNone
*/
query(path: string, stash = true): Prop | null {
return this.get(path, stash);
// todo: future support list search
// let matchedLength = 0;
// let firstMatched = null;
// if (this.items) {
// // target: a.b.c
// // trys: a.b.c, a.b, a
// let i = this.items.length;
// while (i-- > 0) {
// const expr = this.items[i];
// if (!expr.key) {
// continue;
// }
// const name = String(expr.key);
// if (name === path) {
// // completely match
// return expr;
// }
// // fisrt match
// const l = name.length;
// if (path.slice(0, l + 1) === `${name}.`) {
// matchedLength = l;
// firstMatched = expr;
// }
// }
// }
// let ret = null;
// if (firstMatched) {
// ret = firstMatched.get(path.slice(matchedLength + 1), true);
// }
// if (!ret && stash) {
// return this.stash.get(path);
// }
// return ret;
@action
query(path: string, createIfNone = true): Prop | null {
return this.get(path, createIfNone);
}
/**
* ,
* @param stash
* @param createIfNone
*/
get(path: string, stash = false): Prop | null {
@action
get(path: string, createIfNone = false): Prop | null {
let entry = path;
let nest = '';
const i = path.indexOf('.');
@ -244,10 +199,14 @@ export class Props implements IPropParent {
}
}
const prop = this.maps.get(entry) || (stash && this.stash.get(entry)) || null;
let prop = this.maps.get(entry);
if (!prop && createIfNone) {
prop = new Prop(this, UNSET, entry);
this.items.push(prop);
}
if (prop) {
return nest ? prop.get(nest, stash) : prop;
return nest ? prop.get(nest, createIfNone) : prop;
}
return null;
@ -256,6 +215,7 @@ export class Props implements IPropParent {
/**
*
*/
@action
delete(prop: Prop): void {
const i = this.items.indexOf(prop);
if (i > -1) {
@ -267,6 +227,7 @@ export class Props implements IPropParent {
/**
* key
*/
@action
deleteKey(key: string): void {
this.items = this.items.filter(item => {
if (item.key === key) {
@ -280,6 +241,7 @@ export class Props implements IPropParent {
/**
*
*/
@action
add(value: CompositeValue | null, key?: string | number, spread = false, options: any = {}): Prop {
const prop = new Prop(this, value, key, spread, options);
this.items.push(prop);
@ -319,6 +281,7 @@ export class Props implements IPropParent {
/**
*
*/
@action
forEach(fn: (item: Prop, key: number | string | undefined) => void): void {
this.items.forEach(item => {
return fn(item, item.key);
@ -328,12 +291,14 @@ export class Props implements IPropParent {
/**
*
*/
@action
map<T>(fn: (item: Prop, key: number | string | undefined) => T): T[] | null {
return this.items.map(item => {
return fn(item, item.key);
});
}
@action
filter(fn: (item: Prop, key: number | string | undefined) => boolean) {
return this.items.filter(item => {
return fn(item, item.key);
@ -345,22 +310,28 @@ export class Props implements IPropParent {
/**
*
*/
@action
purge() {
if (this.purged) {
return;
}
this.purged = true;
this.stash.purge();
this.items.forEach(item => item.purge());
}
getProp(path: string, stash = true): Prop | null {
return this.query(path, stash as any) || null;
/**
* ,
* @param createIfNone
*/
@action
getProp(path: string, createIfNone = true): Prop | null {
return this.query(path, createIfNone) || null;
}
/**
*
*/
@action
getPropValue(path: string): any {
return this.getProp(path, false)?.value;
}
@ -368,6 +339,7 @@ export class Props implements IPropParent {
/**
*
*/
@action
setPropValue(path: string, value: any) {
this.getProp(path, true)!.setValue(value);
}
@ -383,6 +355,7 @@ export class Props implements IPropParent {
* @deprecated
* props node
*/
@action
toData() {
return this.export()?.props;
}

View File

@ -1,14 +1,16 @@
import { EventEmitter } from 'events';
import { obx } from '@ali/lowcode-editor-core';
import { obx, makeObservable } from '@ali/lowcode-editor-core';
import { Node, comparePosition, PositionNO } from './node/node';
import { DocumentModel } from './document-model';
export class Selection {
private emitter = new EventEmitter();
@obx.val private _selected: string[] = [];
@obx.shallow private _selected: string[] = [];
constructor(readonly doc: DocumentModel) {}
constructor(readonly doc: DocumentModel) {
makeObservable(this);
}
/**
* id

View File

@ -1,5 +1,5 @@
import { EventEmitter } from 'events';
import { obx, computed } from '@ali/lowcode-editor-core';
import { obx, computed, makeObservable, action } from '@ali/lowcode-editor-core';
import { Designer } from '../designer';
import { DocumentModel, isDocumentModel, isPageSchema } from '../document';
import { ProjectSchema, RootSchema } from '@ali/lowcode-types';
@ -8,7 +8,7 @@ import { ISimulatorHost } from '../simulator';
export class Project {
private emitter = new EventEmitter();
@obx.val readonly documents: DocumentModel[] = [];
@obx.shallow readonly documents: DocumentModel[] = [];
private data: ProjectSchema = { version: '1.0.0', componentsMap: [], componentsTree: [], i18n: {} };
@ -24,6 +24,7 @@ export class Project {
// TODO: 考虑项目级别 History
constructor(readonly designer: Designer, schema?: ProjectSchema) {
makeObservable(this);
this.load(schema);
}
@ -76,6 +77,7 @@ export class Project {
*
* @param autoOpen true string
*/
@action
load(schema?: ProjectSchema, autoOpen?: boolean | string) {
this.unload();
// load new document

View File

@ -105,7 +105,7 @@ describe('Host 测试', () => {
type: AssetType.CSSText,
content: '.theme {font-size: 50px;}',
});
expect(host.componentsMap).toBe(designer.componentsMap);
expect(host.componentsMap).toEqual(designer.componentsMap);
expect(host.requestHandlersMap).toEqual({});
host.set('renderEnv', 'vue');

View File

@ -85,7 +85,7 @@ describe('Prop 类测试', () => {
strProp.unset();
strProp.add(2, true);
strProp.set(1);
strProp.set(0);
expect(numProp.set()).toBeNull();
expect(numProp.has()).toBeFalsy();
@ -391,7 +391,7 @@ describe('Prop 类测试', () => {
prop.unset();
prop.set(0, true);
expect(prop.set('x', 'invalid')).toBeNull();
expect(prop.get(0).getValue()).toBeTruthy();
expect(prop.get(0).getValue()).toBeUndefined();
});
it('export', () => {

View File

@ -50,10 +50,9 @@ describe('组件元数据处理', () => {
expect(meta.availableActions[2].name).toBe('copy');
removeBuiltinComponentAction('remove');
// availableActions 有 computed 缓存
expect(meta.availableActions[0].name).toBe('remove');
expect(meta.availableActions[1].name).toBe('hide');
expect(meta.availableActions[2].name).toBe('copy');
expect(meta.availableActions).toHaveLength(4);
expect(meta.availableActions[0].name).toBe('hide');
expect(meta.availableActions[1].name).toBe('copy');
addBuiltinComponentAction({
name: 'new',
@ -61,10 +60,9 @@ describe('组件元数据处理', () => {
action() {},
},
});
// availableActions 有 computed 缓存
expect(meta.availableActions).toHaveLength(5);
expect(meta.availableActions[0].name).toBe('remove');
expect(meta.availableActions[1].name).toBe('hide');
expect(meta.availableActions[2].name).toBe('copy');
expect(meta.availableActions[0].name).toBe('hide');
expect(meta.availableActions[1].name).toBe('copy');
expect(meta.availableActions[4].name).toBe('new');
});
});

View File

@ -16,8 +16,6 @@
"@ali/lowcode-types": "1.0.66",
"@ali/lowcode-utils": "1.0.66",
"@alifd/next": "^1.19.16",
"@recore/obx": "^1.0.9",
"@recore/obx-react": "^1.0.8",
"classnames": "^2.2.6",
"debug": "^4.1.1",
"intl-messageformat": "^9.3.1",
@ -25,7 +23,9 @@
"power-di": "^2.2.4",
"react": "^16",
"react-dom": "^16.7.0",
"store": "^2.0.12"
"store": "^2.0.12",
"mobx": "^6.3.0",
"mobx-react": "^7.2.0"
},
"devDependencies": {
"@alib/build-scripts": "^0.1.18",

View File

@ -39,7 +39,7 @@ export class Editor extends EventEmitter implements IEditor {
/**
* Ioc Container
*/
@obx.val private context = new Map<KeyType, any>();
@obx.shallow private context = new Map<KeyType, any>();
get locale() {
return globalLocale.getLocale();

View File

@ -97,7 +97,8 @@ export function createIntl(
getLocale(): string;
setLocale(locale: string): void;
} {
const data = computed(() => {
// TODO: make reactive
const data = (() => {
const locale = globalLocale.getLocale();
if (typeof instance === 'string') {
if ((window as any)[instance]) {
@ -110,11 +111,11 @@ export function createIntl(
return (instance as any)[locale] || {};
}
return {};
});
})();
function intl(key: string, params?: object): string {
// TODO: tries lost language
const str = data.value[key];
const str = data[key];
if (str == null) {
return `##intl@${key}##`;

View File

@ -1,5 +1,24 @@
import { observer } from '@recore/obx-react';
import { observer } from 'mobx-react';
import { configure } from 'mobx';
export * from '@recore/obx';
configure({ enforceActions: 'never' });
// 常用的直接导出,其他的以 mobx 命名空间导出
export {
observable as obx,
observable,
observe,
autorun,
makeObservable,
makeAutoObservable,
reaction,
computed,
action,
runInAction,
untracked,
IReactionDisposer,
IReactionPublic,
IReactionOptions,
} from 'mobx';
export * as mobx from 'mobx';
export { observer };

View File

@ -1,4 +1,4 @@
import { obx, computed } from '@ali/lowcode-editor-core';
import { obx, computed, makeObservable } from '@ali/lowcode-editor-core';
import WidgetContainer from './widget/widget-container';
import { Skeleton } from './skeleton';
import { IWidget } from './widget/widget';
@ -24,10 +24,11 @@ export default class Area<C extends IWidgetBaseConfig = any, T extends IWidget =
readonly container: WidgetContainer<T, C>;
constructor(readonly skeleton: Skeleton, readonly name: string, handle: (item: T | C) => T, private exclusive?: boolean, defaultSetCurrent = false) {
makeObservable(this);
this.container = skeleton.createContainer(name, handle, exclusive, () => this.visible, defaultSetCurrent);
}
@computed isEmpty(): boolean {
isEmpty(): boolean {
return this.container.items.length < 1;
}

View File

@ -1,6 +1,6 @@
import { Component, Fragment } from 'react';
import { Icon, Button, Message } from '@alifd/next';
import { Title } from '@ali/lowcode-editor-core';
import { Title, runInAction } from '@ali/lowcode-editor-core';
import { SetterType, FieldConfig, SetterConfig } from '@ali/lowcode-types';
import { SettingField } from '@ali/lowcode-designer';
import { createSettingFieldView } from '../settings/settings-pane';
@ -60,9 +60,11 @@ export class ListSetter extends Component<ArraySetterProps, ArraySetterState> {
itemsMap,
}, () => {
// setValue 会触发onItemChange需要在items被设值之后才能调用
value && value.map((item, index) => {
items[index].setValue(item);
return item;
runInAction(() => {
value && value.map((item, index) => {
items[index].setValue(item);
return item;
});
});
});
}

View File

@ -1,7 +1,6 @@
import { EventEmitter } from 'events';
import { Node, Designer, Selection, SettingTopEntry } from '@ali/lowcode-designer';
import { Editor, obx, computed } from '@ali/lowcode-editor-core';
import { executePendingFn } from '@ali/lowcode-utils';
import { Editor, obx, computed, makeObservable, action } from '@ali/lowcode-editor-core';
function generateSessionId(nodes: Node[]) {
return nodes
@ -34,6 +33,7 @@ export class SettingsMain {
private designer?: Designer;
constructor(readonly editor: Editor) {
makeObservable(this);
this.init();
}
@ -54,6 +54,7 @@ export class SettingsMain {
setupSelection(designer.currentSelection);
}
@action
private setup(nodes: Node[]) {
// check nodes change
const sessionId = generateSessionId(nodes);

View File

@ -158,10 +158,6 @@ class SettingFieldView extends Component<{ field: SettingField }> {
class SettingGroupView extends Component<{ field: SettingField }> {
static contextType = SkeletonContext;
shouldComponentUpdate() {
return false;
}
render() {
const { field } = this.props;
const { extraProps } = field;
@ -227,12 +223,7 @@ export type SettingsPaneProps = {
export class SettingsPane extends Component<SettingsPaneProps> {
static contextType = SkeletonContext;
@obx
private currentStage?: Stage;
shouldComponentUpdate() {
return false;
}
@obx private currentStage?: Stage;
private popupPipe = new PopupPipe();

View File

@ -1,6 +1,6 @@
import React, { Component } from 'react';
import { Tab, Breadcrumb } from '@alifd/next';
import { Title, observer, Editor, obx, globalContext, engineConfig } from '@ali/lowcode-editor-core';
import { Title, observer, Editor, obx, globalContext, engineConfig, makeObservable } from '@ali/lowcode-editor-core';
import { Node, isSettingField, SettingField, Designer } from '@ali/lowcode-designer';
import { SettingsMain } from './main';
import { SettingsPane } from './settings-pane';
@ -16,8 +16,9 @@ export class SettingsPrimaryPane extends Component<{ editor: Editor; config: any
@obx.ref private _activeKey?: any;
shouldComponentUpdate() {
return false;
constructor(props) {
super(props);
makeObservable(this);
}
componentDidMount() {
@ -62,8 +63,8 @@ export class SettingsPrimaryPane extends Component<{ editor: Editor; config: any
);
}
const editor = globalContext.get(Editor);
const designer = editor.get(Designer);
const editor = globalContext.get('editor');
const designer = editor.get('designer');
const current = designer?.currentSelection?.getNodes()?.[0];
let node: Node | null = settings.first;
const focusNode = node.document.focusNode;

View File

@ -157,10 +157,6 @@ export class DraggableLineView extends Component<{ panel: Panel }> {
@observer
export class TitledPanelView extends Component<{ panel: Panel; area?: string }> {
shouldComponentUpdate() {
return false;
}
componentDidMount() {
this.checkVisible();
}
@ -218,10 +214,6 @@ export class PanelView extends Component<{
hideOperationRow?: boolean;
hideDragLine?: boolean;
}> {
shouldComponentUpdate() {
return false;
}
componentDidMount() {
this.checkVisible();
}
@ -333,10 +325,6 @@ class PanelTitle extends Component<{ panel: Panel; className?: string }> {
@observer
export class WidgetView extends Component<{ widget: IWidget }> {
shouldComponentUpdate() {
return false;
}
componentDidMount() {
this.checkVisible();
this.checkDisabled();

View File

@ -1,11 +1,17 @@
import { Component, Fragment } from 'react';
import { Button, Icon } from '@alifd/next';
import { action, makeObservable } from '@ali/lowcode-editor-core';
import { IconFix } from '../../icons/fix';
import { IconFloat } from '../../icons/float';
import Panel from '../../widget/panel';
export default class PanelOperationRow extends Component<{ panel: Panel }> {
constructor(props) {
super(props);
makeObservable(this);
}
// fix or float
@action
setDisplay() {
const { panel } = this.props;
const current = panel;

View File

@ -6,10 +6,6 @@ import Panel from '../widget/panel';
@observer
export default class BottomArea extends Component<{ area: Area<any, Panel> }> {
shouldComponentUpdate() {
return false;
}
render() {
const { area } = this.props;
if (area.isEmpty()) {

View File

@ -25,7 +25,7 @@ class Contents extends Component<{ area: Area }> {
const { area } = this.props;
const top: any[] = [];
const bottom: any[] = [];
area.container.items.sort((a, b) => {
area.container.items.slice().sort((a, b) => {
const index1 = a.config?.index || 0;
const index2 = b.config?.index || 0;
return index1 === index2 ? 0 : (index1 > index2 ? 1 : -1);

View File

@ -4,17 +4,12 @@ import { observer } from '@ali/lowcode-editor-core';
import Area from '../area';
import { PanelConfig } from '../types';
import Panel from '../widget/panel';
import { Designer } from '@ali/lowcode-designer';
@observer
export default class LeftFixedPane extends Component<{ area: Area<PanelConfig, Panel> }> {
shouldComponentUpdate() {
return false;
}
componentDidUpdate() {
// FIXME: dirty fix, need deep think
this.props.area.skeleton.editor.get(Designer)?.touchOffsetObserver();
this.props.area.skeleton.editor.get('designer')?.touchOffsetObserver();
}
@ -42,10 +37,6 @@ export default class LeftFixedPane extends Component<{ area: Area<PanelConfig, P
@observer
class Contents extends Component<{ area: Area<PanelConfig, Panel> }> {
shouldComponentUpdate() {
return false;
}
render() {
const { area } = this.props;
return <Fragment>{area.container.items.map((panel) => panel.content)}</Fragment>;

View File

@ -6,11 +6,6 @@ import Panel from '../widget/panel';
@observer
export default class LeftFloatPane extends Component<{ area: Area<any, Panel> }> {
shouldComponentUpdate() {
return false;
}
private dispose?: () => void;
private focusing?: Focusable;
@ -118,10 +113,6 @@ export default class LeftFloatPane extends Component<{ area: Area<any, Panel> }>
@observer
class Contents extends Component<{ area: Area<any, Panel> }> {
shouldComponentUpdate() {
return false;
}
render() {
const { area } = this.props;
return (

View File

@ -7,10 +7,6 @@ import Widget from '../widget/widget';
@observer
export default class MainArea extends Component<{ area: Area<any, Panel | Widget> }> {
shouldComponentUpdate() {
return false;
}
render() {
const { area } = this.props;
return (

View File

@ -6,10 +6,6 @@ import Panel from '../widget/panel';
@observer
export default class RightArea extends Component<{ area: Area<any, Panel> }> {
shouldComponentUpdate() {
return false;
}
render() {
const { area } = this.props;
return (

View File

@ -25,7 +25,7 @@ class Contents extends Component<{ area: Area, itemClassName?: string }> {
const left: any[] = [];
const center: any[] = [];
const right: any[] = [];
area.container.items.sort((a, b) => {
area.container.items.slice().sort((a, b) => {
const index1 = a.config?.index || 0;
const index2 = b.config?.index || 0;
return index1 === index2 ? 0 : (index1 > index2 ? 1 : -1);

View File

@ -22,10 +22,6 @@ export class Workbench extends Component<{ skeleton: Skeleton; config?: EditorCo
skeleton.buildFromConfig(config, components);
}
shouldComponentUpdate() {
return false;
}
// componentDidCatch(error: any) {
// globalContext.get(Editor).emit('editor.skeleton.workbench.error', error);
// }

View File

@ -1,5 +1,5 @@
import { ReactNode, createElement } from 'react';
import { obx } from '@ali/lowcode-editor-core';
import { makeObservable, obx } from '@ali/lowcode-editor-core';
import { uniqueId, createContent } from '@ali/lowcode-utils';
import { DockConfig } from '../types';
import { Skeleton } from '../skeleton';
@ -59,6 +59,7 @@ export default class Dock implements IWidget {
}
constructor(readonly skeleton: Skeleton, readonly config: DockConfig) {
makeObservable(this);
const { props = {}, name } = config;
this.name = name;
this.align = props.align;

View File

@ -1,4 +1,4 @@
import { obx, computed } from '@ali/lowcode-editor-core';
import { obx, computed, makeObservable } from '@ali/lowcode-editor-core';
import { uniqueId } from '@ali/lowcode-utils';
import { createElement, ReactNode, ReactInstance } from 'react';
import { Skeleton } from '../skeleton';
@ -77,6 +77,7 @@ export default class PanelDock implements IWidget {
}
constructor(readonly skeleton: Skeleton, readonly config: PanelDockConfig) {
makeObservable(this);
const { content, contentProps, panelProps, name, props } = config;
this.name = name;
this.id = uniqueId(`dock:${name}$`);

View File

@ -1,6 +1,6 @@
import { EventEmitter } from 'events';
import { createElement, ReactNode } from 'react';
import { obx, computed } from '@ali/lowcode-editor-core';
import { obx, computed, makeObservable } from '@ali/lowcode-editor-core';
import { uniqueId, createContent } from '@ali/lowcode-utils';
import { TitleContent } from '@ali/lowcode-types';
import WidgetContainer from './widget-container';
@ -24,7 +24,7 @@ export default class Panel implements IWidget {
private emitter = new EventEmitter();
get actived(): boolean {
@computed get actived(): boolean {
return this._actived;
}
@ -81,6 +81,7 @@ export default class Panel implements IWidget {
private parent?: WidgetContainer;
constructor(readonly skeleton: Skeleton, readonly config: PanelConfig) {
makeObservable(this);
const { name, content, props = {} } = config;
const { hideTitleBar, title, icon, description, help } = props;
this.name = name;
@ -160,13 +161,13 @@ export default class Panel implements IWidget {
return;
}
if (flag) {
this._actived = true;
this.parent?.active(this);
if (this.parent.name === 'leftFloatArea') {
this.skeleton.leftFixedArea.container.unactiveAll();
} else if (this.parent.name === 'leftFixedArea') {
this.skeleton.leftFloatArea.container.unactiveAll();
}
this._actived = true;
this.parent?.active(this);
if (!this.inited) {
this.inited = true;
}

View File

@ -1,6 +1,6 @@
import { obx, computed } from '@ali/lowcode-editor-core';
import { isPanel } from './panel';
import { obx, computed, makeObservable } from '@ali/lowcode-editor-core';
import { hasOwnProperty } from '@ali/lowcode-utils';
import { isPanel } from './panel';
export interface WidgetItem {
name: string;
@ -15,7 +15,7 @@ function isActiveable(obj: any): obj is Activeable {
}
export default class WidgetContainer<T extends WidgetItem = any, G extends WidgetItem = any> {
@obx.val items: T[] = [];
@obx.shallow items: T[] = [];
private maps: { [name: string]: T } = {};
@ -32,8 +32,9 @@ export default class WidgetContainer<T extends WidgetItem = any, G extends Widge
private exclusive: boolean = false,
private checkVisible: () => boolean = () => true,
private defaultSetCurrent: boolean = false,
// eslint-disable-next-line no-empty-function
) {}
) {
makeObservable(this);
}
@computed get visible() {
return this.checkVisible();

View File

@ -1,5 +1,5 @@
import { ReactNode, createElement } from 'react';
import { obx } from '@ali/lowcode-editor-core';
import { makeObservable, obx } from '@ali/lowcode-editor-core';
import { createContent, uniqueId } from '@ali/lowcode-utils';
import { WidgetConfig, IWidgetBaseConfig } from '../types';
import { Skeleton } from '../skeleton';
@ -71,6 +71,7 @@ export default class Widget implements IWidget {
readonly title: TitleContent;
constructor(readonly skeleton: Skeleton, readonly config: WidgetConfig) {
makeObservable(this);
const { props = {}, name } = config;
this.name = name;
this.align = props.align;

View File

@ -1,4 +1,4 @@
import { computed, obx } from '@ali/lowcode-editor-core';
import { computed, makeObservable, obx } from '@ali/lowcode-editor-core';
import {
Designer,
ISensor,
@ -17,12 +17,12 @@ import {
contains,
Node,
} from '@ali/lowcode-designer';
import { uniqueId } from '@ali/lowcode-utils';
import { IEditor } from '@ali/lowcode-types';
import TreeNode from './tree-node';
import { IndentTrack } from './helper/indent-track';
import DwellTimer from './helper/dwell-timer';
import { uniqueId } from '@ali/lowcode-utils';
import { Backup } from './views/backup-pane';
import { IEditor } from '@ali/lowcode-types';
import { ITreeBoard, TreeMaster, getTreeMaster } from './tree-master';
export class OutlineMain implements ISensor, ITreeBoard, IScrollable {
@ -51,6 +51,7 @@ export class OutlineMain implements ISensor, ITreeBoard, IScrollable {
readonly at: string | symbol;
constructor(editor: IEditor, at: string | symbol) {
makeObservable(this);
this.editor = editor;
this.at = at;
let inited = false;

View File

@ -1,4 +1,4 @@
import { computed, obx } from '@ali/lowcode-editor-core';
import { computed, makeObservable, obx } from '@ali/lowcode-editor-core';
import { Designer, isLocationChildrenDetail } from '@ali/lowcode-designer';
import TreeNode from './tree-node';
import { Tree } from './tree';
@ -14,6 +14,7 @@ export class TreeMaster {
readonly designer: Designer;
constructor(designer: Designer) {
makeObservable(this);
this.designer = designer;
let startTime: any;
designer.dragon.onDragstart(() => {
@ -71,7 +72,7 @@ export class TreeMaster {
}
}
@obx.val private boards = new Set<ITreeBoard>();
@obx.shallow private boards = new Set<ITreeBoard>();
addBoard(board: ITreeBoard) {
this.boards.add(board);
@ -81,7 +82,7 @@ export class TreeMaster {
this.boards.delete(board);
}
@computed hasVisibleTreeBoard() {
hasVisibleTreeBoard() {
for (const item of this.boards) {
if (item.visible && item.at !== Backup) {
return true;

View File

@ -1,5 +1,5 @@
import { TitleContent, isI18nData } from '@ali/lowcode-types';
import { computed, obx, intl } from '@ali/lowcode-editor-core';
import { computed, obx, intl, makeObservable } from '@ali/lowcode-editor-core';
import { Node, DocumentModel, isLocationChildrenDetail, LocationChildrenDetail, Designer } from '@ali/lowcode-designer';
import { Tree } from './tree';
@ -35,7 +35,7 @@ export default class TreeNode {
/**
*
*/
@computed isResponseDropping(): boolean {
isResponseDropping(): boolean {
const loc = this.node.document.dropLocation;
if (!loc) {
return false;
@ -43,7 +43,7 @@ export default class TreeNode {
return loc.target === this.node;
}
@computed isFocusingNode(): boolean {
isFocusingNode(): boolean {
const loc = this.node.document.dropLocation;
if (!loc) {
return false;
@ -218,6 +218,7 @@ export default class TreeNode {
readonly tree: Tree;
constructor(tree: Tree, node: Node) {
makeObservable(this);
this.tree = tree;
this.document = node.document;
this.designer = this.document.designer;

View File

@ -1,5 +1,5 @@
import { DocumentModel, Node } from '@ali/lowcode-designer';
import { computed } from '@ali/lowcode-editor-core';
import { computed, makeObservable } from '@ali/lowcode-editor-core';
import TreeNode from './tree-node';
export class Tree {
@ -15,6 +15,7 @@ export class Tree {
}
constructor(readonly document: DocumentModel) {
makeObservable(this);
this.id = document.id;
}

View File

@ -10,10 +10,6 @@ import { IEditor } from '@ali/lowcode-types';
export class OutlinePane extends Component<{ config: any; editor: IEditor }> {
private main = new OutlineMain(this.props.editor, this.props.config.name || this.props.config.pluginKey);
shouldComponentUpdate() {
return false;
}
componentWillUnmount() {
this.main.purge();
}

View File

@ -18,10 +18,6 @@ class ModalTreeNodeView extends Component<{ treeNode: TreeNode }> {
this.modalNodesManager = props.treeNode.document.modalNodesManager;
}
shouldComponentUpdate() {
return false;
}
hideAllNodes() {
this.modalNodesManager.hideModalNodes();
}
@ -57,10 +53,6 @@ class ModalTreeNodeView extends Component<{ treeNode: TreeNode }> {
@observer
export default class RootTreeNodeView extends Component<{ treeNode: TreeNode }> {
shouldComponentUpdate() {
return false;
}
render() {
const { treeNode } = this.props;
const className = classNames('tree-node', {

View File

@ -11,10 +11,6 @@ export default class TreeBranches extends Component<{
treeNode: TreeNode;
isModal?: boolean;
}> {
shouldComponentUpdate() {
return false;
}
render() {
const { treeNode, isModal } = this.props;
const { expanded } = treeNode;
@ -39,10 +35,6 @@ class TreeNodeChildren extends Component<{
treeNode: TreeNode;
isModal?: boolean;
}> {
shouldComponentUpdate() {
return false;
}
render() {
const { treeNode, isModal } = this.props;
const children: any = [];
@ -112,10 +104,6 @@ class TreeNodeChildren extends Component<{
class TreeNodeSlots extends Component<{
treeNode: TreeNode;
}> {
shouldComponentUpdate() {
return false;
}
render() {
const { treeNode } = this.props;
if (!treeNode.hasSlots()) {

View File

@ -10,10 +10,6 @@ export default class TreeNodeView extends Component<{
treeNode: TreeNode;
isModal?: boolean;
}> {
shouldComponentUpdate() {
return false;
}
render() {
const { treeNode, isModal } = this.props;
const className = classNames('tree-node', {

View File

@ -176,10 +176,6 @@ export default class TreeTitle extends Component<{
@observer
class LockBtn extends Component<{ treeNode: TreeNode }> {
shouldComponentUpdate() {
return false;
}
render() {
const { treeNode } = this.props;
return (
@ -199,10 +195,6 @@ class LockBtn extends Component<{ treeNode: TreeNode }> {
@observer
class HideBtn extends Component<{ treeNode: TreeNode }> {
shouldComponentUpdate() {
return false;
}
render() {
const { treeNode } = this.props;
return (
@ -224,10 +216,6 @@ class HideBtn extends Component<{ treeNode: TreeNode }> {
@observer
class ExpandBtn extends Component<{ treeNode: TreeNode }> {
shouldComponentUpdate() {
return false;
}
render() {
const { treeNode } = this.props;
if (!treeNode.expandable) {

View File

@ -17,7 +17,8 @@
"@ali/lowcode-utils": "1.0.66",
"@ali/recore-rax": "^1.2.4",
"@ali/vu-css-style": "^1.0.2",
"@recore/obx": "^1.0.8",
"mobx": "^6.3.0",
"mobx-react": "^7.2.0",
"classnames": "^2.2.6",
"driver-universal": "^3.1.3",
"history": "^5.0.0",
@ -30,7 +31,6 @@
"devDependencies": {
"@alib/build-scripts": "^0.1.18",
"@babel/plugin-transform-react-jsx": "^7.10.4",
"@recore/obx": "^1.0.8",
"@types/classnames": "^2.2.7",
"@types/node": "^13.7.1",
"@types/rax": "^1.0.0",

View File

@ -2,7 +2,7 @@ import { BuiltinSimulatorRenderer, Component, DocumentModel, Node, NodeInstance
import { ComponentSchema, NodeSchema, NpmInfo, RootSchema, TransformStage } from '@ali/lowcode-types';
import { Asset, compatibleLegaoSchema, cursor, isElement, isESModule, isPlainObject, isReactComponent, setNativeSelection } from '@ali/lowcode-utils';
import LowCodeRenderer from '@ali/lowcode-rax-renderer';
import { computed, obx } from '@recore/obx';
import { computed, observable as obx, untracked, makeObservable, configure } from 'mobx';
import DriverUniversal from 'driver-universal';
import { EventEmitter } from 'events';
import { createMemoryHistory, MemoryHistory } from 'history';
@ -17,6 +17,7 @@ import { getClientRects } from './utils/get-client-rects';
import loader from './utils/loader';
import { parseQuery, withQueryParams } from './utils/url';
configure({ enforceActions: 'never' });
const { Instance } = shared;
export interface LibraryMap {
@ -110,6 +111,7 @@ export class DocumentInstance {
private dispose?: () => void;
constructor(readonly container: SimulatorRendererContainer, readonly document: DocumentModel) {
makeObservable(this);
this.dispose = host.autorun(() => {
// sync schema
this._schema = document.export(TransformStage.Render);
@ -270,7 +272,8 @@ export class SimulatorRendererContainer implements BuiltinSimulatorRenderer {
});
const documentInstanceMap = new Map<string, DocumentInstance>();
let initialEntry = '/';
host.autorun(({ firstRun }) => {
let firstRun = true;
host.autorun(() => {
this._documentInstances = host.project.documents.map((doc) => {
let inst = documentInstanceMap.get(doc.id);
if (!inst) {
@ -283,6 +286,7 @@ export class SimulatorRendererContainer implements BuiltinSimulatorRenderer {
const path = host.project.currentDocument ? documentInstanceMap.get(host.project.currentDocument.id)!.path : '/';
if (firstRun) {
initialEntry = path;
firstRun = false;
} else {
if (this.history.location.pathname !== path) {
this.history.replace(path);

View File

@ -22,7 +22,9 @@
"@recore/obx-react": "^1.0.7",
"classnames": "^2.2.6",
"react": "^16",
"react-dom": "^16.7.0"
"react-dom": "^16.7.0",
"mobx": "^6.3.0",
"mobx-react": "^7.2.0"
},
"devDependencies": {
"@alib/build-scripts": "^0.1.18",

View File

@ -1,3 +1,4 @@
import { runInAction } from 'mobx';
import renderer from './renderer';
if (typeof window !== 'undefined') {
@ -5,10 +6,12 @@ if (typeof window !== 'undefined') {
}
window.addEventListener('beforeunload', () => {
(window as any).LCSimulatorHost = null;
renderer.dispose?.();
(window as any).SimulatorRenderer = null;
(window as any).ReactDOM.unmountComponentAtNode(document.getElementById('app'));
runInAction(() => {
(window as any).LCSimulatorHost = null;
renderer.dispose?.();
(window as any).SimulatorRenderer = null;
(window as any).ReactDOM.unmountComponentAtNode(document.getElementById('app'));
});
});
export default renderer;

View File

@ -3,7 +3,7 @@ import { Router, Route, Switch } from 'react-router';
import cn from 'classnames';
import { Node } from '@ali/lowcode-designer';
import LowCodeRenderer from '@ali/lowcode-react-renderer';
import { observer } from '@recore/obx-react';
import { observer } from 'mobx-react';
import { isFromVC, getClosestNode } from '@ali/lowcode-utils';
import { SimulatorRendererContainer, DocumentInstance } from './renderer';
@ -99,10 +99,6 @@ function getDeviceView(view: any, device: string, mode: string) {
@observer
class Layout extends Component<{ rendererContainer: SimulatorRendererContainer }> {
shouldComponentUpdate() {
return false;
}
render() {
const { rendererContainer, children } = this.props;
const { layout } = rendererContainer;
@ -133,10 +129,6 @@ class Renderer extends Component<{
rendererContainer: SimulatorRendererContainer,
documentInstance: DocumentInstance,
}> {
shouldComponentUpdate() {
return false;
}
render() {
const { documentInstance, rendererContainer: renderer } = this.props;
const { container } = documentInstance;

View File

@ -2,7 +2,7 @@ import React, { createElement, ReactInstance } from 'react';
import { render as reactRender } from 'react-dom';
import { host } from './host';
import SimulatorRendererView from './renderer-view';
import { computed, obx, untracked } from '@recore/obx';
import { computed, observable as obx, untracked, makeObservable, configure } from 'mobx';
import { getClientRects } from './utils/get-client-rects';
import { reactFindDOMNodes, FIBER_KEY } from './utils/react-find-dom-nodes';
import {
@ -28,7 +28,10 @@ import Leaf from './builtin-components/leaf';
import { withQueryParams, parseQuery } from './utils/url';
import { supportsQuickPropSetting, getUppermostPropKey, setInstancesProp } from './utils/misc';
const loader = new AssetLoader();
const DELAY_THRESHOLD = 10;
const FULL_RENDER_THRESHOLD = 500;
configure({ enforceActions: 'never' });
export class DocumentInstance {
public instancesMap = new Map<string, ReactInstance[]>();
@ -40,9 +43,18 @@ export class DocumentInstance {
private disposeFunctions: Array<() => void> = [];
constructor(readonly container: SimulatorRendererContainer, readonly document: DocumentModel) {
const documentExportDisposer = host.autorun(() => {
this._schema = document.export(TransformStage.Render);
});
makeObservable(this);
// 标识当前文档导出的状态,用来控制 reaction effect 是否执行
let asleep = false;
const setDocSchema = (value?: any) => {
this._schema = value || document.export(TransformStage.Render);
};
const documentExportDisposer = host.reaction(() => {
return document.export(TransformStage.Render);
}, (value) => {
if (asleep) return;
setDocSchema(value);
}, { delay: DELAY_THRESHOLD, fireImmediately: true });
this.disposeFunctions.push(documentExportDisposer);
let tid: NodeJS.Timeout;
this.disposeFunctions.push(host.onActivityEvent((data: ActivityData, ctx: any) => {
@ -53,7 +65,7 @@ export class DocumentInstance {
if (tid) clearTimeout(tid);
// 临时关闭全量计算 schema 的逻辑,在增量计算结束后,来一次全量计算
documentExportDisposer.$obx.sleep();
asleep = true;
if (data.type === ActivityType.MODIFIED) {
// 对于修改场景,优先判断是否能走「快捷设置」逻辑
if (supportsQuickPropSetting(data, this)) {
@ -69,7 +81,10 @@ export class DocumentInstance {
// FIXME: 待补充逻辑
}
tid = setTimeout(() => documentExportDisposer.$obx.wakeup(true), FULL_RENDER_THRESHOLD);
tid = setTimeout(() => {
asleep = false;
setDocSchema();
}, FULL_RENDER_THRESHOLD);
// TODO: 调试增量模式,打开以下代码
// this._deltaData = data;
// this._deltaMode = true;
@ -246,6 +261,7 @@ export class SimulatorRendererContainer implements BuiltinSimulatorRenderer {
}
constructor() {
makeObservable(this);
this.autoRender = host.autoRender;
this.disposeFunctions.push(host.connect(this, () => {
@ -272,7 +288,8 @@ export class SimulatorRendererContainer implements BuiltinSimulatorRenderer {
}));
const documentInstanceMap = new Map<string, DocumentInstance>();
let initialEntry = '/';
this.disposeFunctions.push(host.autorun(({ firstRun }) => {
let firstRun = true;
this.disposeFunctions.push(host.autorun(() => {
this._documentInstances = host.project.documents.map((doc) => {
let inst = documentInstanceMap.get(doc.id);
if (!inst) {
@ -286,6 +303,7 @@ export class SimulatorRendererContainer implements BuiltinSimulatorRenderer {
: '/';
if (firstRun) {
initialEntry = path;
firstRun = false;
} else if (this.history.location.pathname !== path) {
this.history.replace(path);
}

View File

@ -9,7 +9,7 @@ interface ILiteralObject {
}
export class Env {
@obx.val envs: ILiteralObject = {};
@obx.shallow envs: ILiteralObject = {};
private emitter: EventEmitter;

View File

@ -102,8 +102,8 @@ export class AccordionField extends VEField {
constructor(props: IVEFieldProps) {
super(props);
this._generateClassNames(props);
if (this.props.onExpandChange) {
this.willDetach = this.props.onExpandChange(() => this.forceUpdate());
if (props.onExpandChange) {
this.willDetach = props.onExpandChange(() => this.forceUpdate());
}
}

View File

@ -18,7 +18,7 @@ class DocItem {
constructor(parent, doc, unInitial) {
this.parent = parent;
const { use, ...strings } = doc;
this.doc = obx.val({
this.doc = obx({
type: 'i18n',
...strings,
});

View File

@ -31,9 +31,9 @@
// skip type checking of declaration files
"skipLibCheck": true,
"baseUrl": "./packages",
"useDefineForClassFields": true,
"paths": {
"@ali/lowcode-*": ["./*/src"],
"@ali/visualengine": ["./vision-preset/src"]
"@ali/lowcode-*": ["./*/src"]
},
"outDir": "lib"
},