complete parse props

This commit is contained in:
kangwei 2020-03-15 18:09:46 +08:00
parent 5f569cc1ca
commit d8003720a2
15 changed files with 358 additions and 374 deletions

View File

@ -31,9 +31,8 @@ import {
import { isNodeSchema, NodeSchema } from '../../../designer/schema'; import { isNodeSchema, NodeSchema } from '../../../designer/schema';
import { ComponentMetadata } from '../../../designer/component-meta'; import { ComponentMetadata } from '../../../designer/component-meta';
import { ReactInstance } from 'react'; import { ReactInstance } from 'react';
import { setNativeSelection } from '../../../designer/helper/navtive-selection';
import cursor from '../../../designer/helper/cursor';
import { isRootNode } from '../../../designer/document/node/root-node'; import { isRootNode } from '../../../designer/document/node/root-node';
import { parseProps } from '../utils/parse-props';
export interface SimulatorProps { export interface SimulatorProps {
// 从 documentModel 上获取 // 从 documentModel 上获取
@ -333,12 +332,26 @@ export class SimulatorHost implements ISimulator<SimulatorProps> {
* @see ISimulator * @see ISimulator
*/ */
generateComponentMetadata(componentName: string): ComponentMetadata { generateComponentMetadata(componentName: string): ComponentMetadata {
// if html tags
if (isHTMLTag(componentName)) {
return {
componentName,
// TODO: read builtins html metadata
};
}
const component = this.getComponent(componentName); const component = this.getComponent(componentName);
if (component) {
parseProps(component as any);
}
// TODO: // TODO:
// 1. generate builtin div/p/h1/h2 // 1. generate builtin div/p/h1/h2
// 2. read propTypes // 2. read propTypes
return { return {
componentName, componentName,
props: parseProps(this.getComponent(componentName)),
}; };
} }
@ -346,7 +359,7 @@ export class SimulatorHost implements ISimulator<SimulatorProps> {
* @see ISimulator * @see ISimulator
*/ */
getComponent(componentName: string): Component | null { getComponent(componentName: string): Component | null {
return null; return this.renderer?.getComponent(componentName) || null;
} }
@obx.val private instancesMap = new Map<string, ReactInstance[]>(); @obx.val private instancesMap = new Map<string, ReactInstance[]>();
@ -933,6 +946,10 @@ export class SimulatorHost implements ISimulator<SimulatorProps> {
// #endregion // #endregion
} }
function isHTMLTag(name: string) {
return /^[a-z]\w*$/.test(name);
}
function isPointInRect(point: CanvasPoint, rect: Rect) { function isPointInRect(point: CanvasPoint, rect: Rect) {
return ( return (
point.canvasY >= rect.top && point.canvasY >= rect.top &&

View File

@ -7,7 +7,6 @@ import { RootSchema, NpmInfo } from '../../../designer/schema';
import { getClientRects } from '../../../utils/get-client-rects'; import { getClientRects } from '../../../utils/get-client-rects';
import { Asset } from '../utils/asset'; import { Asset } from '../utils/asset';
import loader from '../utils/loader'; import loader from '../utils/loader';
import { ComponentMetadata } from '../../../designer/component-meta';
import { reactFindDOMNodes, FIBER_KEY } from '../utils/react-find-dom-nodes'; import { reactFindDOMNodes, FIBER_KEY } from '../utils/react-find-dom-nodes';
import { isESModule } from '../../../../../utils/is-es-module'; import { isESModule } from '../../../../../utils/is-es-module';
import { NodeInstance } from '../../../designer/simulator'; import { NodeInstance } from '../../../designer/simulator';
@ -68,7 +67,7 @@ export class SimulatorRenderer {
private buildComponents() { private buildComponents() {
this._components = buildComponents(this._componentsMap); this._components = buildComponents(this._componentsMap);
} }
@obx.ref private _components = {}; @obx.ref private _components: any = {};
@computed get components(): object { @computed get components(): object {
// 根据 device 选择不同组件,进行响应式 // 根据 device 选择不同组件,进行响应式
// 更好的做法是,根据 device 选择加载不同的组件资源,甚至是 simulatorUrl // 更好的做法是,根据 device 选择加载不同的组件资源,甚至是 simulatorUrl
@ -175,6 +174,27 @@ export class SimulatorRenderer {
this.ctxMap.set(id, ctx); this.ctxMap.set(id, ctx);
} }
getComponent(componentName: string) {
const paths = componentName.split('.');
const subs: string[] = [];
while (true) {
const component = this._components[componentName];
if (component) {
return getSubComponent(component, subs);
}
const sub = paths.pop();
if (!sub) {
return null;
}
subs.unshift(sub);
componentName = paths.join('.');
}
return null;
}
getComponentInstances(id: string): ReactInstance[] | null { getComponentInstances(id: string): ReactInstance[] | null {
return this.instancesMap.get(id) || null; return this.instancesMap.get(id) || null;
} }

View File

@ -0,0 +1,200 @@
import PropTypes from 'prop-types';
import { isValidElement } from 'react';
import { isElement } from '../../../utils/is-element';
import { PropType, PropConfig } from '../../../designer/prop-config';
export const primitiveTypes = [
'string',
'number',
'array',
'bool',
'func',
'object',
'node',
'element',
'symbol',
'any',
];
function makeRequired(propType: any, lowcodeType: string | object) {
function lowcodeCheckTypeIsRequired(...rest: any[]) {
return propType.isRequired(...rest);
}
if (typeof lowcodeType === 'string') {
lowcodeType = {
type: lowcodeType,
};
}
lowcodeCheckTypeIsRequired.lowcodeType = {
...lowcodeType,
isRequired: true,
};
return lowcodeCheckTypeIsRequired;
}
function define(propType: any = PropTypes.any, lowcodeType: string | object = {}) {
if (!propType._inner && propType.name !== 'lowcodeCheckType') {
propType.lowcodeType = lowcodeType;
}
function lowcodeCheckType(...rest: any[]) {
return propType(...rest);
}
lowcodeCheckType.lowcodeType = lowcodeType;
lowcodeCheckType.isRequired = makeRequired(propType, lowcodeType);
return lowcodeCheckType;
}
const LowcodeTypes: any = {
...PropTypes,
define,
};
(window as any).PropTypes = LowcodeTypes;
(window as any).React.PropTypes = LowcodeTypes;
// override primitive type chechers
primitiveTypes.forEach(type => {
const propType = (PropTypes as any)[type];
if (!propType) {
return;
}
propType._inner = true;
LowcodeTypes[type] = define(propType, type);
});
// You can ensure that your prop is limited to specific values by treating
// it as an enum.
LowcodeTypes.oneOf = (list: any[]) => {
return define(PropTypes.oneOf(list), {
type: 'oneOf',
value: list,
});
};
// An array of a certain type
LowcodeTypes.arrayOf = (type: any) => {
return define(PropTypes.arrayOf(type), {
type: 'arrayOf',
value: type.lowcodeType || 'any',
});
};
// An object with property values of a certain type
LowcodeTypes.objectOf = (type: any) => {
return define(PropTypes.objectOf(type), {
type: 'objectOf',
value: type.lowcodeType || 'any',
});
};
// An object that could be one of many types
LowcodeTypes.oneOfType = (types: any[]) => {
const itemTypes = types.map(type => type.lowcodeType || 'any');
return define(PropTypes.oneOfType(types), {
type: 'oneOfType',
value: itemTypes,
});
};
// An object with warnings on extra properties
LowcodeTypes.exact = (typesMap: any) => {
const configs = Object.keys(typesMap).map(key => {
return {
name: key,
propType: typesMap[key].lowcodeType || 'any',
};
});
return define(PropTypes.exact(typesMap), {
type: 'exact',
value: configs,
});
};
// An object taking on a particular shape
LowcodeTypes.shape = (typesMap: any) => {
const configs = Object.keys(typesMap).map(key => {
return {
name: key,
propType: typesMap[key].lowcodeType || 'any',
};
});
return define(PropTypes.shape(typesMap), {
type: 'shape',
value: configs,
});
};
const BasicTypes = ['string', 'number', 'object'];
export function parseProps(component: any): PropConfig[] {
if (!component) {
return [];
}
const propTypes = component.propTypes || ({} as any);
const defaultProps = component.defaultProps || ({} as any);
const result: any = {};
if (!propTypes) return [];
Object.keys(propTypes).forEach(key => {
const propTypeItem = propTypes[key];
const defaultValue = defaultProps[key];
const lowcodeType = propTypeItem.lowcodeType;
if (lowcodeType) {
result[key] = {
name: key,
propType: lowcodeType,
};
if (defaultValue != null) {
result[key].defaultValue = defaultValue;
}
return;
}
let i = primitiveTypes.length;
while (i-- > 0) {
const k = primitiveTypes[i];
if ((LowcodeTypes as any)[k] === propTypeItem) {
result[key] = {
name: key,
propType: k,
};
if (defaultValue != null) {
result[key].defaultValue = defaultValue;
}
return;
}
}
result[key] = {
name: key,
propType: 'any',
};
if (defaultValue != null) {
result[key].defaultValue = defaultValue;
}
});
Object.keys(defaultProps).forEach(key => {
if (result[key]) return;
const defaultValue = defaultProps[key];
let type: string = typeof defaultValue;
if (type === 'boolean') {
type = 'bool';
} else if (type === 'function') {
type = 'func';
} else if (type === 'object' && Array.isArray(defaultValue)) {
type = 'array';
} else if (defaultValue && isValidElement(defaultValue)) {
type = 'node';
} else if (defaultValue && isElement(defaultValue)) {
type = 'element';
} else if (!BasicTypes.includes(type)) {
type = 'any';
}
result[key] = {
name: key,
propType: type || 'any',
defaultValue,
};
});
return Object.keys(result).map(key => result[key]);
}

View File

@ -1,198 +0,0 @@
import PropTypes from 'prop-types';
export const primitiveTypeMaps = {
string: {
defaultValue: '',
display: 'inline',
setter: 'TextSetter',
},
number: {
display: 'inline',
setter: 'NumberSetter' // extends TextSetter
},
array: {
defaultValue: [],
display: 'inline',
// itemType: any
setter: 'ArraySetter' // extends ExpressionSetter
},
bool: {
defaultValue: false,
display: 'inline',
setter: 'BoolSetter'
},
func: {
defaultValue: () => {},
display: 'inline',
setter: 'FunctionSetter' // extends ExpressionSetter
},
object: {
defaultValue: {},
display: 'inline',
// itemType: any
setter: 'ObjectSetter' // extends ExpressionSetter
},
// Anything that can be rendered: numbers, strings, elements or an array
// (or fragment) containing these types.
node: {
defaultValue: '',
display: 'inline',
setter: 'FragmentSetter',
},
// A React element.
element: {
display: 'inline',
setter: 'JSXSetter', // extends ExpressionSetter
},
symbol: {
display: 'inline',
setter: 'ExpressionSetter',
},
any: {
display: 'inline',
setter: 'ExpressionSetter',
}
};
function makeRequired(propType, visionType) {
function visionCheckTypeIsRequired(...rest) {
return propType.isRequired(...rest);
}
visionCheckTypeIsRequired.visionType = {
...visionType,
required: true,
};
return visionCheckTypeIsRequired;
}
function define(propType = PropTypes.any, visionType = {}) {
if (!propType._inner && propType.name !== 'visionCheckType') {
propType.visionType = visionType;
}
function visionCheckType(...rest) {
return propType(...rest);
}
visionCheckType.visionType = visionType;
visionCheckType.isRequired = makeRequired(propType, visionType);
return visionCheckType;
}
const VisionTypes = {
...PropTypes,
define,
};
export default VisionTypes;
// override primitive type chechers
Object.keys(primitiveTypeMaps).forEach((type) => {
const propType = PropTypes[type];
if (!propType) {
return;
}
propType._inner = true;
VisionTypes[type] = define(propType, primitiveTypeMaps[type]);
});
// You can ensure that your prop is limited to specific values by treating
// it as an enum.
VisionTypes.oneOf = (list) => {
return define(PropTypes.oneOf(list), {
defaultValue: list && list[0],
display: 'inline',
setter: {
type: 'SelectSetter',
options: list,
},
});
};
// An array of a certain type
VisionTypes.arrayOf = (type) => {
return define(PropTypes.arrayOf(type), {
defaultValue: [],
display: 'inline',
setter: {
type: 'ArraySetter', // list
itemType: type.visionType || primitiveTypeMaps.any, // addable type
}
});
};
// An object with property values of a certain type
VisionTypes.objectOf = (type) => {
return define(PropTypes.objectOf(type), {
defaultValue: {},
display: 'inline',
setter: {
type: 'ObjectSetter', // all itemType
itemType: type.visionType || primitiveTypeMaps.any, // addable type
}
});
};
// An object that could be one of many types
VisionTypes.oneOfType = (types) => {
const itemType = types.map(type => type.visionType || primitiveTypeMaps.any);
return define(PropTypes.oneOfType(types), {
defaultValue: itemType[0] && itemType[0].defaultValue,
display: 'inline',
setter: {
type: 'OneOfTypeSetter',
itemType, // addable type
},
});
};
// You can also declare that a prop is an instance of a class. This uses
// JS's instanceof operator.
VisionTypes.instanceOf = (classType) => {
return define(PropTypes.instanceOf(classType), {
display: 'inline',
setter: 'ExpressionSetter',
});
};
// An object with warnings on extra properties
VisionTypes.exact = (typesMap) => {
const exactTypes = {};
const defaultValue = {};
Object.keys(typesMap).forEach(key => {
exactTypes[key] = typesMap[key].visionType || primitiveTypeMaps.any;
defaultValue[key] = exactTypes[key].defaultValue;
});
return define(PropTypes.exact(typesMap), {
defaultValue,
display: 'inline',
setter: {
type: 'ObjectSetter', // all itemType
exactTypes,
},
});
}
// An object taking on a particular shape
VisionTypes.shape = (typesMap) => {
const exactTypes = {};
const defaultValue = {};
Object.keys(typesMap).forEach(key => {
exactTypes[key] = typesMap[key].visionType || primitiveTypeMaps.any;
defaultValue[key] = exactTypes[key].defaultValue;
});
return define(PropTypes.shape(typesMap), {
defaultValue,
display: 'inline',
setter: {
type: 'ObjectSetter', // all itemType
exactTypes,
itemType: primitiveTypeMaps.any, // addable type
},
});
};
// color
// time
// date
// range

View File

@ -102,7 +102,7 @@ export default class DocumentModel {
let schema: any; let schema: any;
if (isDOMText(data) || isJSExpression(data)) { if (isDOMText(data) || isJSExpression(data)) {
schema = { schema = {
componentName: '#frag', componentName: 'Leaf',
children: data, children: data,
}; };
} else { } else {
@ -281,6 +281,7 @@ export default class DocumentModel {
// TODO: emit simulator mounted // TODO: emit simulator mounted
} }
// FIXME: does needed?
getComponent(componentName: string): any { getComponent(componentName: string): any {
return this.simulator!.getComponent(componentName); return this.simulator!.getComponent(componentName);
} }

View File

@ -1,90 +0,0 @@
import { obx, computed } from '@recore/obx';
import { JSExpression, isJSExpression } from '../../schema';
export default class NodeContent {
@obx.ref private _value: string | JSExpression = '';
@computed get value(): string | JSExpression {
return this._value;
}
set value(val: string | JSExpression) {
this._value = val;
}
/**
*
*/
@computed get code() {
if (isJSExpression(this._value)) {
return this._value.value;
}
return JSON.stringify(this.value);
}
/**
*
*/
set code(code: string) {
if (isJSExpression(this._value)) {
this._value = {
...this._value,
value: code,
};
} else {
let useCode: boolean = true;
try {
const v = JSON.parse(code);
const t = typeof v;
if (v == null) {
this._value = '';
useCode = false;
} else if (t === 'string' || t === 'number' || t === 'boolean') {
this._value = String(v);
useCode = false;
}
} catch (e) {
// ignore
}
if (useCode) {
this._value = {
type: 'JSExpression',
value: code,
mock: this._value,
};
}
}
}
constructor(value: any) {
this.import(value);
}
import(value: any) {
const type = typeof value;
if (value == null) {
this._value = '';
} else if (type === 'string' || type === 'number' || type === 'boolean') {
this._value = String(value);
} else if (isJSExpression(value)) {
this._value = value;
}
}
/**
*
*/
@computed isJSExpression(): boolean {
return isJSExpression(this._value);
}
/**
*
*/
@computed isEmpty() {
if (isJSExpression(this._value)) {
return this._value.value === '';
}
return this._value === '';
}
}

View File

@ -1,11 +1,9 @@
import { obx, computed, untracked } from '@recore/obx'; import { obx, computed } from '@recore/obx';
import { NodeSchema, NodeData, PropsMap, PropsList } from '../../schema'; import { NodeSchema, NodeData, PropsMap, PropsList, isDOMText, isJSExpression } from '../../schema';
import Props, { EXTRA_KEY_PREFIX } 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';
import NodeContent from './node-content';
import { Component } from '../../simulator';
import { ComponentMeta } from '../../component-meta'; import { ComponentMeta } from '../../component-meta';
/** /**
@ -48,12 +46,12 @@ export default class Node {
* * Component / * * Component /
*/ */
readonly componentName: string; readonly componentName: string;
protected _props?: Props; /**
protected _children: NodeChildren | NodeContent; *
*/
readonly props: Props;
protected _children?: NodeChildren;
@obx.ref private _parent: NodeParent | null = null; @obx.ref private _parent: NodeParent | null = null;
get props(): Props | undefined {
return this._props;
}
/** /**
* *
*/ */
@ -63,8 +61,8 @@ export default class Node {
/** /**
* *
*/ */
get children(): NodeChildren | NodeContent { get children(): NodeChildren | null {
return this._children; return this._children || null;
} }
/** /**
* *
@ -99,19 +97,23 @@ export default class Node {
this.id = id || `node$${document.nextId()}`; this.id = id || `node$${document.nextId()}`;
this.componentName = componentName; this.componentName = componentName;
this._slotFor = slotFor; this._slotFor = slotFor;
let _props: Props;
if (isNodeParent(this)) { if (isNodeParent(this)) {
this._props = new Props(this, props, extras); _props = new Props(this, props, extras);
this._children = new NodeChildren(this as NodeParent, children || []); this._children = new NodeChildren(this as NodeParent, children || []);
} else { } else {
this._children = new NodeContent(children); _props = new Props(this, {
children: isDOMText(children) || isJSExpression(children) ? children : '',
});
} }
this.props = _props;
} }
/** /**
* *
*/ */
get isNodeParent(): boolean { get isNodeParent(): boolean {
return this.componentName.charAt(0) !== '#'; return this.componentName !== 'Leaf';
} }
/** /**
@ -165,16 +167,6 @@ export default class Node {
} }
} }
/**
*
*/
@obx.ref get component(): Component {
if (this.isNodeParent) {
return this.document.getComponent(this.componentName) || this.componentName;
}
return this.componentName;
}
/** /**
* *
*/ */
@ -186,7 +178,7 @@ export default class Node {
if (!this.isNodeParent || this.componentName === 'Fragment') { if (!this.isNodeParent || this.componentName === 'Fragment') {
return null; return null;
} }
return this.props?.export(true).props || null; return this.props.export(true).props || null;
} }
private _conditionGroup: string | null = null; private _conditionGroup: string | null = null;
@ -231,12 +223,12 @@ export default class Node {
// //
} }
getProp(path: string, useStash = true): Prop | null { getProp(path: string, stash = true): Prop | null {
return this.props?.query(path, useStash as any) || null; return this.props.query(path, stash as any) || null;
} }
getExtraProp(key: string, useStash = true): Prop | null { getExtraProp(key: string, stash = true): Prop | null {
return this.props?.get(EXTRA_KEY_PREFIX + key, useStash) || null; return this.props.get(EXTRA_KEY_PREFIX + key, stash) || null;
} }
/** /**
@ -257,14 +249,14 @@ export default class Node {
* *
*/ */
mergeProps(props: PropsMap) { mergeProps(props: PropsMap) {
this.props?.merge(props); this.props.merge(props);
} }
/** /**
* *
*/ */
setProps(props?: PropsMap | PropsList | null) { setProps(props?: PropsMap | PropsList | null) {
this.props?.import(props); this.props.import(props);
} }
/** /**
@ -320,10 +312,10 @@ export default class Node {
const { componentName, id, children, props, ...extras } = data; const { componentName, id, children, props, ...extras } = data;
if (isNodeParent(this)) { if (isNodeParent(this)) {
this._props!.import(props, extras); this.props.import(props, extras);
this._children.import(children, checkId); (this._children as NodeChildren).import(children, checkId);
} else { } else {
this._children.import(children); this.props.get('children', true)!.setValue(isDOMText(children) || isJSExpression(children) ? children : '');
} }
} }
@ -332,22 +324,30 @@ export default class Node {
* @param serialize id * @param serialize id
*/ */
export(serialize = false): NodeSchema { export(serialize = false): NodeSchema {
const { props, extras } = this.props?.export(serialize) || {}; const baseSchema: any = {
const schema: any = {
componentName: this.componentName, componentName: this.componentName,
};
if (serialize) {
baseSchema.id = this.id;
}
if (!isNodeParent(this)) {
baseSchema.children = this.props.get('children')?.export(serialize);
return baseSchema;
}
const { props, extras } = this.props.export(serialize) || {};
const schema: any = {
...baseSchema,
props, props,
...extras, ...extras,
}; };
if (serialize) {
schema.id = this.id;
}
if (isNodeParent(this)) {
if (this.children.size > 0) { if (this.children.size > 0) {
schema.children = this.children.export(serialize); schema.children = this.children.export(serialize);
} }
} else {
schema.children = (this.children as NodeContent).value;
}
return schema; return schema;
} }
@ -400,7 +400,7 @@ export default class Node {
if (isNodeParent(this)) { if (isNodeParent(this)) {
this.children.purge(); this.children.purge();
} }
this.props?.purge(); this.props.purge();
this.document.internalRemoveAndPurgeNode(this); this.document.internalRemoveAndPurgeNode(this);
} }
} }

View File

@ -16,16 +16,18 @@ export interface IPropParent {
readonly props: Props; readonly props: Props;
} }
export type ValueTypes = 'unset' | 'literal' | 'map' | 'list' | 'expression' | 'slot';
export default class Prop implements IPropParent { export default class Prop implements IPropParent {
readonly isProp = true; readonly isProp = true;
readonly id = uniqueId('prop$'); readonly id = uniqueId('prop$');
@obx.ref private _type: 'unset' | 'literal' | 'map' | 'list' | 'expression' | 'slot' = 'unset'; @obx.ref private _type: ValueTypes = 'unset';
/** /**
* *
*/ */
get type(): 'unset' | 'literal' | 'map' | 'list' | 'expression' | 'slot' { get type(): ValueTypes {
return this._type; return this._type;
} }
@ -83,6 +85,7 @@ export default class Prop implements IPropParent {
return null; return null;
} }
private _code: string | null = null;
/** /**
* *
*/ */
@ -90,13 +93,41 @@ export default class Prop implements IPropParent {
if (isJSExpression(this.value)) { if (isJSExpression(this.value)) {
return this.value.value; return this.value.value;
} }
// todo: JSFunction ...
if (this.type === 'slot') { if (this.type === 'slot') {
return JSON.stringify(this._slotNode!.export(false)); return JSON.stringify(this._slotNode!.export(false));
} }
return JSON.stringify(this.value); return this._code != null ? this._code : JSON.stringify(this.value);
} }
set code(val) {
// todo /**
*
*/
set code(code: string) {
if (isJSExpression(this._value)) {
this.setValue({
...this._value,
value: code,
});
this._code = code;
return;
}
try {
const v = JSON.parse(code);
this.setValue(v);
this._code = code;
return;
} catch (e) {
// ignore
}
this.setValue({
type: 'JSExpression',
value: code,
mock: this._value,
});
this._code = code;
} }
@computed getAsString(): string { @computed getAsString(): string {
@ -111,6 +142,7 @@ export default class Prop implements IPropParent {
*/ */
setValue(val: CompositeValue) { setValue(val: CompositeValue) {
this._value = val; this._value = val;
this._code = null;
const t = typeof val; const t = typeof val;
if (val == null) { if (val == null) {
this._value = null; this._value = null;
@ -174,7 +206,7 @@ export default class Prop implements IPropParent {
this._slotNode?.internalSetParent(null); this._slotNode?.internalSetParent(null);
const owner = this.props.owner; const owner = this.props.owner;
this._slotNode = owner.document.createNode(data, this); this._slotNode = owner.document.createNode(data, this);
this._slotNode.internalSetParent(owner); this._slotNode.internalSetParent(owner as any);
} }
this.dispose(); this.dispose();
} }

View File

@ -1,9 +1,9 @@
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, CompositeObject } from '../../../schema'; import { CompositeValue, PropsList, PropsMap } 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 Node from '../node';
export const EXTRA_KEY_PREFIX = '__'; export const EXTRA_KEY_PREFIX = '__';
@ -40,7 +40,7 @@ export default class Props implements IPropParent {
@obx type: 'map' | 'list' = 'map'; @obx type: 'map' | 'list' = 'map';
constructor(readonly owner: NodeParent, value?: PropsMap | PropsList | null, extras?: object) { constructor(readonly owner: Node, 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));

View File

@ -56,9 +56,6 @@ export default class RootNode extends Node implements NodeParent {
get children(): NodeChildren { get children(): NodeChildren {
return this._children as NodeChildren; return this._children as NodeChildren;
} }
get props(): Props {
return this._props as any;
}
internalSetParent(parent: null) { internalSetParent(parent: null) {
// empty // empty
} }

View File

@ -157,7 +157,7 @@ export interface NodeInstance<T = ComponentInstance> {
/** /**
* *
*/ */
export type Component = ComponentType<any> | object | string; export type Component = ComponentType<any> | object;
/** /**
* *

View File

@ -237,7 +237,7 @@ export default class ArraySetter extends Component<{
if (setter?.componentName === 'ObjectSetter') { if (setter?.componentName === 'ObjectSetter') {
const items: FieldConfig[] = setter.props?.config?.items; const items: FieldConfig[] = setter.props?.config?.items;
if (items && Array.isArray(items)) { if (items && Array.isArray(items)) {
columns = items.filter(item => item.isRequired || item.important); columns = items.filter(item => item.isRequired || item.important || (item.setter as any)?.isRequired);
if (columns.length === 3) { if (columns.length === 3) {
columns = columns.slice(0, 3); columns = columns.slice(0, 3);
} else if (columns.length > 3) { } else if (columns.length > 3) {
@ -264,10 +264,7 @@ export default class ArraySetter extends Component<{
} }
this.pipe = (this.context as PopupPipe).create({ width }); this.pipe = (this.context as PopupPipe).create({ width });
} }
this.pipe.send( this.pipe.send(<TableSetter key={field.id} {...props} columns={columns} />, title);
<TableSetter key={field.id} {...props} columns={columns} />,
title,
);
return ( return (
<Button <Button
type={forceInline ? 'normal' : 'primary'} type={forceInline ? 'normal' : 'primary'}

View File

@ -32,9 +32,7 @@ export default class ObjectSetter extends Component<{
interface ObjectSetterConfig { interface ObjectSetterConfig {
items?: FieldConfig[]; items?: FieldConfig[];
extraConfig?: { extraSetter?: SetterType;
setter?: SetterType;
};
} }
interface RowSetterProps { interface RowSetterProps {
@ -61,7 +59,7 @@ class RowSetter extends Component<RowSetterProps> {
const l = Math.min(config.items.length, columns); const l = Math.min(config.items.length, columns);
for (let i = 0; i < l; i++) { for (let i = 0; i < l; i++) {
const conf = config.items[i]; const conf = config.items[i];
if (conf.isRequired || conf.important) { if (conf.isRequired || conf.important || (conf.setter as any)?.isRequired) {
const item = field.createField({ const item = field.createField({
...conf, ...conf,
// in column-cell // in column-cell

View File

@ -160,6 +160,7 @@ export class SettingField implements SettingTarget {
readonly isSettingField = true; readonly isSettingField = true;
readonly id = uniqueId('field'); readonly id = uniqueId('field');
readonly type: 'field' | 'virtual-field' | 'group'; readonly type: 'field' | 'virtual-field' | 'group';
readonly isRequired: boolean = false;
readonly isGroup: boolean; readonly isGroup: boolean;
private _name: string | number; private _name: string | number;
get name() { get name() {
@ -209,6 +210,7 @@ export class SettingField implements SettingTarget {
...rest, ...rest,
...extraProps, ...extraProps,
}; };
this.isRequired = config.isRequired || (setter as any)?.isRequired;
this.isGroup = this.type === 'group'; this.isGroup = this.type === 'group';
// copy parent properties // copy parent properties

View File

@ -264,7 +264,7 @@ registerMetadataTransducer((metadata) => {
registerMetadataTransducer((metadata) => { registerMetadataTransducer((metadata) => {
const { componentName, configure = {} } = metadata; const { componentName, configure = {} } = metadata;
if (componentName === '#frag') { if (componentName === 'Leaf') {
return { return {
...metadata, ...metadata,
configure: { configure: {
@ -277,19 +277,23 @@ registerMetadataTransducer((metadata) => {
props: { props: {
setters: [{ setters: [{
componentName: 'StringSetter', componentName: 'StringSetter',
props: {
// todo:
multiline: true,
},
initialValue: '', initialValue: '',
}, { }, {
componentName: 'ExpressionSetter', componentName: 'ExpressionSetter',
initialValue: { initialValue: {
type: 'JSExpression', type: 'JSExpression',
value: '' value: '',
} },
}] }],
} },
} },
}] }],
} },
} };
} }
const { props, events, styles } = configure as any; const { props, events, styles } = configure as any;
@ -297,8 +301,6 @@ registerMetadataTransducer((metadata) => {
let isRoot: boolean = false; let isRoot: boolean = false;
if (componentName === 'Page' || componentName === 'Component') { if (componentName === 'Page' || componentName === 'Component') {
isRoot = true; isRoot = true;
// todo
/*
supportEvents = [{ supportEvents = [{
description: '初始化时', description: '初始化时',
name: 'constructor' name: 'constructor'
@ -312,7 +314,6 @@ registerMetadataTransducer((metadata) => {
description: '卸载时', description: '卸载时',
name: 'componentWillUnmount' name: 'componentWillUnmount'
}] }]
*/
} else { } else {
supportEvents = (events?.supportEvents || []).map((event: any) => { supportEvents = (events?.supportEvents || []).map((event: any) => {
return typeof event === 'string' ? { return typeof event === 'string' ? {
@ -332,9 +333,11 @@ registerMetadataTransducer((metadata) => {
}, { }, {
name: 'key', name: 'key',
title: 'Key', title: 'Key',
// todo: use Mixin
setter: 'StringSetter', setter: 'StringSetter',
}, { }, {
name: 'ref', name: 'ref',
title: 'Ref',
setter: 'StringSetter', setter: 'StringSetter',
}, { }, {
name: '!more', name: '!more',
@ -395,6 +398,11 @@ registerMetadataTransducer((metadata) => {
if (isRoot) { if (isRoot) {
// todo... // todo...
combined.push({
name: '#advanced',
title: '高级',
items: []
});
} else { } else {
combined.push({ combined.push({
name: '#advanced', name: '#advanced',