Merge branch 2.x-fix-types into 2.x

Title: 补充第二批类型定义 

- 优化 types 类型定义,使得类型注释能在 VSCode 中正确被提示

![](https://cn-hangzhou.oss-cdn.aliyun-inc.com/git/force/uploads/comment/43449/101888317763806785/image.png)

-  参照协议规范,完善所有 schema 和 asset 相关描述
- 将资产包协议的类型定义从 utils 移动到 types 中
- 跟随物料规范,追加 ComponentSort 标识

Link: https://code.aone.alibaba-inc.com/ali-lowcode/ali-lowcode-engine/codereview/7216325
This commit is contained in:
lihao.ylh 2021-12-21 14:43:21 +08:00
commit 9a4da20bf0
13 changed files with 842 additions and 205 deletions

View File

@ -13,5 +13,5 @@ export interface SettingEntry extends SettingTarget {
// 父级
readonly parent: SettingEntry;
get(propName: string | number): SettingEntry | null;
get: (propName: string | number) => SettingEntry | null;
}

View File

@ -18,9 +18,9 @@ export interface IWidgetBaseConfig {
name: string;
/**
* 停靠位置:
* 当 type 为 'Panel' 时自动为 'leftFloatArea';
* 当 type 为 'Widget' 时自动为 'mainArea';
* 其他时候自动为 'leftArea';
* - 当 type 为 'Panel' 时自动为 'leftFloatArea';
* - 当 type 为 'Widget' 时自动为 'mainArea';
* - 其他时候自动为 'leftArea';
*/
area?: IWidgetConfigArea;
props?: Record<string, any>;

View File

@ -1,35 +1,223 @@
import { NpmInfo } from './npm';
import { PropConfig } from './prop-config';
import { Snippet, ComponentMetadata } from './metadata';
import { I18nData } from './i18n';
export interface Package { // 应该被编辑器默认加载,定义组件大包及external资源的信息
package: string; // 包名
version: string; // 包版本号
urls?: string[] | any; // 组件渲染态视图打包后的 CDN url 列表,包含 js 和 css
editUrls?: string[] | any; // 组件编辑态视图打包后的 CDN url 列表,包含 js 和 css
library: string; // 作为全局变量引用时的名称,和webpack output.library字段含义一样,用来定义全局变量名
async?: boolean,
export interface AssetItem {
type: AssetType;
content?: string | null;
device?: string;
level?: AssetLevel;
id?: string;
}
export enum AssetLevel {
// 环境依赖库 比如 react, react-dom
Environment = 1,
// 基础类库,比如 lodash deep fusion antd
Library = 2,
// 主题
Theme = 3,
// 运行时
Runtime = 4,
// 业务组件
Components = 5,
// 应用 & 页面
App = 6,
}
export const AssetLevels = [
AssetLevel.Environment,
AssetLevel.Library,
AssetLevel.Theme,
AssetLevel.Runtime,
AssetLevel.Components,
AssetLevel.App,
];
export type URL = string;
export enum AssetType {
JSUrl = 'jsUrl',
CSSUrl = 'cssUrl',
CSSText = 'cssText',
JSText = 'jsText',
Bundle = 'bundle',
}
export interface AssetBundle {
type: AssetType.Bundle;
level?: AssetLevel;
assets?: Asset | AssetList | null;
}
export type Asset = AssetList | AssetBundle | AssetItem | URL;
export type AssetList = Array<Asset | undefined | null>;
/**
* 资产包协议
*/
export interface AssetsJson {
/**
* 资产包协议版本号
*/
version: string;
/**
* 大包列表,external与package的概念相似,融合在一起
*/
packages?: Package[];
/**
* 所有组件的描述协议列表所有组件的列表
*/
components: Array<ComponentDescription | RemoteComponentDescription>;
/**
* 组件分类列表,用来描述物料面板
* @deprecated 最新版物料面板已不需要此描述
*/
componentList?: ComponentCategory[];
/**
* 业务组件分类列表,用来描述物料面板
* @deprecated 最新版物料面板已不需要此描述
*/
bizComponentList?: ComponentCategory[];
/**
* 用于描述组件面板中的 tab 和 category
*/
sort?: ComponentSort;
}
/**
* 用于描述组件面板中的 tab 和 category
*/
export interface ComponentSort {
/**
* 用于描述组件面板的 tab 项及其排序,例如:["精选组件", "原子组件"]
*/
groupList?: string[];
/**
* 组件面板中同一个 tab 下的不同区间用 category 区分,category 的排序依照 categoryList 顺序排列;
*/
categoryList?: string[];
}
/**
* 定义组件大包及 external 资源的信息
* 应该被编辑器默认加载
*/
export interface Package {
/**
* 包名
*/
package: string;
/**
* 包版本号
*/
version: string;
/**
* 组件渲染态视图打包后的 CDN url 列表,包含 js 和 css
*/
urls?: string[] | any;
/**
* 组件编辑态视图打包后的 CDN url 列表,包含 js 和 css
*/
editUrls?: string[] | any;
/**
* 作为全局变量引用时的名称,和webpack output.library字段含义一样,用来定义全局变量名
*/
library: string;
/**
* @deprecated
*
* @todo 需推进提案 @度城
*/
async?: boolean;
/**
* 组件描述导出名字,可以通过 window[exportName] 获取到组件描述的 Object 内容;
*/
exportName?: string;
}
export interface ComponentCategory { // 组件分类
title: string; // 组件分类title
icon?: string; // 组件分类icon
children?: ComponentItem[] | ComponentCategory[]; // 可能有子分类
/**
* 组件分类
* @deprecated 已被 ComponentMetadata 替代
*/
export interface ComponentCategory {
/**
* 组件分类title
*/
title: string;
/**
* 组件分类icon
*/
icon?: string;
/**
* 可能有子分类
*/
children?: ComponentItem[] | ComponentCategory[];
}
export interface ComponentItem { // 组件
title: string; // 组件title
componentName?: string; // 组件名
icon?: string; // 组件icon
/**
* 组件
* @deprecated 已被 ComponentMetadata 替代
*/
export interface ComponentItem {
/**
* 组件title
*/
title: string;
/**
* 组件名
*/
componentName?: string;
/**
* 组件icon
*/
icon?: string;
/**
* 可用片段
*/
snippets?: Snippet[];
/**
* 一级分组
*/
group?: string | I18nData;
/**
* 二级分组
*/
category?: string | I18nData;
/**
* 组件优先级排序
*/
priority?: number;
}
/**
* 本地物料描述
*/
export interface ComponentDescription extends ComponentMetadata {
/**
* @todo 待补充文档 @jinchan
*/
keywords: string[];
}
/**
* 远程物料描述
*/
export interface RemoteComponentDescription {
exportName: string;
url: string;
/**
* 组件描述导出名字,可以通过 window[exportName] 获取到组件描述的 Object 内容;
*/
exportName?: string;
/**
* 组件描述的资源链接;
*/
url?: string;
/**
* 组件(库)的 npm 信息;
*/
package?: {
npm?: string;
};
}

View File

@ -1,7 +1,11 @@
import { TitleContent } from './title';
import { SetterType, DynamicSetter } from './setter-config';
import { SettingTarget } from './setting-target';
import { LiveTextEditingConfig } from './metadata';
/**
* extra props for field
*/
export interface FieldExtraProps {
/**
* 是否必填参数
@ -15,6 +19,9 @@ export interface FieldExtraProps {
* get value for field
*/
getValue?: (target: SettingTarget, fieldValue: any) => any;
/**
* set value for field
*/
setValue?: (target: SettingTarget, value: any) => void;
/**
* the field conditional show, is not set always true
@ -49,16 +56,20 @@ export interface FieldExtraProps {
* compatiable vision display
*/
display?: 'accordion' | 'inline' | 'block' | 'plain' | 'popup' | 'entry';
liveTextEditing?: {
selector: string;
// 编辑模式 纯文本|段落编辑|文章编辑(默认纯文本,无跟随工具条)
mode?: 'plaintext' | 'paragraph' | 'article';
// 从 contentEditable 获取内容并设置到属性
onSaveContent?: (content: string, prop: any) => any;
};
// @todo 这个 omit 是否合理?
/**
* @todo 待补充文档
*/
liveTextEditing?: Omit<LiveTextEditingConfig, 'propTarget'>;
}
/**
* 属性面板配置
*/
export interface FieldConfig extends FieldExtraProps {
/**
* 面板配置隶属于单个 field 还是分组
*/
type?: 'field' | 'group';
/**
* the name of this setting field, which used in quickEditor
@ -70,6 +81,8 @@ export interface FieldConfig extends FieldExtraProps {
*/
title?: TitleContent;
/**
* 单个属性的 setter 配置
*
* the field body contains when .type = 'field'
*/
setter?: SetterType | DynamicSetter;
@ -79,6 +92,15 @@ export interface FieldConfig extends FieldExtraProps {
items?: FieldConfig[];
/**
* extra props for field
* 其他配置属性(不做流通要求)
*/
extraProps?: FieldExtraProps;
/**
* @deprecated
*/
description?: TitleContent;
/**
* @deprecated
*/
isExtends?: boolean;
}

View File

@ -9,39 +9,107 @@ import { NodeSchema, NodeData, ComponentSchema } from './schema';
import { SettingTarget } from './setting-target';
import { I18nData } from './i18n';
/**
* 嵌套控制函数
*/
export type NestingFilter = (testNode: any, currentNode: any) => boolean;
/**
* 嵌套控制
* 防止错误的节点嵌套,比如 a 嵌套 a, FormField 只能在 Form 容器下,Column 只能在 Table 下等
*/
export interface NestingRule {
// 子级白名单
/**
* 子级白名单
*/
childWhitelist?: string[] | string | RegExp | NestingFilter;
// 父级白名单
/**
* 父级白名单
*/
parentWhitelist?: string[] | string | RegExp | NestingFilter;
// 后裔白名单
/**
* 后裔白名单
*/
descendantWhitelist?: string[] | string | RegExp | NestingFilter;
// 后裔黑名单
/**
* 后裔黑名单
*/
descendantBlacklist?: string[] | string | RegExp | NestingFilter;
// 祖先白名单 可用来做区域高亮
/**
* 祖先白名单 可用来做区域高亮
*/
ancestorWhitelist?: string[] | string | RegExp | NestingFilter;
}
/**
* 组件能力配置
*/
export interface ComponentConfigure {
/**
* 是否容器组件
*/
isContainer?: boolean;
/**
* 组件是否带浮层,浮层组件拖入设计器时会遮挡画布区域,此时应当辅助一些交互以防止阻挡
*/
isModal?: boolean;
/**
* 是否存在渲染的根节点
*/
isNullNode?: boolean;
/**
* 组件树描述信息
*/
descriptor?: string;
/**
* 嵌套控制:防止错误的节点嵌套
* 比如 a 嵌套 a, FormField 只能在 Form 容器下,Column 只能在 Table 下等
*/
nestingRule?: NestingRule;
/**
* 是否是最小渲染单元
* 最小渲染单元下的组件渲染和更新都从单元的根节点开始渲染和更新。如果嵌套了多层最小渲染单元,渲染会从最外层的最小渲染单元开始渲染。
*/
isMinimalRenderUnit?: boolean;
/**
* 组件选中框的 cssSelector
*/
rootSelector?: string;
// copy, move, remove | *
/**
* 禁用的行为,可以为 `'copy'`, `'move'`, `'remove'` 或它们组成的数组
*/
disableBehaviors?: string[] | string;
/**
* 用于详细配置上述操作项的内容
*/
actions?: ComponentAction[];
}
/**
* 可用片段
*
* 内容为组件不同状态下的低代码 schema (可以有多个),用户从组件面板拖入组件到设计器时会向页面 schema 中插入 snippets 中定义的组件低代码 schema
*/
export interface Snippet {
screenshot: string;
label: string;
schema: NodeSchema;
/**
* 组件分类title
*/
title?: string;
/**
* snippet 截图
*/
screenshot?: string;
/**
* snippet 打标
*
* @deprecated 暂未使用
*/
label?: string;
/**
* 待插入的 schema
*/
schema?: NodeSchema;
}
export interface InitialItem {
@ -57,52 +125,110 @@ export interface AutorunItem {
autorun: (target: SettingTarget) => any;
}
/**
* 试验特性配置
*/
export interface Experimental {
/**
* @todo 待补充文档
*/
context?: { [contextInfoName: string]: any };
/**
* @deprecated 使用组件 metadata 上的 snippets 字段即可
*/
snippets?: Snippet[];
/**
* @todo 待补充文档
*/
view?: ComponentType<any>;
transducers?: any; // ? should support
/**
* @todo 待补充文档
*/
transducers?: any;
/**
* @deprecated 用于动态初始化拖拽到设计器里的组件的 prop 的值
*/
initials?: InitialItem[];
/**
* @todo 待补充文档
*/
filters?: FilterItem[];
/**
* @todo 待补充文档
*/
autoruns?: AutorunItem[];
/**
* 配置 callbacks 可捕获引擎抛出的一些事件,例如 onNodeAdd、onResize 等
*/
callbacks?: Callbacks;
/**
* 拖入容器时,自动带入 children 列表
*/
initialChildren?: NodeData[] | ((target: SettingTarget) => NodeData[]);
isAbsoluteLayoutContainer: boolean;
/**
* @todo 待补充文档
*/
isAbsoluteLayoutContainer?: boolean;
/**
* @todo 待补充文档
*/
hideSelectTools?: boolean;
// 样式 及 位置,handle上必须有明确的标识以便事件路由判断,或者主动设置事件独占模式
// NWSE 是交给引擎计算放置位置,ReactElement 必须自己控制初始位置
/**
* 样式 及 位置,handle上必须有明确的标识以便事件路由判断,或者主动设置事件独占模式
* NWSE 是交给引擎计算放置位置,ReactElement 必须自己控制初始位置
*/
/**
* 用于配置设计器中组件 resize 操作工具的样式和内容
* - hover 时控制柄高亮
* - mousedown 时请求独占
* - dragstart 请求通用 resizing 控制 请求 hud 显示
* - drag 时 计算并设置效果,更新控制柄位置
*/
getResizingHandlers?: (
currentNode: any,
) =>
| Array<{
type: 'N' | 'W' | 'S' | 'E' | 'NW' | 'NE' | 'SE' | 'SW';
content?: ReactElement;
propTarget?: string;
appearOn?: 'mouse-enter' | 'mouse-hover' | 'selected' | 'always';
}>
| ReactElement[];
// hover时 控制柄高亮
// mousedown 时请求独占
// dragstart 请求 通用 resizing 控制
// 请求 hud 显示
// drag 时 计算 并 设置效果
// 更新控制柄位置
) => (
| Array<{
type: 'N' | 'W' | 'S' | 'E' | 'NW' | 'NE' | 'SE' | 'SW';
content?: ReactElement;
propTarget?: string;
appearOn?: 'mouse-enter' | 'mouse-hover' | 'selected' | 'always';
}>
| ReactElement[]
);
// 纯文本编辑:如果 children 内容是
// 文本编辑:配置
/**
* Live Text Editing:如果 children 内容是纯文本,支持双击直接编辑
*/
liveTextEditing?: LiveTextEditingConfig[];
/**
* @deprecated 暂未使用
*/
isTopFixed?: boolean;
}
// thinkof Array
/**
* Live Text Editing(如果 children 内容是纯文本,支持双击直接编辑)的可配置项目
*/
export interface LiveTextEditingConfig {
/**
* @todo 待补充文档
*/
propTarget: string;
/**
* @todo 待补充文档
*/
selector?: string;
// 编辑模式 纯文本|段落编辑|文章编辑(默认纯文本,无跟随工具条)
/**
* 编辑模式 纯文本|段落编辑|文章编辑(默认纯文本,无跟随工具条)
* @default 'plaintext'
*/
mode?: 'plaintext' | 'paragraph' | 'article';
// 从 contentEditable 获取内容并设置到属性
/**
* 从 contentEditable 获取内容并设置到属性
*/
onSaveContent?: (content: string, prop: any) => any;
}
@ -112,41 +238,97 @@ export type ConfigureSupportEvent = string | {
description?: string;
};
export type ConfigureSupport = {
/**
* 通用扩展面板支持性配置
*/
export interface ConfigureSupport {
/**
* 支持事件列表
*/
events?: ConfigureSupportEvent[];
/**
* 支持 className 设置
*/
className?: boolean;
/**
* 支持样式设置
*/
style?: boolean;
/**
* 支持生命周期设置
*/
lifecycles?: any[];
// general?: boolean;
/**
* 支持循环设置
*/
loop?: boolean;
/**
* 支持条件式渲染设置
*/
condition?: boolean;
};
}
/**
* 编辑体验配置
*/
export interface Configure {
/**
* 属性面板配置
*/
props?: FieldConfig[];
/**
* 组件能力配置
*/
component?: ComponentConfigure;
/**
* 通用扩展面板支持性配置
*/
supports?: ConfigureSupport;
}
/**
* 动作描述
*/
export interface ActionContentObject {
// 图标
/**
* 图标
*/
icon?: IconType;
// 描述
/**
* 描述
*/
title?: TipContent;
// 执行动作
/**
* 执行动作
*/
action?: (currentNode: any) => void;
}
/**
* @todo 工具条动作
*/
export interface ComponentAction {
// behaviorName
/**
* behaviorName
*/
name: string;
// 菜单名称
/**
* 菜单名称
*/
content: string | ReactNode | ActionContentObject;
// 子集
/**
* 子集
*/
items?: ComponentAction[];
// 显示与否,always: 无法禁用
/**
* 显示与否
* always: 无法禁用
*/
condition?: boolean | ((currentNode: any) => boolean) | 'always';
// 显示在工具条上
/**
* 显示在工具条上
*/
important?: boolean;
}
@ -154,7 +336,13 @@ export function isActionContentObject(obj: any): obj is ActionContentObject {
return obj && typeof obj === 'object';
}
/**
* 组件 meta 配置
*/
export interface ComponentMetadata {
/**
* 组件名
*/
componentName: string;
/**
* unique id
@ -168,29 +356,78 @@ export interface ComponentMetadata {
* svg icon for component
*/
icon?: IconType;
/**
* 组件标签
*/
tags?: string[];
/**
* 组件描述
*/
description?: string;
/**
* 组件文档链接
*/
docUrl?: string;
/**
* 组件快照
*/
screenshot?: string;
/**
* 组件研发模式
*/
devMode?: 'procode' | 'lowcode';
/**
* npm 源引入完整描述对象
*/
npm?: NpmInfo;
/**
* 组件属性信息
*/
props?: PropConfig[];
/**
* 编辑体验增强
*/
configure?: FieldConfig[] | Configure;
/**
* 试验特性配置
*/
experimental?: Experimental;
/**
* @todo 待补充文档
*/
schema?: ComponentSchema;
/**
* 可用片段
*/
snippets?: Snippet[];
/**
* 一级分组
*/
group?: string | I18nData;
/**
* 二级分组
*/
category?: string | I18nData;
/**
* 组件优先级排序
*/
priority?: number;
}
/**
* @todo 待补充文档
*/
export interface TransformedComponentMetadata extends ComponentMetadata {
configure: Configure & { combined?: FieldConfig[] };
}
// handleResizing
/**
* handleResizing
*/
// hooks & events
/**
* 配置 callbacks 可捕获引擎抛出的一些事件,例如 onNodeAdd、onResize 等
*/
export interface Callbacks {
// hooks
onMouseDownHook?: (e: MouseEvent, currentNode: any) => any;
@ -198,7 +435,8 @@ export interface Callbacks {
onClickHook?: (e: MouseEvent, currentNode: any) => any;
// onLocateHook?: (e: any, currentNode: any) => any;
// onAcceptHook?: (currentNode: any, locationData: any) => any;
onMoveHook?: (currentNode: any) => boolean; // thinkof 限制性拖拽
onMoveHook?: (currentNode: any) => boolean;
// thinkof 限制性拖拽
onHoverHook?: (currentNode: any) => boolean;
onChildMoveHook?: (childNode: any, currentNode: any) => boolean;
@ -230,4 +468,4 @@ export interface Callbacks {
},
currentNode: any,
) => void;
}
}

View File

@ -1,10 +1,34 @@
/**
* npm 源引入完整描述对象
*/
export interface NpmInfo {
/**
* 源码组件名称
*/
componentName?: string;
/**
* 源码组件库名
*/
package: string;
/**
* 源码组件版本号
*/
version?: string;
/**
* 是否解构
*/
destructuring?: boolean;
/**
* 源码组件名称
*/
exportName?: string;
/**
* 子组件名
*/
subName?: string;
/**
* 组件路径
*/
main?: string;
}

View File

@ -38,10 +38,28 @@ export interface Exact {
isRequired?: boolean;
}
/**
* 组件属性信息
*/
export interface PropConfig {
/**
* 属性名称
*/
name: string;
/**
* 属性类型
*/
propType: PropType;
/**
* 属性描述
*/
description?: string;
/**
* 属性默认值
*/
defaultValue?: any;
/**
* @deprecated 已被弃用
*/
setter?: any;
}

View File

@ -11,19 +11,47 @@ import { I18nMap } from './i18n';
import { UtilsMap } from './utils';
import { AppConfig } from './app-config';
// 搭建基础协议 - 单个组件树节点描述
// 转换成一个 .jsx 文件内 React Class 类 render 函数返回的 jsx 代码
/**
* 搭建基础协议 - 单个组件树节点描述
*/
export interface NodeSchema {
id?: string;
componentName: string; // 组件名称 必填、首字母大写
props?: PropsMap | PropsList; // 组件属性对象
/**
* 组件名称 必填、首字母大写
*/
componentName: string;
/**
* 组件属性对象
*/
props?: PropsMap | PropsList;
/**
* 组件属性对象
*/
leadingComponents?: string;
condition?: CompositeValue; // 渲染条件
loop?: CompositeValue; // 循环数据
loopArgs?: [string, string]; // 循环迭代对象、索引名称 ["item", "index"]
children?: NodeData | NodeData[]; // 子节点
isLocked?: boolean; // 是否锁定
/**
* 渲染条件
*/
condition?: CompositeValue;
/**
* 循环数据
*/
loop?: CompositeValue;
/**
* 循环迭代对象、索引名称 ["item", "index"]
*/
loopArgs?: [string, string];
/**
* 子节点
*/
children?: NodeData | NodeData[];
/**
* 是否锁定
*/
isLocked?: boolean;
// @todo
// ------- future support -----
conditionGroup?: string;
title?: string;
@ -49,22 +77,53 @@ export function isDOMText(data: any): data is DOMText {
export type DOMText = string;
/**
* 容器结构描述
*/
export interface ContainerSchema extends NodeSchema {
componentName: string; // 'Block' | 'Page' | 'Component';
/**
* 'Block' | 'Page' | 'Component';
*/
componentName: string;
/**
* 文件名称
*/
fileName: string;
/**
* @todo 待文档定义
*/
meta?: Record<string, unknown>;
/**
* 容器初始数据
*/
state?: {
[key: string]: CompositeValue;
};
/**
* 自定义方法设置
*/
methods?: {
[key: string]: JSExpression | JSFunction;
};
/**
* 生命周期对象
*/
lifeCycles?: {
[key: string]: JSExpression | JSFunction;
};
/**
* 样式文件
*/
css?: string;
/**
* 异步数据源配置
*/
dataSource?: DataSource;
/**
* 低代码业务组件默认属性
*/
defaultProps?: CompositeObject;
// @todo propDefinitions
}
/**
@ -91,25 +150,66 @@ export interface BlockSchema extends ContainerSchema {
componentName: 'Block';
}
/**
* @todo
*/
export type RootSchema = PageSchema | ComponentSchema | BlockSchema;
/**
* Slot schema 描述
*/
export interface SlotSchema extends NodeSchema {
componentName: 'Slot';
name?: string;
params?: string[];
}
/**
* 应用描述
*/
export interface ProjectSchema {
version: string;
componentsMap: ComponentsMap;
componentsTree: RootSchema[];
i18n?: I18nMap;
utils?: UtilsMap;
constants?: JSONObject;
css?: string;
dataSource?: DataSource;
config?: AppConfig | Record<string, any>;
id?: string;
/**
* 当前应用协议版本号
*/
version: string;
/**
* 当前应用所有组件映射关系
*/
componentsMap: ComponentsMap;
/**
* 描述应用所有页面、低代码组件的组件树
* 低代码业务组件树描述
* 是长度固定为1的数组, 即数组内仅包含根容器的描述(低代码业务组件容器类型)
*/
componentsTree: RootSchema[];
/**
* 国际化语料
*/
i18n?: I18nMap;
/**
* 应用范围内的全局自定义函数或第三方工具类扩展
*/
utils?: UtilsMap;
/**
* 应用范围内的全局常量
*/
constants?: JSONObject;
/**
* 应用范围内的全局样式
*/
css?: string;
/**
* 当前应用的公共数据源
*/
dataSource?: DataSource;
/**
* 当前应用配置信息
*/
config?: AppConfig | Record<string, any>;
/**
* 当前应用元数据信息
*/
meta?: Record<string, any>;
}

View File

@ -1,6 +1,7 @@
import { ComponentClass, Component, ComponentType, ReactElement, isValidElement } from 'react';
import { TitleContent } from './title';
import { SettingTarget } from './setting-target';
import { CompositeValue } from './value-type';
function isReactClass(obj: any): obj is ComponentClass<any> {
return obj && obj.prototype && (obj.prototype.isReactComponent || obj.prototype instanceof Component);
@ -15,27 +16,56 @@ export type CustomView = ReactElement | ComponentType<any>;
export type DynamicProps = (target: SettingTarget) => Record<string, unknown>;
export type DynamicSetter = (target: SettingTarget) => string | SetterConfig | CustomView;
/**
* 设置器配置
*/
export interface SetterConfig {
// if *string* passed must be a registered Setter Name
/**
* if *string* passed must be a registered Setter Name
* 配置设置器用哪一个 setter
*/
componentName: string | CustomView;
/**
* 传递给 setter 的属性
*
* the props pass to Setter Component
*/
props?: Record<string, unknown> | DynamicProps;
/**
* @deprecated
*/
children?: any;
/**
* 是否必填?
*
* ArraySetter 里有个快捷预览,可以在不打开面板的情况下直接编辑
*/
isRequired?: boolean;
/**
* Setter 的初始值
*
* @todo initialValue 可能要和 defaultValue 二选一
*/
initialValue?: any | ((target: SettingTarget) => any);
/* for MixedSetter */
// for MixedSetter
/**
* 给 MixedSetter 时切换 Setter 展示用的
*/
title?: TitleContent;
// for MixedSetter check this is available
/**
* 给 MixedSetter 用于判断优先选中哪个
*/
condition?: (target: SettingTarget) => boolean;
/**
* 给 MixedSetter,切换值时声明类型
*
* @todo 物料协议推进
*/
valueType?: CompositeValue[];
}
/**
* if *string* passed must be a registered Setter Name, future support blockSchema
*/
// if *string* passed must be a registered Setter Name, future support blockSchema
export type SetterType = SetterConfig | SetterConfig[] | string | CustomView;
export function isSetterConfig(obj: any): obj is SetterConfig {

View File

@ -31,31 +31,55 @@ export interface SettingTarget {
*/
readonly top: SettingTarget;
// 父级
/**
* 父级
*/
readonly parent: SettingTarget;
// 获取当前值
getValue(): any;
/**
* 获取当前值
*/
getValue: () => any;
// 设置当前值
setValue(value: any): void;
/**
* 设置当前值
*/
setValue: (value: any) => void;
// 取得子项
get(propName: string | number): SettingTarget | null;
/**
* 取得子项
*/
get: (propName: string | number) => SettingTarget | null;
// 获取子项属性值
getPropValue(propName: string | number): any;
/**
* 获取子项属性值
*/
getPropValue: (propName: string | number) => any;
// 设置子项属性值
setPropValue(propName: string | number, value: any): void;
/**
* 设置子项属性值
*/
setPropValue: (propName: string | number, value: any) => void;
// 清除已设置值
clearPropValue(propName: string | number): void;
/**
* 清除已设置值
*/
clearPropValue: (propName: string | number) => void;
// 获取顶层附属属性值
getExtraPropValue(propName: string): any;
/**
* 获取顶层附属属性值
*/
getExtraPropValue: (propName: string) => any;
// 设置顶层附属属性值
setExtraPropValue(propName: string, value: any): void;
/**
* 设置顶层附属属性值
*/
setExtraPropValue: (propName: string, value: any) => void;
// @todo 补充 node 定义
/**
* 获取 node 中的第一项
*/
getNode: () => any;
}

View File

@ -3,11 +3,29 @@ import { I18nData, isI18nData } from './i18n';
import { TipContent } from './tip';
import { IconType } from './icon';
/**
* 描述 props 的 setter title
*/
export interface TitleConfig {
/**
* 文字描述
*/
label?: I18nData | ReactNode;
/**
* hover 后的展现内容
*/
tip?: TipContent;
/**
* 文档链接,暂未实现
*/
docUrl?: string;
/**
* 图标
*/
icon?: IconType;
/**
* CSS 类
*/
className?: string;
}

View File

@ -1,6 +1,10 @@
import { NodeSchema, NodeData } from './schema';
// 表达式
/**
* 变量表达式
*
* 表达式内通过 this 对象获取上下文
*/
export interface JSExpression {
type: 'JSExpression';
/**
@ -9,69 +13,93 @@ export interface JSExpression {
value: string;
/**
* 模拟值
*
* @todo 待标准描述
*/
mock?: any;
/** 源码 */
compiled?: string;
}
// 函数
export interface JSFunction {
type: 'JSFunction';
/**
* 表达式字符串
* 源码
*
* @todo 待标准描述
*/
value: string;
compiled?: string;
}
/**
* 事件函数类型
* @see https://yuque.antfin-inc.com/mo/spec/spec-low-code-building-schema#feHTW
*
* 保留与原组件属性、生命周期( React / 小程序)一致的输入参数,并给所有事件函数 binding 统一一致的上下文(当前组件所在容器结构的 this 对象)
*/
export interface JSFunction {
type: 'JSFunction';
/**
* 函数定义,或直接函数表达式
*/
value: string;
/** 源码 */
compiled?: string;
}
// 函数
export interface JSFunction {
type: 'JSFunction';
/**
* 函数字符串
* 源码
*
* @todo 待标准描述
*/
value: string;
compiled?: string;
/**
* 模拟值
*
* @todo 待标准描述
*/
mock?: any;
/**
* 额外扩展属性,如 extType、events
*
* @todo 待标准描述
*/
[key: string]: any;
}
/**
* Slot 函数类型
*
* 通常用于描述组件的某一个属性为 ReactNode 或 Function return ReactNode 的场景。
*/
export interface JSSlot {
type: 'JSSlot';
/**
* @todo 待标准描述
*/
title?: string;
// 函数的入参
/**
* 组件的某一个属性为 Function return ReactNode 时,函数的入参
*
* 其子节点可以通过this[参数名] 来获取对应的参数。
*/
params?: string[];
/**
* 具体的值。
*/
value?: NodeData[] | NodeData;
/**
* @todo 待标准描述
*/
name?: string;
}
/**
* @deprecated
*
* @todo 待文档描述
*/
export interface JSBlock {
type: 'JSBlock';
value: NodeSchema;
}
// JSON 基本类型
/**
* JSON 基本类型
*/
export type JSONValue =
| boolean
| string
@ -85,7 +113,9 @@ export interface JSONObject {
[key: string]: JSONValue;
}
// 复合类型
/**
* 复合类型
*/
export type CompositeValue =
| JSONValue
| JSExpression

View File

@ -1,67 +1,10 @@
import { Package, ComponentCategory, ComponentDescription, RemoteComponentDescription } from '@ali/lowcode-types';
import { AssetItem, AssetType, AssetLevels, Asset, AssetList, AssetBundle, AssetLevel, AssetsJson } from '@ali/lowcode-types';
import { isCSSUrl } from './is-css-url';
import { createDefer } from './create-defer';
import { load, evaluate } from './script';
export interface AssetItem {
type: AssetType;
content?: string | null;
device?: string;
level?: AssetLevel;
id?: string;
}
export enum AssetLevel {
// 环境依赖库 比如 react, react-dom
Environment = 1,
// 基础类库,比如 lodash deep fusion antd
Library = 2,
// 主题
Theme = 3,
// 运行时
Runtime = 4,
// 业务组件
Components = 5,
// 应用 & 页面
App = 6,
}
export const AssetLevels = [
AssetLevel.Environment,
AssetLevel.Library,
AssetLevel.Theme,
AssetLevel.Runtime,
AssetLevel.Components,
AssetLevel.App,
];
export type URL = string;
export enum AssetType {
JSUrl = 'jsUrl',
CSSUrl = 'cssUrl',
CSSText = 'cssText',
JSText = 'jsText',
Bundle = 'bundle',
}
export interface AssetBundle {
type: AssetType.Bundle;
level?: AssetLevel;
assets?: Asset | AssetList | null;
}
export type Asset = AssetList | AssetBundle | AssetItem | URL;
export type AssetList = Array<Asset | undefined | null>;
export interface AssetsJson {
version: string; // 资产包协议版本号
packages?: Package[]; // 大包列表,external与package的概念相似,融合在一起
components: Array<ComponentDescription | RemoteComponentDescription>; // 所有组件的描述协议列表所有组件的列表
componentList?: ComponentCategory[]; // 组件分类列表,用来描述物料面板
bizComponentList?: ComponentCategory[]; // 业务组件分类列表,用来描述物料面板
}
// API 向下兼容
export { AssetItem, AssetType, AssetLevels, Asset, AssetList, AssetBundle, AssetLevel, AssetsJson } from '@ali/lowcode-types';
export function isAssetItem(obj: any): obj is AssetItem {
return obj && obj.type;
@ -104,7 +47,7 @@ export function assetItem(type: AssetType, content?: string | null, level?: Asse
export function megreAssets(assets: AssetsJson, incrementalAssets: AssetsJson): AssetsJson {
if (incrementalAssets.packages) {
assets.packages = [...assets.packages, ...incrementalAssets.packages];
assets.packages = [...(assets.packages || []), ...incrementalAssets.packages];
}
if (incrementalAssets.components) {
@ -122,9 +65,9 @@ function megreAssetsComponentList(assets: AssetsJson, incrementalAssets: AssetsJ
if (incrementalAssets[listName]) {
if (assets[listName]) {
// 根据title进行合并
incrementalAssets[listName].map((item) => {
incrementalAssets[listName]?.map((item) => {
let matchFlag = false;
assets[listName].map((assetItem) => {
assets[listName]?.map((assetItem) => {
if (assetItem.title === item.title) {
assetItem.children = assetItem.children.concat(item.children);
matchFlag = true;
@ -133,7 +76,7 @@ function megreAssetsComponentList(assets: AssetsJson, incrementalAssets: AssetsJ
return assetItem;
});
!matchFlag && assets[listName].push(item);
!matchFlag && assets[listName]?.push(item);
return item;
});
}
@ -319,8 +262,10 @@ export class AssetLoader {
return isUrl ? load(content) : evaluate(content);
}
private async loadAsyncLibrary(asyncLibraryMap) {
const promiseList = []; const libraryKeyList = [];
// todo 补充类型
private async loadAsyncLibrary(asyncLibraryMap: Record<string, any>) {
const promiseList: any[] = [];
const libraryKeyList: any[] = [];
for (const key in asyncLibraryMap) {
// 需要异步加载
if (asyncLibraryMap[key].async) {