feat: support prop.autorun

This commit is contained in:
kangwei 2020-05-28 17:35:17 +08:00
parent 8f1a9de33b
commit c0a523584e
5 changed files with 100 additions and 35 deletions

View File

@ -1,4 +1,4 @@
import { obx, computed } from '@ali/lowcode-editor-core'; import { obx, computed, autorun } from '@ali/lowcode-editor-core';
import { import {
isDOMText, isDOMText,
isJSExpression, isJSExpression,
@ -155,17 +155,29 @@ export class Node<Schema extends NodeSchema = NodeSchema> {
this._children = new NodeChildren(this as ParentalNode, this.initialChildren(children)); this._children = new NodeChildren(this as ParentalNode, this.initialChildren(children));
this._children.interalInitParent(); this._children.interalInitParent();
this.props.import(this.transformProps(props || {}), extras); this.props.import(this.transformProps(props || {}), extras);
this.setupAutoruns();
} }
} }
private transformProps(props: any): any { private transformProps(props: any): any {
// FIXME! support PropsList // FIXME! support PropsList
const x = this.document.designer.transformProps(props, this, TransformStage.Init); return this.document.designer.transformProps(props, this, TransformStage.Init);
return x;
// TODO: run transducers in metadata.experimental // TODO: run transducers in metadata.experimental
} }
private autoruns?: Array<() => void>;
private setupAutoruns() {
const autoruns = this.componentMeta.getMetadata().experimental?.autoruns;
if (!autoruns || autoruns.length < 1) {
return;
}
this.autoruns = autoruns.map(item => {
return autorun(() => {
item.autorun(this.props.get(item.name, true) as any)
}, true);
});
}
private initialChildren(children: any): NodeData[] { private initialChildren(children: any): NodeData[] {
// FIXME! this is dirty code // FIXME! this is dirty code
if (children == null) { if (children == null) {
@ -591,6 +603,7 @@ export class Node<Schema extends NodeSchema = NodeSchema> {
if (this.isParental()) { if (this.isParental()) {
this.children.purge(); this.children.purge();
} }
this.autoruns?.forEach(dispose => dispose());
this.props.purge(); this.props.purge();
this.document.internalRemoveAndPurgeNode(this); this.document.internalRemoveAndPurgeNode(this);
} }
@ -608,7 +621,16 @@ export class Node<Schema extends NodeSchema = NodeSchema> {
insertBefore(node: Node, ref?: Node) { insertBefore(node: Node, ref?: Node) {
this.children?.insert(node, ref ? ref.index : null); this.children?.insert(node, ref ? ref.index : null);
} }
insertAfter(node: Node, ref?: Node) { insertAfter(node: any, ref?: Node) {
if (!isNode(node)) {
if (node.getComponentName) {
node = this.document.createNode({
componentName: node.getComponentName(),
});
} else {
node = this.document.createNode(node);
}
}
this.children?.insert(node, ref ? ref.index + 1 : null); this.children?.insert(node, ref ? ref.index + 1 : null);
} }
getParent() { getParent() {
@ -687,6 +709,7 @@ export class Node<Schema extends NodeSchema = NodeSchema> {
* @deprecated * @deprecated
*/ */
getSuitablePlace(node: Node, ref: any): any { getSuitablePlace(node: Node, ref: any): any {
// TODO:
if (this.isRoot()) { if (this.isRoot()) {
return { container: this, ref }; return { container: this, ref };
} }

View File

@ -1,6 +1,7 @@
import { obx, autorun, untracked, computed } from '@ali/lowcode-editor-core'; import { obx, autorun, untracked, computed } from '@ali/lowcode-editor-core';
import { Prop, IPropParent, UNSET } from './prop'; import { Prop, IPropParent, UNSET } from './prop';
import { Props } from './props'; import { Props } from './props';
import { Node } from '../node';
export type PendingItem = Prop[]; export type PendingItem = Prop[];
export class PropStash implements IPropParent { export class PropStash implements IPropParent {
@ -15,8 +16,10 @@ export class PropStash implements IPropParent {
return maps; return maps;
} }
private willPurge: () => void; private willPurge: () => void;
readonly owner: Node;
constructor(readonly props: Props, write: (item: Prop) => void) { constructor(readonly props: Props, write: (item: Prop) => void) {
this.owner = props.owner;
this.willPurge = autorun(() => { this.willPurge = autorun(() => {
if (this.space.size < 1) { if (this.space.size < 1) {
return; return;

View File

@ -4,7 +4,7 @@ import { uniqueId, isPlainObject, hasOwnProperty } from '@ali/lowcode-utils';
import { PropStash } from './prop-stash'; import { PropStash } from './prop-stash';
import { valueToSource } from './value-to-source'; import { valueToSource } from './value-to-source';
import { Props } from './props'; import { Props } from './props';
import { SlotNode } from '../node'; import { SlotNode, Node } from '../node';
import { TransformStage } from '../transform-stage'; import { TransformStage } from '../transform-stage';
export const UNSET = Symbol.for('unset'); export const UNSET = Symbol.for('unset');
@ -13,12 +13,14 @@ export type UNSET = typeof UNSET;
export interface IPropParent { export interface IPropParent {
delete(prop: Prop): void; delete(prop: Prop): void;
readonly props: Props; readonly props: Props;
readonly owner: Node;
} }
export type ValueTypes = 'unset' | 'literal' | 'map' | 'list' | 'expression' | 'slot'; export type ValueTypes = 'unset' | 'literal' | 'map' | 'list' | 'expression' | 'slot';
export class Prop implements IPropParent { export class Prop implements IPropParent {
readonly isProp = true; readonly isProp = true;
readonly owner: Node;
/** /**
* @see SettingTarget * @see SettingTarget
@ -350,6 +352,7 @@ export class Prop implements IPropParent {
key?: string | number, key?: string | number,
spread = false, spread = false,
) { ) {
this.owner = parent.owner;
this.props = parent.props; this.props = parent.props;
if (value !== UNSET) { if (value !== UNSET) {
this.setValue(value); this.setValue(value);
@ -613,6 +616,10 @@ export class Prop implements IPropParent {
getProps() { getProps() {
return this.parent; return this.parent;
} }
getNode() {
return this.owner;
}
} }
export function isProp(obj: any): obj is Prop { export function isProp(obj: any): obj is Prop {

View File

@ -1,6 +1,7 @@
import { ComponentType, ReactElement, isValidElement, ComponentClass } from 'react'; import { ComponentType, ReactElement, isValidElement, ComponentClass } from 'react';
import { isPlainObject } from '@ali/lowcode-utils'; import { isPlainObject } from '@ali/lowcode-utils';
import { isI18nData, SettingTarget, InitialItem, FilterItem, isJSSlot, isJSExpression } from '@ali/lowcode-types'; import { isI18nData, SettingTarget, InitialItem, FilterItem, isJSSlot, isJSExpression, AutorunItem } from '@ali/lowcode-types';
import { untracked } from '@ali/lowcode-editor-core';
type Field = SettingTarget; type Field = SettingTarget;
@ -290,19 +291,44 @@ export function upgradePropConfig(config: OldPropConfig, collector: ConfigCollec
}; };
} }
if (accessor && !slotName) { if (!slotName) {
extraProps.getValue = (field: Field, fieldValue: any) => { if (accessor) {
return accessor.call(field, fieldValue); extraProps.getValue = (field: Field, fieldValue: any) => {
}; return accessor.call(field, fieldValue);
if (!initialFn) { };
initialFn = accessor; }
if (sync || accessor) {
collector.addAutorun({
name,
autorun: (field: Field) => {
let fieldValue = untracked(() => field.getValue());
if (accessor) {
fieldValue = accessor.call(field, fieldValue)
}
if (sync) {
fieldValue = sync.call(field, fieldValue);
if (fieldValue !== undefined) {
field.setValue(fieldValue);
}
} else {
field.setValue(fieldValue);
}
}
});
}
if (mutator) {
extraProps.setValue = (field: Field, value: any) => {
mutator.call(field, value, value);
};
} }
} }
const setterInitial = getInitialFromSetter(setter); const setterInitial = getInitialFromSetter(setter);
collector.addInitial({ collector.addInitial({
// FIXME! name should be "xxx.xxx" // FIXME! name could be "xxx.xxx"
name: slotName || name, name: slotName || name,
initial: (field: Field, currentValue: any) => { initial: (field: Field, currentValue: any) => {
// FIXME! read from prototype.defaultProps // FIXME! read from prototype.defaultProps
@ -345,20 +371,6 @@ export function upgradePropConfig(config: OldPropConfig, collector: ConfigCollec
}); });
} }
if (sync) {
extraProps.autorun = (field: Field) => {
const value = sync.call(field, field.getValue());
if (value !== undefined) {
field.setValue(value);
}
};
}
if (mutator && !slotName) {
extraProps.setValue = (field: Field, value: any) => {
mutator.call(field, value, value);
};
}
if (slotName) { if (slotName) {
newConfig.name = slotName; newConfig.name = slotName;
if (!newConfig.title && slotTitle) { if (!newConfig.title && slotTitle) {
@ -396,7 +408,6 @@ export function upgradePropConfig(config: OldPropConfig, collector: ConfigCollec
let primarySetter: any; let primarySetter: any;
if (type === 'composite') { if (type === 'composite') {
const initials: InitialItem[] = []; const initials: InitialItem[] = [];
const filters: FilterItem[] = [];
const objItems = items const objItems = items
? upgradeConfigure(items, ? upgradeConfigure(items,
{ {
@ -404,8 +415,17 @@ export function upgradePropConfig(config: OldPropConfig, collector: ConfigCollec
initials.push(item); initials.push(item);
}, },
addFilter: (item) => { addFilter: (item) => {
filters.push(item); collector.addFilter({
} name: `${name}.${item.name}`,
filter: item.filter,
});
},
addAutorun: (item) => {
collector.addAutorun({
name: `${name}.${item.name}`,
autorun: item.autorun,
});
},
} }
) )
: []; : [];
@ -481,10 +501,12 @@ export function upgradePropConfig(config: OldPropConfig, collector: ConfigCollec
type AddInitial = (initialItem: InitialItem) => void; type AddInitial = (initialItem: InitialItem) => void;
type AddFilter = (filterItem: FilterItem) => void; type AddFilter = (filterItem: FilterItem) => void;
type AddAutorun = (autorunItem: AutorunItem) => void;
type ConfigCollector = { type ConfigCollector = {
addInitial: AddInitial, addInitial: AddInitial;
addFilter: AddFilter, addFilter: AddFilter;
addAutorun: AddAutorun;
} }
function getInitialFromSetter(setter: any) { function getInitialFromSetter(setter: any) {
@ -750,6 +772,7 @@ export function upgradeMetadata(oldConfig: OldPrototypeConfig) {
const initials: InitialItem[] = []; const initials: InitialItem[] = [];
const filters: FilterItem[] = []; const filters: FilterItem[] = [];
const autoruns: AutorunItem[] = [];
const props = upgradeConfigure(configure || [], const props = upgradeConfigure(configure || [],
{ {
addInitial: (item) => { addInitial: (item) => {
@ -758,10 +781,14 @@ export function upgradeMetadata(oldConfig: OldPrototypeConfig) {
addFilter: (item) => { addFilter: (item) => {
filters.push(item); filters.push(item);
}, },
addAutorun: (item) => {
autoruns.push(item);
}
} }
); );
experimental.initials = initials; experimental.initials = initials;
experimental.filters = filters; experimental.filters = filters;
experimental.autoruns = autoruns;
const supports: any = {}; const supports: any = {};
if (canUseCondition != null) { if (canUseCondition != null) {

View File

@ -49,6 +49,11 @@ export interface FilterItem {
name: string; name: string;
filter: (target: SettingTarget, currentValue: any) => any; filter: (target: SettingTarget, currentValue: any) => any;
} }
export interface AutorunItem {
name: string;
autorun: (target: SettingTarget) => any;
}
export interface Experimental { export interface Experimental {
context?: { [contextInfoName: string]: any }; context?: { [contextInfoName: string]: any };
@ -57,8 +62,8 @@ export interface Experimental {
transducers?: any; // ? should support transducers?: any; // ? should support
initials?: InitialItem[]; initials?: InitialItem[];
filters?: FilterItem[]; filters?: FilterItem[];
autoruns?: AutorunItem[];
callbacks?: Callbacks; callbacks?: Callbacks;
// TODO: thinkof function
initialChildren?: NodeData[] | ((target: SettingTarget) => NodeData[]); initialChildren?: NodeData[] | ((target: SettingTarget) => NodeData[]);
// 样式 及 位置handle上必须有明确的标识以便事件路由判断或者主动设置事件独占模式 // 样式 及 位置handle上必须有明确的标识以便事件路由判断或者主动设置事件独占模式