mirror of
https://github.com/alibaba/lowcode-engine.git
synced 2025-12-15 05:36:39 +00:00
complete metadata transducer
This commit is contained in:
parent
f37743327b
commit
5f569cc1ca
@ -29,7 +29,7 @@ import {
|
|||||||
CanvasPoint,
|
CanvasPoint,
|
||||||
} from '../../../designer/helper/location';
|
} from '../../../designer/helper/location';
|
||||||
import { isNodeSchema, NodeSchema } from '../../../designer/schema';
|
import { isNodeSchema, NodeSchema } from '../../../designer/schema';
|
||||||
import { ComponentDescription } from '../../../designer/component-type';
|
import { ComponentMetadata } from '../../../designer/component-meta';
|
||||||
import { ReactInstance } from 'react';
|
import { ReactInstance } from 'react';
|
||||||
import { setNativeSelection } from '../../../designer/helper/navtive-selection';
|
import { setNativeSelection } from '../../../designer/helper/navtive-selection';
|
||||||
import cursor from '../../../designer/helper/cursor';
|
import cursor from '../../../designer/helper/cursor';
|
||||||
@ -332,8 +332,14 @@ export class SimulatorHost implements ISimulator<SimulatorProps> {
|
|||||||
/**
|
/**
|
||||||
* @see ISimulator
|
* @see ISimulator
|
||||||
*/
|
*/
|
||||||
describeComponent(component: Component): ComponentDescription {
|
generateComponentMetadata(componentName: string): ComponentMetadata {
|
||||||
throw new Error('Method not implemented.');
|
const component = this.getComponent(componentName);
|
||||||
|
// TODO:
|
||||||
|
// 1. generate builtin div/p/h1/h2
|
||||||
|
// 2. read propTypes
|
||||||
|
return {
|
||||||
|
componentName,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -826,7 +832,7 @@ export class SimulatorHost implements ISimulator<SimulatorProps> {
|
|||||||
return this.checkDropTarget(container, dragObject as any);
|
return this.checkDropTarget(container, dragObject as any);
|
||||||
}
|
}
|
||||||
|
|
||||||
const config = container.componentType;
|
const config = container.componentMeta;
|
||||||
|
|
||||||
if (!config.isContainer) {
|
if (!config.isContainer) {
|
||||||
return false;
|
return false;
|
||||||
@ -911,7 +917,7 @@ export class SimulatorHost implements ISimulator<SimulatorProps> {
|
|||||||
|
|
||||||
checkNestingUp(parent: NodeParent, target: NodeSchema | Node): boolean {
|
checkNestingUp(parent: NodeParent, target: NodeSchema | Node): boolean {
|
||||||
if (isNode(target) || isNodeSchema(target)) {
|
if (isNode(target) || isNodeSchema(target)) {
|
||||||
const config = isNode(target) ? target.componentType : this.designer.getComponentType(target.componentName);
|
const config = isNode(target) ? target.componentMeta : this.document.getComponentMeta(target.componentName);
|
||||||
if (config) {
|
if (config) {
|
||||||
return config.checkNestingUp(target, parent);
|
return config.checkNestingUp(target, parent);
|
||||||
}
|
}
|
||||||
@ -921,7 +927,7 @@ export class SimulatorHost implements ISimulator<SimulatorProps> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
checkNestingDown(parent: NodeParent, target: NodeSchema | Node): boolean {
|
checkNestingDown(parent: NodeParent, target: NodeSchema | Node): boolean {
|
||||||
const config = parent.componentType;
|
const config = parent.componentMeta;
|
||||||
return config.checkNestingDown(parent, target) && this.checkNestingUp(parent, target);
|
return config.checkNestingDown(parent, target) && this.checkNestingUp(parent, target);
|
||||||
}
|
}
|
||||||
// #endregion
|
// #endregion
|
||||||
|
|||||||
@ -7,7 +7,7 @@ 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 { ComponentDescription } from '../../../designer/component-type';
|
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';
|
||||||
@ -39,13 +39,13 @@ export class SimulatorRenderer {
|
|||||||
|
|
||||||
// sync device
|
// sync device
|
||||||
});
|
});
|
||||||
host.componentsConsumer.consume(async (componentsAsset) => {
|
host.componentsConsumer.consume(async componentsAsset => {
|
||||||
if (componentsAsset) {
|
if (componentsAsset) {
|
||||||
await this.load(componentsAsset);
|
await this.load(componentsAsset);
|
||||||
this.buildComponents();
|
this.buildComponents();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
host.injectionConsumer.consume((data) => {
|
host.injectionConsumer.consume(data => {
|
||||||
// sync utils, i18n, contants,... config
|
// sync utils, i18n, contants,... config
|
||||||
this._appContext = {
|
this._appContext = {
|
||||||
utils: {},
|
utils: {},
|
||||||
@ -142,7 +142,7 @@ export class SimulatorRenderer {
|
|||||||
origUnmount = origUnmount.origUnmount;
|
origUnmount = origUnmount.origUnmount;
|
||||||
}
|
}
|
||||||
// hack! delete instance from map
|
// hack! delete instance from map
|
||||||
const newUnmount = function (this: any) {
|
const newUnmount = function(this: any) {
|
||||||
unmountIntance(id, instance);
|
unmountIntance(id, instance);
|
||||||
origUnmount && origUnmount.call(this);
|
origUnmount && origUnmount.call(this);
|
||||||
};
|
};
|
||||||
@ -204,7 +204,7 @@ export class SimulatorRenderer {
|
|||||||
cursor.release();
|
cursor.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
private _running: boolean = false;
|
private _running = false;
|
||||||
run() {
|
run() {
|
||||||
if (this._running) {
|
if (this._running) {
|
||||||
return;
|
return;
|
||||||
@ -281,15 +281,14 @@ function findComponent(componentName: string, npm?: NpmInfo) {
|
|||||||
return getSubComponent(library, paths);
|
return getSubComponent(library, paths);
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildComponents(componentsMap: { [componentName: string]: ComponentDescription }) {
|
function buildComponents(componentsMap: { [componentName: string]: NpmInfo }) {
|
||||||
const components: any = {};
|
const components: any = {};
|
||||||
Object.keys(componentsMap).forEach(componentName => {
|
Object.keys(componentsMap).forEach(componentName => {
|
||||||
components[componentName] = findComponent(componentName, componentsMap[componentName].npm);
|
components[componentName] = findComponent(componentName, componentsMap[componentName]);
|
||||||
});
|
});
|
||||||
return components;
|
return components;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
let REACT_KEY = '';
|
let REACT_KEY = '';
|
||||||
function cacheReactKey(el: Element): Element {
|
function cacheReactKey(el: Element): Element {
|
||||||
if (REACT_KEY !== '') {
|
if (REACT_KEY !== '') {
|
||||||
|
|||||||
224
packages/designer/src/designer/component-meta.ts
Normal file
224
packages/designer/src/designer/component-meta.ts
Normal file
@ -0,0 +1,224 @@
|
|||||||
|
import { ReactNode } from 'react';
|
||||||
|
import Node, { NodeParent } from './document/node/node';
|
||||||
|
import { NodeData, NodeSchema } from './schema';
|
||||||
|
import { PropConfig } from './prop-config';
|
||||||
|
|
||||||
|
export interface NestingRule {
|
||||||
|
childWhitelist?: string[];
|
||||||
|
parentWhitelist?: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Configure {
|
||||||
|
props?: any[];
|
||||||
|
styles?: object;
|
||||||
|
events?: object;
|
||||||
|
component?: {
|
||||||
|
isContainer?: boolean;
|
||||||
|
isModal?: boolean;
|
||||||
|
descriptor?: string;
|
||||||
|
nestingRule?: NestingRule;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ComponentMetadata {
|
||||||
|
componentName: string;
|
||||||
|
/**
|
||||||
|
* unique id
|
||||||
|
*/
|
||||||
|
uri?: string;
|
||||||
|
/**
|
||||||
|
* title or description
|
||||||
|
*/
|
||||||
|
title?: string;
|
||||||
|
/**
|
||||||
|
* svg icon for component
|
||||||
|
*/
|
||||||
|
icon?: string | ReactNode;
|
||||||
|
tags?: string[];
|
||||||
|
description?: string;
|
||||||
|
docUrl?: string;
|
||||||
|
screenshot?: string;
|
||||||
|
devMode?: 'procode' | 'lowcode';
|
||||||
|
npm?: {
|
||||||
|
package: string;
|
||||||
|
exportName: string;
|
||||||
|
subName: string;
|
||||||
|
main: string;
|
||||||
|
destructuring: boolean;
|
||||||
|
version: string;
|
||||||
|
};
|
||||||
|
props?: PropConfig[];
|
||||||
|
configure?: any[] | Configure;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TransformedComponentMetadata extends ComponentMetadata {
|
||||||
|
configure?: Configure & {
|
||||||
|
combined?: any[];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureAList(list?: string | string[]): string[] | null {
|
||||||
|
if (!list) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (!Array.isArray(list)) {
|
||||||
|
list = list.split(/ *[ ,|] */).filter(Boolean);
|
||||||
|
}
|
||||||
|
if (list.length < 1) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
function npmToURI(npm: {
|
||||||
|
package: string;
|
||||||
|
exportName?: string;
|
||||||
|
subName?: string;
|
||||||
|
destructuring?: boolean;
|
||||||
|
main?: string;
|
||||||
|
version: string;
|
||||||
|
}): string {
|
||||||
|
const pkg = [];
|
||||||
|
if (npm.package) {
|
||||||
|
pkg.push(npm.package);
|
||||||
|
}
|
||||||
|
if (npm.main) {
|
||||||
|
if (npm.main[0] === '/') {
|
||||||
|
pkg.push(npm.main.slice(1));
|
||||||
|
} else if (npm.main.slice(0, 2) === './') {
|
||||||
|
pkg.push(npm.main.slice(2));
|
||||||
|
} else {
|
||||||
|
pkg.push(npm.main);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let uri = pkg.join('/');
|
||||||
|
uri += `:${npm.destructuring && npm.exportName ? npm.exportName : 'default'}`;
|
||||||
|
|
||||||
|
if (npm.subName) {
|
||||||
|
uri += `.${npm.subName}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type MetadataTransducer = (prev: ComponentMetadata) => TransformedComponentMetadata;
|
||||||
|
const metadataTransducers: MetadataTransducer[] = [];
|
||||||
|
|
||||||
|
export function registerMetadataTransducer(transducer: MetadataTransducer) {
|
||||||
|
metadataTransducers.push(transducer);
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ComponentMeta {
|
||||||
|
readonly isComponentMeta = true;
|
||||||
|
private _uri?: string;
|
||||||
|
get uri(): string {
|
||||||
|
return this._uri!;
|
||||||
|
}
|
||||||
|
private _componentName?: string;
|
||||||
|
get componentName(): string {
|
||||||
|
return this._componentName!;
|
||||||
|
}
|
||||||
|
private _isContainer?: boolean;
|
||||||
|
get isContainer(): boolean {
|
||||||
|
return this._isContainer! || this.isRootComponent();
|
||||||
|
}
|
||||||
|
private _isModal?: boolean;
|
||||||
|
get isModal(): boolean {
|
||||||
|
return this._isModal!;
|
||||||
|
}
|
||||||
|
private _descriptor?: string;
|
||||||
|
get descriptor(): string {
|
||||||
|
return this._descriptor!;
|
||||||
|
}
|
||||||
|
private _acceptable?: boolean;
|
||||||
|
get acceptable(): boolean {
|
||||||
|
return this._acceptable!;
|
||||||
|
}
|
||||||
|
private _transformedMetadata?: TransformedComponentMetadata;
|
||||||
|
get configure() {
|
||||||
|
const config = this._transformedMetadata?.configure;
|
||||||
|
return config?.combined || config?.props || [];
|
||||||
|
}
|
||||||
|
|
||||||
|
private parentWhitelist?: string[] | null;
|
||||||
|
private childWhitelist?: string[] | null;
|
||||||
|
|
||||||
|
get title() {
|
||||||
|
return this._metadata.title || this.componentName;
|
||||||
|
}
|
||||||
|
|
||||||
|
get icon() {
|
||||||
|
return this._metadata.icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(private _metadata: ComponentMetadata) {
|
||||||
|
this.parseMetadata(_metadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
private parseMetadata(metadta: ComponentMetadata) {
|
||||||
|
const { componentName, uri, npm, props } = metadta;
|
||||||
|
this._uri = uri || (npm ? npmToURI(npm) : componentName);
|
||||||
|
this._componentName = componentName;
|
||||||
|
|
||||||
|
metadta.uri = this._uri;
|
||||||
|
// 额外转换逻辑
|
||||||
|
this._transformedMetadata = this.transformMetadata(metadta);
|
||||||
|
|
||||||
|
const { configure = {} } = this._transformedMetadata;
|
||||||
|
this._acceptable = false;
|
||||||
|
|
||||||
|
const { component } = configure;
|
||||||
|
if (component) {
|
||||||
|
this._isContainer = component.isContainer ? true : false;
|
||||||
|
this._isModal = component.isModal ? true : false;
|
||||||
|
this._descriptor = component.descriptor;
|
||||||
|
if (component.nestingRule) {
|
||||||
|
const { parentWhitelist, childWhitelist } = component.nestingRule;
|
||||||
|
this.parentWhitelist = ensureAList(parentWhitelist);
|
||||||
|
this.childWhitelist = ensureAList(childWhitelist);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this._isContainer = false;
|
||||||
|
this._isModal = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private transformMetadata(metadta: ComponentMetadata): TransformedComponentMetadata {
|
||||||
|
const result = metadataTransducers.reduce((prevMetadata, current) => {
|
||||||
|
return current(prevMetadata);
|
||||||
|
}, metadta);
|
||||||
|
|
||||||
|
if (!result.configure) {
|
||||||
|
result.configure = {};
|
||||||
|
}
|
||||||
|
return result as any;
|
||||||
|
}
|
||||||
|
|
||||||
|
isRootComponent() {
|
||||||
|
return this.componentName === 'Page' || this.componentName === 'Block' || this.componentName === 'Component';
|
||||||
|
}
|
||||||
|
|
||||||
|
set metadata(metadata: ComponentMetadata) {
|
||||||
|
this._metadata = metadata;
|
||||||
|
this.parseMetadata(metadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
get metadata(): ComponentMetadata {
|
||||||
|
return this._metadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
checkNestingUp(my: Node | NodeData, parent: NodeParent) {
|
||||||
|
if (this.parentWhitelist) {
|
||||||
|
return this.parentWhitelist.includes(parent.componentName);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
checkNestingDown(my: Node, target: Node | NodeSchema) {
|
||||||
|
if (this.childWhitelist) {
|
||||||
|
return this.childWhitelist.includes(target.componentName);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,343 +0,0 @@
|
|||||||
import { ReactNode } from 'react';
|
|
||||||
import Node, { NodeParent } from './document/node/node';
|
|
||||||
import { NodeData, NodeSchema } from './schema';
|
|
||||||
|
|
||||||
export type BasicTypes = 'array' | 'bool' | 'func' | 'number' | 'object' | 'string' | 'node' | 'element' | 'any';
|
|
||||||
export interface CompositeType {
|
|
||||||
type: BasicTypes;
|
|
||||||
isRequired: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: add complex types
|
|
||||||
|
|
||||||
export interface PropConfig {
|
|
||||||
name: string;
|
|
||||||
propType: BasicTypes | CompositeType;
|
|
||||||
description?: string;
|
|
||||||
defaultValue?: any;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface NestingRule {
|
|
||||||
childWhitelist?: string[];
|
|
||||||
parentWhitelist?: string[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Configure {
|
|
||||||
props?: any[];
|
|
||||||
styles?: object;
|
|
||||||
events?: object;
|
|
||||||
component?: {
|
|
||||||
isContainer?: boolean;
|
|
||||||
isModal?: boolean;
|
|
||||||
descriptor?: string;
|
|
||||||
nestingRule?: NestingRule;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ComponentDescription {
|
|
||||||
componentName: string;
|
|
||||||
/**
|
|
||||||
* unique id
|
|
||||||
*/
|
|
||||||
uri?: string;
|
|
||||||
/**
|
|
||||||
* title or description
|
|
||||||
*/
|
|
||||||
title?: string;
|
|
||||||
/**
|
|
||||||
* svg icon for component
|
|
||||||
*/
|
|
||||||
icon?: string | ReactNode;
|
|
||||||
tags?: string[];
|
|
||||||
description?: string;
|
|
||||||
docUrl?: string;
|
|
||||||
screenshot?: string;
|
|
||||||
devMode?: 'procode' | 'lowcode';
|
|
||||||
npm?: {
|
|
||||||
package: string;
|
|
||||||
exportName: string;
|
|
||||||
subName: string;
|
|
||||||
main: string;
|
|
||||||
destructuring: boolean;
|
|
||||||
version: string;
|
|
||||||
};
|
|
||||||
props?: PropConfig[];
|
|
||||||
configure?: any[] | Configure;
|
|
||||||
}
|
|
||||||
|
|
||||||
function ensureAList(list?: string | string[]): string[] | null {
|
|
||||||
if (!list) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (!Array.isArray(list)) {
|
|
||||||
list = list.split(/ *[ ,|] */).filter(Boolean);
|
|
||||||
}
|
|
||||||
if (list.length < 1) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
function npmToURI(npm: {
|
|
||||||
package: string;
|
|
||||||
exportName?: string;
|
|
||||||
subName?: string;
|
|
||||||
destructuring?: boolean;
|
|
||||||
main?: string;
|
|
||||||
version: string;
|
|
||||||
}): string {
|
|
||||||
const pkg = [];
|
|
||||||
if (npm.package) {
|
|
||||||
pkg.push(npm.package);
|
|
||||||
}
|
|
||||||
if (npm.main) {
|
|
||||||
if (npm.main[0] === '/') {
|
|
||||||
pkg.push(npm.main.slice(1));
|
|
||||||
} else if (npm.main.slice(0, 2) === './') {
|
|
||||||
pkg.push(npm.main.slice(2));
|
|
||||||
} else {
|
|
||||||
pkg.push(npm.main);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let uri = pkg.join('/');
|
|
||||||
uri += `:${npm.destructuring && npm.exportName ? npm.exportName : 'default'}`;
|
|
||||||
|
|
||||||
if (npm.subName) {
|
|
||||||
uri += `.${npm.subName}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return uri;
|
|
||||||
}
|
|
||||||
|
|
||||||
function generatePropsConfigure(props: PropConfig[]) {
|
|
||||||
// todo:
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
export class ComponentType {
|
|
||||||
readonly isComponentType = true;
|
|
||||||
private _uri?: string;
|
|
||||||
get uri(): string {
|
|
||||||
return this._uri!;
|
|
||||||
}
|
|
||||||
private _componentName?: string;
|
|
||||||
get componentName(): string {
|
|
||||||
return this._componentName!;
|
|
||||||
}
|
|
||||||
private _isContainer?: boolean;
|
|
||||||
get isContainer(): boolean {
|
|
||||||
return true; // this._isContainer! || this.isRootComponent();
|
|
||||||
}
|
|
||||||
private _isModal?: boolean;
|
|
||||||
get isModal(): boolean {
|
|
||||||
return this._isModal!;
|
|
||||||
}
|
|
||||||
private _descriptor?: string;
|
|
||||||
get descriptor(): string {
|
|
||||||
return this._descriptor!;
|
|
||||||
}
|
|
||||||
private _acceptable?: boolean;
|
|
||||||
get acceptable(): boolean {
|
|
||||||
return this._acceptable!;
|
|
||||||
}
|
|
||||||
private _configure?: Configure;
|
|
||||||
get configure() {
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
name: '#props',
|
|
||||||
title: '属性',
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
name: 'label',
|
|
||||||
title: '标签',
|
|
||||||
setter: 'StringSetter',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'data',
|
|
||||||
title: '数据',
|
|
||||||
setter: {
|
|
||||||
componentName: 'ArraySetter',
|
|
||||||
props: {
|
|
||||||
itemConfig: {
|
|
||||||
setter: {
|
|
||||||
componentName: 'ObjectSetter',
|
|
||||||
props: {
|
|
||||||
config: {
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
name: 'title',
|
|
||||||
title: '名称',
|
|
||||||
setter: 'StringSetter',
|
|
||||||
important: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'records',
|
|
||||||
title: '记录集',
|
|
||||||
setter: {
|
|
||||||
componentName: 'ArraySetter',
|
|
||||||
props: {
|
|
||||||
itemConfig: {
|
|
||||||
setter: {
|
|
||||||
componentName: 'ArraySetter',
|
|
||||||
props: {
|
|
||||||
itemConfig: {
|
|
||||||
setter: 'StringSetter',
|
|
||||||
defaultValue: '',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
defaultValue: [],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
important: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
extraConfig: {},
|
|
||||||
},
|
|
||||||
// mode: 'popup'
|
|
||||||
},
|
|
||||||
},
|
|
||||||
defaultValue: {},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'age',
|
|
||||||
title: '年龄',
|
|
||||||
setter: 'NumberSetter',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: '#styles',
|
|
||||||
title: '样式',
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
name: 'className',
|
|
||||||
title: '类名绑定',
|
|
||||||
setter: 'ClassNameSetter',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'className2',
|
|
||||||
title: '类名绑定',
|
|
||||||
setter: 'StringSetter',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: '#inlineStyles',
|
|
||||||
title: '行内样式',
|
|
||||||
items: [],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: '#events',
|
|
||||||
title: '事件',
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
name: '!events',
|
|
||||||
title: '事件绑定',
|
|
||||||
setter: {
|
|
||||||
componentName: 'EventsSetter',
|
|
||||||
},
|
|
||||||
extraProps: {
|
|
||||||
getValue(field: any) {
|
|
||||||
console.info('lifeCycles', field.getExtraPropValue('lifeCycles'));
|
|
||||||
return field.getPropValue('xxx');
|
|
||||||
},
|
|
||||||
setValue(field: any, val: any) {
|
|
||||||
field.setExtraPropValue('lifeCycles', val);
|
|
||||||
field.setPropValue('xxx', val);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: '#data',
|
|
||||||
title: '数据',
|
|
||||||
items: [],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
private parentWhitelist?: string[] | null;
|
|
||||||
private childWhitelist?: string[] | null;
|
|
||||||
|
|
||||||
get title() {
|
|
||||||
return this._spec.title || this.componentName;
|
|
||||||
}
|
|
||||||
|
|
||||||
get icon() {
|
|
||||||
return this._spec.icon;
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(private _spec: ComponentDescription) {
|
|
||||||
this.parseSpec(_spec);
|
|
||||||
}
|
|
||||||
|
|
||||||
private parseSpec(spec: ComponentDescription) {
|
|
||||||
const { componentName, uri, configure, npm, props } = spec;
|
|
||||||
this._uri = uri || (npm ? npmToURI(npm) : componentName);
|
|
||||||
this._componentName = componentName;
|
|
||||||
this._acceptable = false;
|
|
||||||
|
|
||||||
if (!configure || Array.isArray(configure)) {
|
|
||||||
this._configure = {
|
|
||||||
props: !configure ? [] : configure,
|
|
||||||
styles: {
|
|
||||||
supportClassName: true,
|
|
||||||
supportInlineStyle: true,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
this._configure = configure;
|
|
||||||
}
|
|
||||||
if (!this._configure.props) {
|
|
||||||
this._configure.props = props ? generatePropsConfigure(props) : [];
|
|
||||||
}
|
|
||||||
const { component } = this._configure;
|
|
||||||
if (component) {
|
|
||||||
this._isContainer = component.isContainer ? true : false;
|
|
||||||
this._isModal = component.isModal ? true : false;
|
|
||||||
this._descriptor = component.descriptor;
|
|
||||||
if (component.nestingRule) {
|
|
||||||
const { parentWhitelist, childWhitelist } = component.nestingRule;
|
|
||||||
this.parentWhitelist = ensureAList(parentWhitelist);
|
|
||||||
this.childWhitelist = ensureAList(childWhitelist);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this._isContainer = false;
|
|
||||||
this._isModal = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
isRootComponent() {
|
|
||||||
return this.componentName === 'Page' || this.componentName === 'Block' || this.componentName === 'Component';
|
|
||||||
}
|
|
||||||
|
|
||||||
set spec(spec: ComponentDescription) {
|
|
||||||
this._spec = spec;
|
|
||||||
this.parseSpec(spec);
|
|
||||||
}
|
|
||||||
|
|
||||||
get spec(): ComponentDescription {
|
|
||||||
return this._spec;
|
|
||||||
}
|
|
||||||
|
|
||||||
checkNestingUp(my: Node | NodeData, parent: NodeParent) {
|
|
||||||
if (this.parentWhitelist) {
|
|
||||||
return this.parentWhitelist.includes(parent.componentName);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
checkNestingDown(my: Node, target: Node | NodeSchema) {
|
|
||||||
if (this.childWhitelist) {
|
|
||||||
return this.childWhitelist.includes(target.componentName);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -2,7 +2,7 @@ import { ComponentType as ReactComponentType } from 'react';
|
|||||||
import { obx, computed, autorun } from '@recore/obx';
|
import { obx, computed, autorun } from '@recore/obx';
|
||||||
import BuiltinSimulatorView from '../builtins/simulator';
|
import BuiltinSimulatorView from '../builtins/simulator';
|
||||||
import Project from './project';
|
import Project from './project';
|
||||||
import { ProjectSchema } from './schema';
|
import { ProjectSchema, NpmInfo } from './schema';
|
||||||
import Dragon, { isDragNodeObject, isDragNodeDataObject, LocateEvent, DragObject } from './helper/dragon';
|
import Dragon, { isDragNodeObject, isDragNodeDataObject, LocateEvent, DragObject } from './helper/dragon';
|
||||||
import ActiveTracker from './helper/active-tracker';
|
import ActiveTracker from './helper/active-tracker';
|
||||||
import Hovering from './helper/hovering';
|
import Hovering from './helper/hovering';
|
||||||
@ -10,7 +10,7 @@ import Location, { LocationData, isLocationChildrenDetail } from './helper/locat
|
|||||||
import DocumentModel from './document/document-model';
|
import DocumentModel from './document/document-model';
|
||||||
import Node, { insertChildren } from './document/node/node';
|
import Node, { insertChildren } from './document/node/node';
|
||||||
import { isRootNode } from './document/node/root-node';
|
import { isRootNode } from './document/node/root-node';
|
||||||
import { ComponentDescription, ComponentType } from './component-type';
|
import { ComponentMetadata, ComponentMeta } from './component-meta';
|
||||||
import Scroller, { IScrollable } from './helper/scroller';
|
import Scroller, { IScrollable } from './helper/scroller';
|
||||||
import { INodeSelector } from './simulator';
|
import { INodeSelector } from './simulator';
|
||||||
import OffsetObserver, { createOffsetObserver } from './helper/offset-observer';
|
import OffsetObserver, { createOffsetObserver } from './helper/offset-observer';
|
||||||
@ -25,7 +25,7 @@ export interface DesignerProps {
|
|||||||
simulatorComponent?: ReactComponentType<any>;
|
simulatorComponent?: ReactComponentType<any>;
|
||||||
dragGhostComponent?: ReactComponentType<any>;
|
dragGhostComponent?: ReactComponentType<any>;
|
||||||
suspensed?: boolean;
|
suspensed?: boolean;
|
||||||
componentsDescription?: ComponentDescription[];
|
componentsDescription?: ComponentMetadata[];
|
||||||
eventPipe?: EventEmitter;
|
eventPipe?: EventEmitter;
|
||||||
onMount?: (designer: Designer) => void;
|
onMount?: (designer: Designer) => void;
|
||||||
onDragstart?: (e: LocateEvent) => void;
|
onDragstart?: (e: LocateEvent) => void;
|
||||||
@ -222,7 +222,7 @@ export default class Designer {
|
|||||||
this.suspensed = props.suspensed;
|
this.suspensed = props.suspensed;
|
||||||
}
|
}
|
||||||
if (props.componentsDescription !== this.props.componentsDescription && props.componentsDescription != null) {
|
if (props.componentsDescription !== this.props.componentsDescription && props.componentsDescription != null) {
|
||||||
this.buildComponentTypesMap(props.componentsDescription);
|
this.buildComponentMetasMap(props.componentsDescription);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// init hotkeys
|
// init hotkeys
|
||||||
@ -239,7 +239,7 @@ export default class Designer {
|
|||||||
this.suspensed = props.suspensed;
|
this.suspensed = props.suspensed;
|
||||||
}
|
}
|
||||||
if (props.componentsDescription != null) {
|
if (props.componentsDescription != null) {
|
||||||
this.buildComponentTypesMap(props.componentsDescription);
|
this.buildComponentMetasMap(props.componentsDescription);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.props = props;
|
this.props = props;
|
||||||
@ -283,52 +283,53 @@ export default class Designer {
|
|||||||
// todo:
|
// todo:
|
||||||
}
|
}
|
||||||
|
|
||||||
@obx.val private _componentTypesMap = new Map<string, ComponentType>();
|
@obx.val private _componentMetasMap = new Map<string, ComponentMeta>();
|
||||||
private _lostComponentTypesMap = new Map<string, ComponentType>();
|
private _lostComponentMetasMap = new Map<string, ComponentMeta>();
|
||||||
|
|
||||||
private buildComponentTypesMap(specs: ComponentDescription[]) {
|
private buildComponentMetasMap(metas: ComponentMetadata[]) {
|
||||||
specs.forEach(spec => {
|
metas.forEach(data => {
|
||||||
const key = spec.componentName;
|
const key = data.componentName;
|
||||||
let cType = this._componentTypesMap.get(key);
|
let meta = this._componentMetasMap.get(key);
|
||||||
if (cType) {
|
if (meta) {
|
||||||
cType.spec = spec;
|
meta.metadata = data;
|
||||||
} else {
|
} else {
|
||||||
cType = this._lostComponentTypesMap.get(key);
|
meta = this._lostComponentMetasMap.get(key);
|
||||||
|
|
||||||
if (cType) {
|
if (meta) {
|
||||||
cType.spec = spec;
|
meta.metadata = data;
|
||||||
this._lostComponentTypesMap.delete(key);
|
this._lostComponentMetasMap.delete(key);
|
||||||
} else {
|
} else {
|
||||||
cType = new ComponentType(spec);
|
meta = new ComponentMeta(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
this._componentTypesMap.set(key, cType);
|
this._componentMetasMap.set(key, meta);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getComponentType(componentName: string): ComponentType {
|
getComponentMeta(componentName: string, generateMetadata?: () => ComponentMetadata | null): ComponentMeta {
|
||||||
if (this._componentTypesMap.has(componentName)) {
|
if (this._componentMetasMap.has(componentName)) {
|
||||||
return this._componentTypesMap.get(componentName)!;
|
return this._componentMetasMap.get(componentName)!;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._lostComponentTypesMap.has(componentName)) {
|
if (this._lostComponentMetasMap.has(componentName)) {
|
||||||
return this._lostComponentTypesMap.get(componentName)!;
|
return this._lostComponentMetasMap.get(componentName)!;
|
||||||
}
|
}
|
||||||
|
|
||||||
const cType = new ComponentType({
|
const meta = new ComponentMeta({
|
||||||
componentName,
|
componentName,
|
||||||
|
...(generateMetadata ? generateMetadata() : null),
|
||||||
});
|
});
|
||||||
|
|
||||||
this._lostComponentTypesMap.set(componentName, cType);
|
this._lostComponentMetasMap.set(componentName, meta);
|
||||||
|
|
||||||
return cType;
|
return meta;
|
||||||
}
|
}
|
||||||
|
|
||||||
get componentsMap(): { [key: string]: ComponentDescription } {
|
get componentsMap(): { [key: string]: NpmInfo } {
|
||||||
const maps: any = {};
|
const maps: any = {};
|
||||||
this._componentTypesMap.forEach((config, key) => {
|
this._componentMetasMap.forEach((config, key) => {
|
||||||
maps[key] = config.spec;
|
maps[key] = config.metadata.npm;
|
||||||
});
|
});
|
||||||
return maps;
|
return maps;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import RootNode from './node/root-node';
|
|||||||
import { ISimulator, Component } from '../simulator';
|
import { ISimulator, Component } from '../simulator';
|
||||||
import { computed, obx, autorun } from '@recore/obx';
|
import { computed, obx, autorun } from '@recore/obx';
|
||||||
import Location from '../helper/location';
|
import Location from '../helper/location';
|
||||||
import { ComponentType } from '../component-type';
|
import { ComponentMeta } from '../component-meta';
|
||||||
import History from '../helper/history';
|
import History from '../helper/history';
|
||||||
import Prop from './node/props/prop';
|
import Prop from './node/props/prop';
|
||||||
|
|
||||||
@ -41,7 +41,7 @@ export default class DocumentModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get fileName(): string {
|
get fileName(): string {
|
||||||
return (this.rootNode.getExtraProp('fileName')?.getAsString()) || this.id;
|
return this.rootNode.getExtraProp('fileName')?.getAsString() || this.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
set fileName(fileName: string) {
|
set fileName(fileName: string) {
|
||||||
@ -60,7 +60,7 @@ export default class DocumentModel {
|
|||||||
this.id = this.rootNode.id;
|
this.id = this.rootNode.id;
|
||||||
this.history = new History(
|
this.history = new History(
|
||||||
() => this.schema,
|
() => this.schema,
|
||||||
(schema) => this.import(schema as RootSchema, true),
|
schema => this.import(schema as RootSchema, true),
|
||||||
);
|
);
|
||||||
this.setupListenActiveNodes();
|
this.setupListenActiveNodes();
|
||||||
}
|
}
|
||||||
@ -237,7 +237,7 @@ export default class DocumentModel {
|
|||||||
return this.rootNode.schema as any;
|
return this.rootNode.schema as any;
|
||||||
}
|
}
|
||||||
|
|
||||||
import(schema: RootSchema, checkId: boolean = false) {
|
import(schema: RootSchema, checkId = false) {
|
||||||
this.rootNode.import(schema, checkId);
|
this.rootNode.import(schema, checkId);
|
||||||
// todo: purge something
|
// todo: purge something
|
||||||
// todo: select added and active track added
|
// todo: select added and active track added
|
||||||
@ -285,13 +285,15 @@ export default class DocumentModel {
|
|||||||
return this.simulator!.getComponent(componentName);
|
return this.simulator!.getComponent(componentName);
|
||||||
}
|
}
|
||||||
|
|
||||||
getComponentType(componentName: string, component?: Component | null): ComponentType {
|
getComponentMeta(componentName: string): ComponentMeta {
|
||||||
// TODO: guess componentConfig from component by simulator
|
return this.designer.getComponentMeta(
|
||||||
return this.designer.getComponentType(componentName);
|
componentName,
|
||||||
|
() => this.simulator?.generateComponentMetadata(componentName) || null,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@obx.ref private _opened: boolean = false;
|
@obx.ref private _opened = false;
|
||||||
@obx.ref private _suspensed: boolean = false;
|
@obx.ref private _suspensed = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 是否不是激活的
|
* 是否不是激活的
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import NodeChildren from './node-children';
|
|||||||
import Prop from './props/prop';
|
import Prop from './props/prop';
|
||||||
import NodeContent from './node-content';
|
import NodeContent from './node-content';
|
||||||
import { Component } from '../../simulator';
|
import { Component } from '../../simulator';
|
||||||
import { ComponentType } from '../../component-type';
|
import { ComponentMeta } from '../../component-meta';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 基础节点
|
* 基础节点
|
||||||
@ -78,8 +78,8 @@ export default class Node {
|
|||||||
|
|
||||||
@computed get title(): string {
|
@computed get title(): string {
|
||||||
let t = this.getExtraProp('title');
|
let t = this.getExtraProp('title');
|
||||||
if (!t && this.componentType.descriptor) {
|
if (!t && this.componentMeta.descriptor) {
|
||||||
t = this.getProp(this.componentType.descriptor, false);
|
t = this.getProp(this.componentMeta.descriptor, false);
|
||||||
}
|
}
|
||||||
if (t) {
|
if (t) {
|
||||||
const v = t.getAsString();
|
const v = t.getAsString();
|
||||||
@ -87,7 +87,7 @@ export default class Node {
|
|||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return this.componentType.title;
|
return this.componentMeta.title;
|
||||||
}
|
}
|
||||||
|
|
||||||
get isSlotRoot(): boolean {
|
get isSlotRoot(): boolean {
|
||||||
@ -157,7 +157,7 @@ export default class Node {
|
|||||||
/**
|
/**
|
||||||
* 悬停高亮
|
* 悬停高亮
|
||||||
*/
|
*/
|
||||||
hover(flag: boolean = true) {
|
hover(flag = true) {
|
||||||
if (flag) {
|
if (flag) {
|
||||||
this.document.designer.hovering.hover(this);
|
this.document.designer.hovering.hover(this);
|
||||||
} else {
|
} else {
|
||||||
@ -168,18 +168,18 @@ export default class Node {
|
|||||||
/**
|
/**
|
||||||
* 节点组件类
|
* 节点组件类
|
||||||
*/
|
*/
|
||||||
@obx.ref get component(): Component | null {
|
@obx.ref get component(): Component {
|
||||||
if (this.isNodeParent) {
|
if (this.isNodeParent) {
|
||||||
return this.document.getComponent(this.componentName);
|
return this.document.getComponent(this.componentName) || this.componentName;
|
||||||
}
|
}
|
||||||
return null;
|
return this.componentName;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 节点组件描述
|
* 节点组件描述
|
||||||
*/
|
*/
|
||||||
@computed get componentType(): ComponentType {
|
@computed get componentMeta(): ComponentMeta {
|
||||||
return this.document.getComponentType(this.componentName, this.component);
|
return this.document.getComponentMeta(this.componentName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@computed get propsData(): PropsMap | PropsList | null {
|
@computed get propsData(): PropsMap | PropsList | null {
|
||||||
@ -223,19 +223,19 @@ export default class Node {
|
|||||||
}
|
}
|
||||||
|
|
||||||
wrapWith(schema: NodeSchema) {
|
wrapWith(schema: NodeSchema) {
|
||||||
|
// todo
|
||||||
}
|
}
|
||||||
|
|
||||||
replaceWith(schema: NodeSchema, migrate: boolean = true) {
|
replaceWith(schema: NodeSchema, migrate = true) {
|
||||||
// reuse the same id? or replaceSelection
|
// reuse the same id? or replaceSelection
|
||||||
//
|
//
|
||||||
}
|
}
|
||||||
|
|
||||||
getProp(path: string, useStash: boolean = true): Prop | null {
|
getProp(path: string, useStash = true): Prop | null {
|
||||||
return this.props?.query(path, useStash as any) || null;
|
return this.props?.query(path, useStash as any) || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
getExtraProp(key: string, useStash: boolean = true): Prop | null {
|
getExtraProp(key: string, useStash = true): Prop | null {
|
||||||
return this.props?.get(EXTRA_KEY_PREFIX + key, useStash) || null;
|
return this.props?.get(EXTRA_KEY_PREFIX + key, useStash) || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -316,7 +316,7 @@ export default class Node {
|
|||||||
this.import(data);
|
this.import(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
import(data: NodeSchema, checkId: boolean = false) {
|
import(data: NodeSchema, checkId = false) {
|
||||||
const { componentName, id, children, props, ...extras } = data;
|
const { componentName, id, children, props, ...extras } = data;
|
||||||
|
|
||||||
if (isNodeParent(this)) {
|
if (isNodeParent(this)) {
|
||||||
@ -514,4 +514,3 @@ export function insertChildren(
|
|||||||
}
|
}
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
46
packages/designer/src/designer/prop-config.ts
Normal file
46
packages/designer/src/designer/prop-config.ts
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
export type PropType = BasicType | RequiredType | ComplexType;
|
||||||
|
export type BasicType = 'array' | 'bool' | 'func' | 'number' | 'object' | 'string' | 'node' | 'element' | 'any';
|
||||||
|
export type ComplexType = OneOf | OneOfType | ArrayOf | ObjectOf | Shape | Exact;
|
||||||
|
|
||||||
|
export interface RequiredType {
|
||||||
|
type: BasicType;
|
||||||
|
isRequired?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface OneOf {
|
||||||
|
type: 'oneOf';
|
||||||
|
value: string[];
|
||||||
|
isRequired?: boolean;
|
||||||
|
}
|
||||||
|
export interface OneOfType {
|
||||||
|
type: 'oneOfType';
|
||||||
|
value: PropType[];
|
||||||
|
isRequired?: boolean;
|
||||||
|
}
|
||||||
|
export interface ArrayOf {
|
||||||
|
type: 'arrayOf';
|
||||||
|
value: PropType;
|
||||||
|
isRequired?: boolean;
|
||||||
|
}
|
||||||
|
export interface ObjectOf {
|
||||||
|
type: 'objectOf';
|
||||||
|
value: PropType;
|
||||||
|
isRequired?: boolean;
|
||||||
|
}
|
||||||
|
export interface Shape {
|
||||||
|
type: 'shape';
|
||||||
|
value: PropConfig[];
|
||||||
|
isRequired?: boolean;
|
||||||
|
}
|
||||||
|
export interface Exact {
|
||||||
|
type: 'exact';
|
||||||
|
value: PropConfig[];
|
||||||
|
isRequired?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PropConfig {
|
||||||
|
name: string;
|
||||||
|
propType: PropType;
|
||||||
|
description?: string;
|
||||||
|
defaultValue?: any;
|
||||||
|
}
|
||||||
@ -3,7 +3,7 @@ import { LocateEvent, ISensor } from './helper/dragon';
|
|||||||
import { Point } from './helper/location';
|
import { Point } from './helper/location';
|
||||||
import Node from './document/node/node';
|
import Node from './document/node/node';
|
||||||
import { ScrollTarget, IScrollable } from './helper/scroller';
|
import { ScrollTarget, IScrollable } from './helper/scroller';
|
||||||
import { ComponentDescription } from './component-type';
|
import { ComponentMetadata } from './component-meta';
|
||||||
|
|
||||||
export type AutoFit = '100%';
|
export type AutoFit = '100%';
|
||||||
export const AutoFit = '100%';
|
export const AutoFit = '100%';
|
||||||
@ -85,7 +85,6 @@ export interface ISimulator<P = object> extends ISensor {
|
|||||||
// 获取区块代码, 通过 components 传递,可异步获取
|
// 获取区块代码, 通过 components 传递,可异步获取
|
||||||
setProps(props: P): void;
|
setProps(props: P): void;
|
||||||
|
|
||||||
|
|
||||||
setSuspense(suspensed: boolean): void;
|
setSuspense(suspensed: boolean): void;
|
||||||
|
|
||||||
// #region ========= drag and drop helpers =============
|
// #region ========= drag and drop helpers =============
|
||||||
@ -117,7 +116,7 @@ export interface ISimulator<P = object> extends ISensor {
|
|||||||
/**
|
/**
|
||||||
* 描述组件
|
* 描述组件
|
||||||
*/
|
*/
|
||||||
describeComponent(component: Component): ComponentDescription;
|
generateComponentMetadata(componentName: string): ComponentMetadata;
|
||||||
/**
|
/**
|
||||||
* 根据组件信息获取组件类
|
* 根据组件信息获取组件类
|
||||||
*/
|
*/
|
||||||
@ -158,7 +157,7 @@ export interface NodeInstance<T = ComponentInstance> {
|
|||||||
/**
|
/**
|
||||||
* 组件类定义
|
* 组件类定义
|
||||||
*/
|
*/
|
||||||
export type Component = ComponentType<any> | object;
|
export type Component = ComponentType<any> | object | string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 组件实例定义
|
* 组件实例定义
|
||||||
|
|||||||
@ -16,11 +16,8 @@ interface ArraySetterState {
|
|||||||
interface ArraySetterProps {
|
interface ArraySetterProps {
|
||||||
value: any[];
|
value: any[];
|
||||||
field: SettingField;
|
field: SettingField;
|
||||||
itemConfig?: {
|
itemSetter?: SetterType;
|
||||||
setter?: SetterType;
|
columns?: FieldConfig[];
|
||||||
defaultValue?: any | ((field: SettingField) => any);
|
|
||||||
required?: boolean;
|
|
||||||
};
|
|
||||||
multiValue?: boolean;
|
multiValue?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,8 +42,8 @@ export class ListSetter extends Component<ArraySetterProps, ArraySetterState> {
|
|||||||
if (newLength > originLength) {
|
if (newLength > originLength) {
|
||||||
for (let i = originLength; i < newLength; i++) {
|
for (let i = originLength; i < newLength; i++) {
|
||||||
const item = field.createField({
|
const item = field.createField({
|
||||||
...props.itemConfig,
|
|
||||||
name: i,
|
name: i,
|
||||||
|
setter: props.itemSetter,
|
||||||
forceInline: 2,
|
forceInline: 2,
|
||||||
});
|
});
|
||||||
items[i] = item;
|
items[i] = item;
|
||||||
@ -86,16 +83,16 @@ export class ListSetter extends Component<ArraySetterProps, ArraySetterState> {
|
|||||||
private scrollToLast: boolean = false;
|
private scrollToLast: boolean = false;
|
||||||
onAdd() {
|
onAdd() {
|
||||||
const { items, itemsMap } = this.state;
|
const { items, itemsMap } = this.state;
|
||||||
const { itemConfig } = this.props;
|
const { itemSetter } = this.props;
|
||||||
const defaultValue = itemConfig ? itemConfig.defaultValue : null;
|
const initialValue = typeof itemSetter === 'object' ? (itemSetter as any).initialValue : null;
|
||||||
const item = this.props.field.createField({
|
const item = this.props.field.createField({
|
||||||
...itemConfig,
|
|
||||||
name: items.length,
|
name: items.length,
|
||||||
forceInline: 1,
|
setter: itemSetter,
|
||||||
|
forceInline: 2,
|
||||||
});
|
});
|
||||||
items.push(item);
|
items.push(item);
|
||||||
itemsMap.set(item.id, item);
|
itemsMap.set(item.id, item);
|
||||||
item.setValue(typeof defaultValue === 'function' ? defaultValue(item) : defaultValue);
|
item.setValue(typeof initialValue === 'function' ? initialValue(item) : initialValue);
|
||||||
this.scrollToLast = true;
|
this.scrollToLast = true;
|
||||||
this.setState({
|
this.setState({
|
||||||
items: items.slice(),
|
items: items.slice(),
|
||||||
@ -132,13 +129,11 @@ export class ListSetter extends Component<ArraySetterProps, ArraySetterState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
// mini Button: depends popup
|
let columns: any = null;
|
||||||
if (this.props.itemConfig) {
|
if (this.props.columns) {
|
||||||
// check is ObjectSetter then check if show columns
|
columns = this.props.columns.map(column => <Title title={column.title || (column.name as string)} />);
|
||||||
}
|
}
|
||||||
|
|
||||||
console.info(this.state.items);
|
|
||||||
|
|
||||||
const { items } = this.state;
|
const { items } = this.state;
|
||||||
const scrollToLast = this.scrollToLast;
|
const scrollToLast = this.scrollToLast;
|
||||||
this.scrollToLast = false;
|
this.scrollToLast = false;
|
||||||
@ -172,6 +167,7 @@ export class ListSetter extends Component<ArraySetterProps, ArraySetterState> {
|
|||||||
<span>添加</span>
|
<span>添加</span>
|
||||||
</Button>
|
</Button>
|
||||||
</div>*/}
|
</div>*/}
|
||||||
|
{columns && <div className="lc-setter-list-columns">{columns}</div>}
|
||||||
{content}
|
{content}
|
||||||
<Button className="lc-setter-list-add" type="primary" onClick={this.onAdd.bind(this)}>
|
<Button className="lc-setter-list-add" type="primary" onClick={this.onAdd.bind(this)}>
|
||||||
<Icon type="add" />
|
<Icon type="add" />
|
||||||
@ -226,7 +222,6 @@ export default class ArraySetter extends Component<{
|
|||||||
itemConfig?: {
|
itemConfig?: {
|
||||||
setter?: SetterType;
|
setter?: SetterType;
|
||||||
defaultValue?: any | ((field: SettingField) => any);
|
defaultValue?: any | ((field: SettingField) => any);
|
||||||
required?: boolean;
|
|
||||||
};
|
};
|
||||||
mode?: 'popup' | 'list';
|
mode?: 'popup' | 'list';
|
||||||
forceInline?: boolean;
|
forceInline?: boolean;
|
||||||
@ -237,6 +232,20 @@ export default class ArraySetter extends Component<{
|
|||||||
render() {
|
render() {
|
||||||
const { mode, forceInline, ...props } = this.props;
|
const { mode, forceInline, ...props } = this.props;
|
||||||
const { field, itemConfig } = props;
|
const { field, itemConfig } = props;
|
||||||
|
let columns: FieldConfig[] | undefined;
|
||||||
|
const setter: any = itemConfig?.setter;
|
||||||
|
if (setter?.componentName === 'ObjectSetter') {
|
||||||
|
const items: FieldConfig[] = setter.props?.config?.items;
|
||||||
|
if (items && Array.isArray(items)) {
|
||||||
|
columns = items.filter(item => item.isRequired || item.important);
|
||||||
|
if (columns.length === 3) {
|
||||||
|
columns = columns.slice(0, 3);
|
||||||
|
} else if (columns.length > 3) {
|
||||||
|
columns = columns.slice(0, 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (mode === 'popup' || forceInline) {
|
if (mode === 'popup' || forceInline) {
|
||||||
const title = (
|
const title = (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
@ -246,26 +255,22 @@ export default class ArraySetter extends Component<{
|
|||||||
);
|
);
|
||||||
if (!this.pipe) {
|
if (!this.pipe) {
|
||||||
let width = 360;
|
let width = 360;
|
||||||
const setter: any = itemConfig?.setter;
|
if (columns) {
|
||||||
if (setter?.componentName === 'ObjectSetter') {
|
if (columns.length === 3) {
|
||||||
const items: FieldConfig[] = setter.props?.config?.items;
|
width = 480;
|
||||||
if (items && Array.isArray(items)) {
|
} else if (columns.length > 3) {
|
||||||
const length = items.filter(item => item.required || item.important).length;
|
width = 600;
|
||||||
if (length === 3) {
|
|
||||||
width = 480;
|
|
||||||
} else if (length > 3) {
|
|
||||||
width = 600;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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} />,
|
<TableSetter key={field.id} {...props} columns={columns} />,
|
||||||
title,
|
title,
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
|
type={forceInline ? 'normal' : 'primary'}
|
||||||
onClick={e => {
|
onClick={e => {
|
||||||
this.pipe.show((e as any).target, field.id);
|
this.pipe.show((e as any).target, field.id);
|
||||||
}}
|
}}
|
||||||
@ -275,7 +280,7 @@ export default class ArraySetter extends Component<{
|
|||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return <ListSetter {...props} />;
|
return <ListSetter {...props} columns={columns?.slice(0, 2)} />;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,6 +18,18 @@
|
|||||||
margin-top: 8px;;
|
margin-top: 8px;;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.lc-setter-list-columns {
|
||||||
|
display: flex;
|
||||||
|
> .lc-title {
|
||||||
|
flex: 1;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
margin-left: 47px;
|
||||||
|
margin-right: 28px;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
.lc-setter-list-scroll-body {
|
.lc-setter-list-scroll-body {
|
||||||
margin: -8px -5px;
|
margin: -8px -5px;
|
||||||
padding: 8px 10px;
|
padding: 8px 10px;
|
||||||
|
|||||||
@ -34,7 +34,6 @@ interface ObjectSetterConfig {
|
|||||||
items?: FieldConfig[];
|
items?: FieldConfig[];
|
||||||
extraConfig?: {
|
extraConfig?: {
|
||||||
setter?: SetterType;
|
setter?: SetterType;
|
||||||
defaultValue?: any | ((field: SettingField, editor: any) => any);
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,7 +61,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.required || conf.important) {
|
if (conf.isRequired || conf.important) {
|
||||||
const item = field.createField({
|
const item = field.createField({
|
||||||
...conf,
|
...conf,
|
||||||
// in column-cell
|
// in column-cell
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import SettingsPane, { registerSetter, createSetterContent, getSetter, createSet
|
|||||||
import Node from '../../designer/src/designer/document/node/node';
|
import Node from '../../designer/src/designer/document/node/node';
|
||||||
import ArraySetter from './builtin-setters/array-setter';
|
import ArraySetter from './builtin-setters/array-setter';
|
||||||
import ObjectSetter from './builtin-setters/object-setter';
|
import ObjectSetter from './builtin-setters/object-setter';
|
||||||
|
import './register-transducer';
|
||||||
|
|
||||||
export default class SettingsMainView extends Component {
|
export default class SettingsMainView extends Component {
|
||||||
private main: SettingsMain;
|
private main: SettingsMain;
|
||||||
@ -31,9 +32,9 @@ export default class SettingsMainView extends Component {
|
|||||||
if (this.main.isMulti) {
|
if (this.main.isMulti) {
|
||||||
return (
|
return (
|
||||||
<div className="lc-settings-navigator">
|
<div className="lc-settings-navigator">
|
||||||
{this.main.componentType!.icon || <Icon type="ellipsis" size="small" />}
|
{this.main.componentMeta!.icon || <Icon type="ellipsis" size="small" />}
|
||||||
<span>
|
<span>
|
||||||
{this.main.componentType!.title} x {this.main.nodes.length}
|
{this.main.componentMeta!.title} x {this.main.nodes.length}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@ -57,7 +58,7 @@ export default class SettingsMainView extends Component {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="lc-settings-navigator">
|
<div className="lc-settings-navigator">
|
||||||
{this.main.componentType!.icon || <Icon type="ellipsis" size="small" />}
|
{this.main.componentMeta!.icon || <Icon type="ellipsis" size="small" />}
|
||||||
<Breadcrumb className="lc-settings-node-breadcrumb">{items}</Breadcrumb>
|
<Breadcrumb className="lc-settings-node-breadcrumb">{items}</Breadcrumb>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { EventEmitter } from 'events';
|
import { EventEmitter } from 'events';
|
||||||
import { uniqueId } from '../../utils/unique-id';
|
import { uniqueId } from '../../utils/unique-id';
|
||||||
import { ComponentType } from '../../designer/src/designer/component-type';
|
import { ComponentMeta } from '../../designer/src/designer/component-meta';
|
||||||
import Node from '../../designer/src/designer/document/node/node';
|
import Node from '../../designer/src/designer/document/node/node';
|
||||||
import { TitleContent } from './title';
|
import { TitleContent } from './title';
|
||||||
import { ReactElement, ComponentType as ReactComponentType, isValidElement } from 'react';
|
import { ReactElement, ComponentType as ReactComponentType, isValidElement } from 'react';
|
||||||
@ -12,7 +12,7 @@ export interface SettingTarget {
|
|||||||
// 所设置的节点集,至少一个
|
// 所设置的节点集,至少一个
|
||||||
readonly nodes: Node[];
|
readonly nodes: Node[];
|
||||||
|
|
||||||
readonly componentType: ComponentType | null;
|
readonly componentMeta: ComponentMeta | null;
|
||||||
|
|
||||||
readonly items: Array<SettingField | CustomView>;
|
readonly items: Array<SettingField | CustomView>;
|
||||||
|
|
||||||
@ -92,6 +92,8 @@ export interface SetterConfig {
|
|||||||
*/
|
*/
|
||||||
props?: object | DynamicProps;
|
props?: object | DynamicProps;
|
||||||
children?: any;
|
children?: any;
|
||||||
|
isRequired?: boolean;
|
||||||
|
initialValue?: any | ((field: SettingField) => any);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -103,7 +105,7 @@ export interface FieldExtraProps {
|
|||||||
/**
|
/**
|
||||||
* 是否必填参数
|
* 是否必填参数
|
||||||
*/
|
*/
|
||||||
required?: boolean;
|
isRequired?: boolean;
|
||||||
/**
|
/**
|
||||||
* default value of target prop for setter use
|
* default value of target prop for setter use
|
||||||
*/
|
*/
|
||||||
@ -172,7 +174,7 @@ export class SettingField implements SettingTarget {
|
|||||||
readonly isOne: boolean;
|
readonly isOne: boolean;
|
||||||
readonly isNone: boolean;
|
readonly isNone: boolean;
|
||||||
readonly nodes: Node[];
|
readonly nodes: Node[];
|
||||||
readonly componentType: ComponentType | null;
|
readonly componentMeta: ComponentMeta | null;
|
||||||
readonly designer: Designer;
|
readonly designer: Designer;
|
||||||
readonly top: SettingTarget;
|
readonly top: SettingTarget;
|
||||||
get path() {
|
get path() {
|
||||||
@ -212,7 +214,7 @@ export class SettingField implements SettingTarget {
|
|||||||
// copy parent properties
|
// copy parent properties
|
||||||
this.editor = parent.editor;
|
this.editor = parent.editor;
|
||||||
this.nodes = parent.nodes;
|
this.nodes = parent.nodes;
|
||||||
this.componentType = parent.componentType;
|
this.componentMeta = parent.componentMeta;
|
||||||
this.isSame = parent.isSame;
|
this.isSame = parent.isSame;
|
||||||
this.isMulti = parent.isMulti;
|
this.isMulti = parent.isMulti;
|
||||||
this.isOne = parent.isOne;
|
this.isOne = parent.isOne;
|
||||||
@ -369,7 +371,7 @@ export class SettingsMain implements SettingTarget {
|
|||||||
private _nodes: Node[] = [];
|
private _nodes: Node[] = [];
|
||||||
private _items: Array<SettingField | CustomView> = [];
|
private _items: Array<SettingField | CustomView> = [];
|
||||||
private _sessionId = '';
|
private _sessionId = '';
|
||||||
private _componentType: ComponentType | null = null;
|
private _componentMeta: ComponentMeta | null = null;
|
||||||
private _isSame: boolean = true;
|
private _isSame: boolean = true;
|
||||||
readonly path = [];
|
readonly path = [];
|
||||||
readonly top: SettingTarget = this;
|
readonly top: SettingTarget = this;
|
||||||
@ -378,8 +380,8 @@ export class SettingsMain implements SettingTarget {
|
|||||||
return this._nodes;
|
return this._nodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
get componentType() {
|
get componentMeta() {
|
||||||
return this._componentType;
|
return this._componentMeta;
|
||||||
}
|
}
|
||||||
|
|
||||||
get items() {
|
get items() {
|
||||||
@ -506,7 +508,7 @@ export class SettingsMain implements SettingTarget {
|
|||||||
this._sessionId = sessionId;
|
this._sessionId = sessionId;
|
||||||
|
|
||||||
// setups
|
// setups
|
||||||
this.setupComponentType();
|
this.setupComponentMeta();
|
||||||
|
|
||||||
// todo: enhance when componentType not changed do merge
|
// todo: enhance when componentType not changed do merge
|
||||||
// clear fields
|
// clear fields
|
||||||
@ -521,36 +523,36 @@ export class SettingsMain implements SettingTarget {
|
|||||||
this._items = [];
|
this._items = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
private setupComponentType() {
|
private setupComponentMeta() {
|
||||||
if (this.nodes.length < 1) {
|
if (this.nodes.length < 1) {
|
||||||
this._isSame = false;
|
this._isSame = false;
|
||||||
this._componentType = null;
|
this._componentMeta = null;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const first = this.nodes[0];
|
const first = this.nodes[0];
|
||||||
const type = first.componentType;
|
const meta = first.componentMeta;
|
||||||
const l = this.nodes.length;
|
const l = this.nodes.length;
|
||||||
let theSame = true;
|
let theSame = true;
|
||||||
for (let i = 1; i < l; i++) {
|
for (let i = 1; i < l; i++) {
|
||||||
const other = this.nodes[i];
|
const other = this.nodes[i];
|
||||||
if ((other as any).componentType !== type) {
|
if ((other as any).componentType !== meta) {
|
||||||
theSame = false;
|
theSame = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (theSame) {
|
if (theSame) {
|
||||||
this._isSame = true;
|
this._isSame = true;
|
||||||
this._componentType = type;
|
this._componentMeta = meta;
|
||||||
} else {
|
} else {
|
||||||
this._isSame = false;
|
this._isSame = false;
|
||||||
this._componentType = null;
|
this._componentMeta = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private setupItems() {
|
private setupItems() {
|
||||||
this.disposeItems();
|
this.disposeItems();
|
||||||
if (this.componentType) {
|
if (this.componentMeta) {
|
||||||
this._items = this.componentType.configure.map(item => {
|
this._items = this.componentMeta.configure.map(item => {
|
||||||
if (isCustomView(item)) {
|
if (isCustomView(item)) {
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|||||||
459
packages/plugin-settings/src/register-transducer.ts
Normal file
459
packages/plugin-settings/src/register-transducer.ts
Normal file
@ -0,0 +1,459 @@
|
|||||||
|
import {
|
||||||
|
PropConfig,
|
||||||
|
PropType,
|
||||||
|
Shape,
|
||||||
|
OneOf,
|
||||||
|
ObjectOf,
|
||||||
|
ArrayOf,
|
||||||
|
OneOfType,
|
||||||
|
} from '../../designer/src/designer/prop-config';
|
||||||
|
import { SetterType, FieldConfig, SettingField } from './main';
|
||||||
|
import { registerMetadataTransducer } from '../../designer/src/designer/component-meta';
|
||||||
|
|
||||||
|
export function propConfigToFieldConfig(propConfig: PropConfig): FieldConfig {
|
||||||
|
return {
|
||||||
|
...propConfig,
|
||||||
|
setter: propTypeToSetter(propConfig.propType),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function propTypeToSetter(propType: PropType): SetterType {
|
||||||
|
let typeName: string;
|
||||||
|
let isRequired: boolean | undefined = false;
|
||||||
|
if (typeof propType === 'string') {
|
||||||
|
typeName = propType;
|
||||||
|
} else {
|
||||||
|
typeName = propType.type;
|
||||||
|
isRequired = propType.isRequired;
|
||||||
|
}
|
||||||
|
// TODO: use mixinSetter wrapper
|
||||||
|
switch (typeName) {
|
||||||
|
case 'string':
|
||||||
|
return {
|
||||||
|
componentName: 'StringSetter',
|
||||||
|
isRequired,
|
||||||
|
initialValue: '',
|
||||||
|
};
|
||||||
|
|
||||||
|
case 'number':
|
||||||
|
return {
|
||||||
|
componentName: 'NumberSetter',
|
||||||
|
isRequired,
|
||||||
|
initialValue: 0,
|
||||||
|
};
|
||||||
|
case 'bool':
|
||||||
|
return {
|
||||||
|
componentName: 'NumberSetter',
|
||||||
|
isRequired,
|
||||||
|
initialValue: false,
|
||||||
|
};
|
||||||
|
case 'oneOf':
|
||||||
|
const dataSource = ((propType as OneOf).value || []).map((value, index) => {
|
||||||
|
const t = typeof value;
|
||||||
|
return {
|
||||||
|
label: t === 'string' || t === 'number' || t === 'boolean' ? String(value) : `value ${index}`,
|
||||||
|
value,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
const componentName = dataSource.length > 4 ? 'SelectSetter' : 'RadioSetter';
|
||||||
|
return {
|
||||||
|
componentName,
|
||||||
|
props: { dataSource },
|
||||||
|
isRequired,
|
||||||
|
initialValue: dataSource[0] ? dataSource[0].value : null,
|
||||||
|
};
|
||||||
|
|
||||||
|
case 'element':
|
||||||
|
case 'node':
|
||||||
|
return {
|
||||||
|
// slotSetter
|
||||||
|
componentName: 'NodeSetter',
|
||||||
|
props: {
|
||||||
|
mode: typeName,
|
||||||
|
},
|
||||||
|
isRequired,
|
||||||
|
initialValue: {
|
||||||
|
type: 'JSSlot',
|
||||||
|
value: '',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
case 'shape':
|
||||||
|
case 'exact':
|
||||||
|
const items = (propType as Shape).value.map(item => propConfigToFieldConfig(item));
|
||||||
|
return {
|
||||||
|
componentName: 'ObjectSetter',
|
||||||
|
props: {
|
||||||
|
config: {
|
||||||
|
items,
|
||||||
|
extraSetter: typeName === 'shape' ? propTypeToSetter('any') : null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
isRequired,
|
||||||
|
initialValue: (field: any) => {
|
||||||
|
const data: any = {};
|
||||||
|
items.forEach(item => {
|
||||||
|
let initial = item.defaultValue;
|
||||||
|
if (initial == null && item.setter && typeof item.setter === 'object') {
|
||||||
|
initial = (item.setter as any).initialValue;
|
||||||
|
}
|
||||||
|
data[item.name] = initial ? (typeof initial === 'function' ? initial(field) : initial) : null;
|
||||||
|
});
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
case 'object':
|
||||||
|
case 'objectOf':
|
||||||
|
return {
|
||||||
|
componentName: 'ObjectSetter',
|
||||||
|
props: {
|
||||||
|
config: {
|
||||||
|
extraSetter: propTypeToSetter(typeName === 'objectOf' ? (propType as ObjectOf).value : 'any'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
isRequired,
|
||||||
|
};
|
||||||
|
case 'array':
|
||||||
|
case 'arrayOf':
|
||||||
|
return {
|
||||||
|
componentName: 'ArraySetter',
|
||||||
|
props: {
|
||||||
|
itemSetter: propTypeToSetter(typeName === 'arrayOf' ? (propType as ArrayOf).value : 'any'),
|
||||||
|
},
|
||||||
|
isRequired,
|
||||||
|
initialValue: [],
|
||||||
|
};
|
||||||
|
case 'func':
|
||||||
|
return {
|
||||||
|
componentName: 'FunctionSetter',
|
||||||
|
isRequired,
|
||||||
|
initialValue: {
|
||||||
|
type: 'JSFunction',
|
||||||
|
value: 'function(){}',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
case 'oneOfType':
|
||||||
|
return {
|
||||||
|
componentName: 'MixinSetter',
|
||||||
|
props: {
|
||||||
|
setters: (propType as OneOfType).value.map(item => propTypeToSetter(item)),
|
||||||
|
},
|
||||||
|
isRequired,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
componentName: 'MixinSetter',
|
||||||
|
isRequired,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const EVENT_RE = /^on[A-Z][\w]*$/;
|
||||||
|
|
||||||
|
registerMetadataTransducer(metadata => {
|
||||||
|
if (metadata.configure) {
|
||||||
|
if (Array.isArray(metadata.configure)) {
|
||||||
|
return {
|
||||||
|
...metadata,
|
||||||
|
configure: {
|
||||||
|
props: metadata.configure,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (metadata.configure.props) {
|
||||||
|
return metadata as any;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!metadata.props) {
|
||||||
|
return {
|
||||||
|
...metadata,
|
||||||
|
configure: {
|
||||||
|
props: metadata.configure && Array.isArray(metadata.configure) ? metadata.configure : [],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const { configure = {} } = metadata;
|
||||||
|
const { props = [], component = {}, events = {}, styles = {} } = configure;
|
||||||
|
const supportEvents: string[] | null = (events as any).supportEvents ? null : [];
|
||||||
|
|
||||||
|
metadata.props.forEach(prop => {
|
||||||
|
const { name, propType } = prop;
|
||||||
|
if (name === 'children' && (component.isContainer || (propType === 'node' || propType === 'element' || propType === 'any'))) {
|
||||||
|
if (component.isContainer !== false) {
|
||||||
|
component.isContainer = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (EVENT_RE.test(name) && (propType === 'func' || propType === 'any')) {
|
||||||
|
if (supportEvents) {
|
||||||
|
supportEvents.push(name);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name === 'className' && (propType === 'string' || propType === 'any')) {
|
||||||
|
if ((styles as any).supportClassName == null) {
|
||||||
|
(styles as any).supportClassName = true;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name === 'style' && (propType === 'object' || propType === 'any')) {
|
||||||
|
if ((styles as any).supportInlineStyle == null) {
|
||||||
|
(styles as any).supportInlineStyle = true;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
props.push(propConfigToFieldConfig(prop));
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
return {
|
||||||
|
...metadata,
|
||||||
|
configure: {
|
||||||
|
...configure,
|
||||||
|
props,
|
||||||
|
events,
|
||||||
|
styles,
|
||||||
|
component,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
registerMetadataTransducer((metadata) => {
|
||||||
|
const { configure = {}, componentName } = metadata;
|
||||||
|
const { component = {} } = configure as any;
|
||||||
|
if (!component.nestingRule) {
|
||||||
|
let m;
|
||||||
|
// uri match xx.Group set subcontrolling: true, childWhiteList
|
||||||
|
if ((m = /^(.+)\.Group$/.exec(componentName))) {
|
||||||
|
// component.subControlling = true;
|
||||||
|
if (!component.nestingRule) {
|
||||||
|
component.nestingRule = {
|
||||||
|
childWhitelist: [`${m[1]}`],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// uri match xx.Node set selfControlled: false, parentWhiteList
|
||||||
|
else if ((m = /^(.+)\.Node$/.exec(componentName))) {
|
||||||
|
// component.selfControlled = false;
|
||||||
|
component.nestingRule = {
|
||||||
|
parentWhitelist: [`${m[1]}`, componentName],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// uri match .Item .Node .Option set parentWhiteList
|
||||||
|
else if ((m = /^(.+)\.(Item|Node|Option)$/.exec(componentName))) {
|
||||||
|
component.nestingRule = {
|
||||||
|
parentWhitelist: [`${m[1]}`],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (component.isModal == null && /Dialog/.test(componentName)) {
|
||||||
|
component.isModal = true;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...metadata,
|
||||||
|
configure: {
|
||||||
|
...configure,
|
||||||
|
component,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
registerMetadataTransducer((metadata) => {
|
||||||
|
const { componentName, configure = {} } = metadata;
|
||||||
|
if (componentName === '#frag') {
|
||||||
|
return {
|
||||||
|
...metadata,
|
||||||
|
configure: {
|
||||||
|
...configure,
|
||||||
|
combined: [{
|
||||||
|
name: 'children',
|
||||||
|
title: '内容设置',
|
||||||
|
setter: {
|
||||||
|
componentName: 'MixinSetter',
|
||||||
|
props: {
|
||||||
|
setters: [{
|
||||||
|
componentName: 'StringSetter',
|
||||||
|
initialValue: '',
|
||||||
|
}, {
|
||||||
|
componentName: 'ExpressionSetter',
|
||||||
|
initialValue: {
|
||||||
|
type: 'JSExpression',
|
||||||
|
value: ''
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const { props, events, styles } = configure as any;
|
||||||
|
let supportEvents: any;
|
||||||
|
let isRoot: boolean = false;
|
||||||
|
if (componentName === 'Page' || componentName === 'Component') {
|
||||||
|
isRoot = true;
|
||||||
|
// todo
|
||||||
|
/*
|
||||||
|
supportEvents = [{
|
||||||
|
description: '初始化时',
|
||||||
|
name: 'constructor'
|
||||||
|
}, {
|
||||||
|
description: '装载后',
|
||||||
|
name: 'componentDidMount'
|
||||||
|
}, {
|
||||||
|
description: '更新时',
|
||||||
|
name: 'componentDidMount'
|
||||||
|
}, {
|
||||||
|
description: '卸载时',
|
||||||
|
name: 'componentWillUnmount'
|
||||||
|
}]
|
||||||
|
*/
|
||||||
|
} else {
|
||||||
|
supportEvents = (events?.supportEvents || []).map((event: any) => {
|
||||||
|
return typeof event === 'string' ? {
|
||||||
|
name: event,
|
||||||
|
} : event;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// 通用设置
|
||||||
|
const propsGroup = props || [];
|
||||||
|
propsGroup.push({
|
||||||
|
name: '#generals',
|
||||||
|
title: '通用',
|
||||||
|
items: [{
|
||||||
|
name: 'id',
|
||||||
|
title: 'ID',
|
||||||
|
setter: 'StringSetter',
|
||||||
|
}, {
|
||||||
|
name: 'key',
|
||||||
|
title: 'Key',
|
||||||
|
setter: 'StringSetter',
|
||||||
|
}, {
|
||||||
|
name: 'ref',
|
||||||
|
setter: 'StringSetter',
|
||||||
|
}, {
|
||||||
|
name: '!more',
|
||||||
|
title: '更多',
|
||||||
|
setter: 'PropertiesSetter'
|
||||||
|
}]
|
||||||
|
});
|
||||||
|
const combined = [{
|
||||||
|
title: '属性',
|
||||||
|
name: '#props',
|
||||||
|
items: propsGroup,
|
||||||
|
}];
|
||||||
|
const stylesGroup = [];
|
||||||
|
if (styles?.supportClassName) {
|
||||||
|
stylesGroup.push({
|
||||||
|
name: 'className',
|
||||||
|
title: '类名绑定',
|
||||||
|
setter: 'ClassNameSetter'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (styles?.supportInlineStyle) {
|
||||||
|
stylesGroup.push({
|
||||||
|
name: 'style',
|
||||||
|
title: '行内样式',
|
||||||
|
setter: 'StyleSetter'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (stylesGroup.length > 0) {
|
||||||
|
combined.push({
|
||||||
|
name: '#styles',
|
||||||
|
title: '样式',
|
||||||
|
items: stylesGroup,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (supportEvents) {
|
||||||
|
combined.push({
|
||||||
|
name: '#events',
|
||||||
|
title: '事件',
|
||||||
|
items: [{
|
||||||
|
name: '!events',
|
||||||
|
title: '事件设置',
|
||||||
|
setter: {
|
||||||
|
componentName: 'EventsSetter',
|
||||||
|
props: {
|
||||||
|
definition: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getValue(field: SettingField) {
|
||||||
|
return [];
|
||||||
|
},
|
||||||
|
setValue(field: SettingField) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isRoot) {
|
||||||
|
// todo...
|
||||||
|
} else {
|
||||||
|
combined.push({
|
||||||
|
name: '#advanced',
|
||||||
|
title: '高级',
|
||||||
|
items: [{
|
||||||
|
name: '__condition',
|
||||||
|
title: '条件显示',
|
||||||
|
setter: 'ExpressionSetter'
|
||||||
|
}, {
|
||||||
|
name: '#loop',
|
||||||
|
title: '循环',
|
||||||
|
items: [{
|
||||||
|
name: '__loop',
|
||||||
|
title: '循环数据',
|
||||||
|
setter: {
|
||||||
|
componentName: 'MixinSetter',
|
||||||
|
props: {
|
||||||
|
setters: [{
|
||||||
|
componentName: 'JSONSetter',
|
||||||
|
props: {
|
||||||
|
mode: 'popup',
|
||||||
|
placeholder: '编辑数据'
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
componentName: 'ExpressionSetter',
|
||||||
|
props: {
|
||||||
|
placeholder: '绑定数据'
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
name: '__loopArgs.0',
|
||||||
|
title: '迭代变量名',
|
||||||
|
setter: {
|
||||||
|
componentName: 'StringSetter',
|
||||||
|
placeholder: '默认为 item'
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
name: '__loopArgs.1',
|
||||||
|
title: '索引变量名',
|
||||||
|
setter: {
|
||||||
|
componentName: 'StringSetter',
|
||||||
|
placeholder: '默认为 index'
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
name: 'key',
|
||||||
|
title: 'Key',
|
||||||
|
setter: 'ExpressionSetter',
|
||||||
|
}]
|
||||||
|
}]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...metadata,
|
||||||
|
configure: {
|
||||||
|
...configure,
|
||||||
|
combined,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
Loading…
x
Reference in New Issue
Block a user