feat: add some big features to engine

This commit is contained in:
JackLian 2022-12-22 12:10:00 +08:00 committed by 林熠
parent 89f4b11303
commit c2db198415
507 changed files with 9268 additions and 5211 deletions

View File

@ -32,14 +32,14 @@ sidebar_position: 11
*引擎版本 >= 1.0.16
```typescript
import { common } from '@alilc/lowcode-engine';
import { TransitionType } from '@alilc/lowcode-types';
import { IPublicEnumTransitionType } from '@alilc/lowcode-types';
common.utils.startTransaction(() => {
node1.setProps();
node2.setProps();
node3.setProps();
// ...
}, TransitionType.repaint);
}, IPublicEnumTransitionType.repaint);
```
### createIntl

View File

@ -53,7 +53,7 @@ function saveSchema(schema) {
// 保存 schema 相关操作
}
const saveSampleHotKey = (ctx: ILowCodePluginContext) => {
const saveSampleHotKey = (ctx: IPublicModelPluginContext) => {
return {
name: 'saveSample',
async init() {

View File

@ -3,7 +3,7 @@ title: logger - 日志 API
sidebar_position: 9
---
## 模块简介
引擎日志模块,可以按照 **日志级别 **和** 业务类型 **两个维度来定制日志,基于 [zen-logger](https://web.npm.alibaba-inc.com/package/zen-logger) 封装。
引擎日志模块,可以按照 **日志级别 **和** 业务类型 **两个维度来定制日志,参考 [zen-logger](https://web.npm.alibaba-inc.com/package/zen-logger) 实现进行封装。
> 注:日志级别可以通过 url query 动态调整,详见下方使用示例。
## 变量variables
@ -22,8 +22,8 @@ function debug(args: any[]): void
```
**调用示例**
```typescript
import { logger } from '@alilc/lowcode-engine';
import { Logger } from '@alilc/lowcode-utils';
const logger = new Logger({ level: 'warn', bizName: 'designer:pluginManager' });
logger.log('Awesome Low-Code Engine');
```
## 事件events
@ -31,9 +31,9 @@ logger.log('Awesome Low-Code Engine');
## 使用示例
```typescript
import { logger } from '@alilc/lowcode-engine';
import { Logger } from '@alilc/lowcode-utils';
// 内部实现logger = getLogger({ level: 'warn', bizName: 'designer:pluginManager' })
const logger = new Logger({ level: 'warn', bizName: 'designer:pluginManager' });
// 若在 url query 中增加 `__logConf__` 可改变打印日志级别和限定业务类型日志
// 默认__logConf__=warn:*

View File

@ -30,10 +30,11 @@ material.setAssets(assets);
通过物料中心接口动态引入资产包
```typescript
import { ILowCodePluginContext, material, plugins } from '@alilc/lowcode-engine'
import { material, plugins } from '@alilc/lowcode-engine';
import { IPublicModelPluginContext } from '@alilc/lowcode-types';
// 动态加载 assets
plugins.register((ctx: ILowCodePluginContext) => {
plugins.register((ctx: IPublicModelPluginContext) => {
return {
name: 'ext-assets',
async init() {
@ -88,9 +89,9 @@ material.loadIncrementalAssets(assets2);
在设计器辅助层增加一个扩展 action
**类型定义**
```typescript
function addBuiltinComponentAction(action: ComponentAction): void;
function addBuiltinComponentAction(action: IPublicTypeComponentAction): void;
export interface ComponentAction {
export interface IPublicTypeComponentAction {
/**
* behaviorName
*/
@ -102,7 +103,7 @@ export interface ComponentAction {
/**
* 子集
*/
items?: ComponentAction[];
items?: IPublicTypeComponentAction[];
/**
* 显示与否
* always: 无法禁用
@ -174,7 +175,7 @@ material.removeBuiltinComponentAction('myIconName');
```typescript
function modifyBuiltinComponentAction(
actionName: string,
handle: (action: ComponentAction) => void
handle: (action: IPublicTypeComponentAction) => void
): void;
```
**内置设计器辅助 name**
@ -293,7 +294,7 @@ material.registerMetadataTransducer(addonCombine, 1, 'parse-func');
获取所有物料元数据管道函数
**类型定义**
```typescript
function getRegisteredMetadataTransducers(): MetadataTransducer[];
function getRegisteredMetadataTransducers(): IPublicTypeMetadataTransducer[];
```
**示例**

View File

@ -13,11 +13,11 @@ sidebar_position: 4
#### 类型定义
```typescript
async function register(
pluginConfigCreator: (ctx: ILowCodePluginContext) => ILowCodePluginConfig,
pluginConfigCreator: (ctx: IPublicModelPluginContext) => IPublicTypePluginConfig,
options?: ILowCodeRegisterOptions,
): Promise<void>
```
pluginConfigCreator 是一个 ILowCodePluginConfig 生成函数ILowCodePluginConfig 中包含了该插件的 init / destroy 等钩子函数,以及 exports 函数用于返回插件对外暴露的值。
pluginConfigCreator 是一个 IPublicTypePluginConfig 生成函数IPublicTypePluginConfig 中包含了该插件的 init / destroy 等钩子函数,以及 exports 函数用于返回插件对外暴露的值。
另外pluginConfigCreator 还必须挂载 pluginName 字段,全局确保唯一,否则 register 时会报错,可以选择性挂载 meta 字段,用于描述插件的元数据信息,比如兼容的引擎版本、支持的参数配置、依赖插件声明等。
> 注pluginConfigCreator 挂载 pluginName / meta 可以通过低代码工具链的插件脚手架生成,写如 package.json 后将会自动注入到代码中,具体见 [插件元数据工程化示例](#RO9YY)
@ -26,8 +26,9 @@ pluginConfigCreator 是一个 ILowCodePluginConfig 生成函数ILowCodePlugin
#### 简单示例
```typescript
import { plugins } from '@alilc/lowcode-engine';
import { IPublicModelPluginContext } from '@alilc/lowcode-types';
const builtinPluginRegistry = (ctx: ILowCodePluginContext) => {
const builtinPluginRegistry = (ctx: IPublicModelPluginContext) => {
return {
async init() {
const { skeleton } = ctx;
@ -58,8 +59,9 @@ await plugins.register(builtinPluginRegistry);
#### 使用 exports 示例
```typescript
import { plugins } from '@alilc/lowcode-engine';
import { IPublicModelPluginContext } from '@alilc/lowcode-types';
const pluginA = (ctx: ILowCodePluginContext) => {
const pluginA = (ctx: IPublicModelPluginContext) => {
return {
async init() {},
exports() { return { x: 1, } },
@ -67,7 +69,7 @@ const pluginA = (ctx: ILowCodePluginContext) => {
}
pluginA.pluginName = 'pluginA';
const pluginB = (ctx: ILowCodePluginContext) => {
const pluginB = (ctx: IPublicModelPluginContext) => {
return {
async init() {
// 获取 pluginA 的导出值
@ -83,14 +85,15 @@ pluginB.meta = {
await plugins.register(pluginA);
await plugins.register(pluginB);
```
> 注ctx 是在插件 creator 中获取引擎 API 的上下文,具体定义参见 [ILowCodePluginContext](#qEhTb)
> 注ctx 是在插件 creator 中获取引擎 API 的上下文,具体定义参见 [IPublicModelPluginContext](#qEhTb)
####
#### 设置兼容引擎版本示例
```typescript
import { plugins } from '@alilc/lowcode-engine';
import { IPublicModelPluginContext } from '@alilc/lowcode-types';
const builtinPluginRegistry = (ctx: ILowCodePluginContext) => {
const builtinPluginRegistry = (ctx: IPublicModelPluginContext) => {
return {
async init() {
...
@ -108,8 +111,9 @@ await plugins.register(builtinPluginRegistry);
#### 设置插件参数版本示例
```typescript
import { plugins } from '@alilc/lowcode-engine';
import { IPublicModelPluginContext } from '@alilc/lowcode-types';
const builtinPluginRegistry = (ctx: ILowCodePluginContext, options: any) => {
const builtinPluginRegistry = (ctx: IPublicModelPluginContext, options: any) => {
return {
async init() {
// 1.0.4 之后的传值方式,通过 register(xxx, options)
@ -219,27 +223,27 @@ plugins.delete('builtinPluginRegistry');
## 事件events
## 相关模块
### ILowCodePluginContext
### IPublicModelPluginContext
**类型定义**
```typescript
export interface ILowCodePluginContext {
skeleton: Skeleton; // 参考面板 API
hotkey: Hotkey; // 参考快捷键 API
setters: Setters; // 参考设置器 API
config: EngineConfig; // 参考配置 API
material: Material; // 参考物料 API
event: Event; // 参考事件 API
project: Project; // 参考模型 API
common: Common; // 参考模型 API
logger: Logger; // 参考日志 API
plugins: ILowCodePluginManager; // 即本文档描述内容
export interface IPublicModelPluginContext {
get skeleton(): IPublicApiSkeleton;
get hotkey(): IPublicApiHotkey;
get setters(): IPublicApiSetters;
get config(): IEngineConfig;
get material(): IPublicApiMaterial;
get event(): IPublicApiEvent;
get project(): IPublicApiProject;
get common(): IPublicApiCommon;
logger: IPublicApiLogger;
plugins: IPublicApiPlugins;
preference: IPluginPreferenceMananger;
}
```
### ILowCodePluginConfig
### IPublicTypePluginConfig
**类型定义**
```typescript
export interface ILowCodePluginConfig {
export interface IPublicTypePluginConfig {
init?(): void;
destroy?(): void;
exports?(): any;
@ -263,7 +267,7 @@ your-plugin/package.json
```
转换后的结构:
```json
const debug = (ctx: ILowCodePluginContext, options: any) => {
const debug = (ctx: IPublicModelPluginContext, options: any) => {
return {};
}

View File

@ -79,10 +79,10 @@ addPropsTransducer(transducer: PropsTransducer, stage: TransformStage)
**示例 1在保存的时候删除每一个组件的 props.hidden**
```typescript
import { ILowCodePluginContext, project } from '@alilc/lowcode-engine';
import { CompositeObject, TransformStage } from '@alilc/lowcode-types';
import { project } from '@alilc/lowcode-engine';
import { CompositeObject, TransformStage, IPublicModelPluginContext } from '@alilc/lowcode-types';
export const deleteHiddenTransducer = (ctx: ILowCodePluginContext) => {
export const deleteHiddenTransducer = (ctx: IPublicModelPluginContext) => {
return {
name: 'deleteHiddenTransducer',
async init() {

View File

@ -38,8 +38,9 @@ function registerSetter(
```typescript
import { setters, skeleton } from '@alilc/lowcode-engine';
import { setterMap, pluginMap } from '@alilc/lowcode-engine-ext';
import { IPublicModelPluginContext } from '@alilc/lowcode-types';
const setterRegistry = (ctx: ILowCodePluginContext) => {
const setterRegistry = (ctx: IPublicModelPluginContext) => {
return {
name: 'ext-setters-registry',
async init() {
@ -209,8 +210,9 @@ function registerSetter(
```typescript
import { setters, skeleton } from '@alilc/lowcode-engine';
import { setterMap, pluginMap } from '@alilc/lowcode-engine-ext';
import { IPublicModelPluginContext } from '@alilc/lowcode-types';
const setterRegistry = (ctx: ILowCodePluginContext) => {
const setterRegistry = (ctx: IPublicModelPluginContext) => {
return {
name: 'ext-setters-registry',
async init() {

View File

@ -130,12 +130,13 @@ npm publish
2. 在引擎初始化侧引入插件
```typescript
import Inject, { injectAssets } from '@alilc/lowcode-plugin-inject';
import { IPublicModelPluginContext } from '@alilc/lowcode-types';
export default async () => {
// 注意 Inject 插件必须在其他插件前注册,且所有插件的注册必须 await
await plugins.register(Inject);
await plugins.register(OtherPlugin);
await plugins.register((ctx: ILowCodePluginContext) => {
await plugins.register((ctx: IPublicModelPluginContext) => {
return {
name: "editor-init",
async init() {

View File

@ -270,10 +270,11 @@ npm publish
### 在项目中引入资产包
```typescript
import { ILowCodePluginContext, material, plugins } from '@alilc/lowcode-engine';
import { material, plugins } from '@alilc/lowcode-engine';
import { IPublicModelPluginContext } from '@alilc/lowcode-types';
// 动态加载 assets
plugins.register((ctx: ILowCodePluginContext) => {
plugins.register((ctx: IPublicModelPluginContext) => {
return {
name: 'ext-assets',
async init() {

View File

@ -10,9 +10,10 @@ sidebar_position: 6
```typescript
import { plugins } from '@alilc/lowcode-engine';
import { IPublicModelPluginContext } from '@alilc/lowcode-types';
import { Icon, Message } from '@alifd/next';
const addHelloAction = (ctx: ILowCodePluginContext) => {
const addHelloAction = (ctx: IPublicModelPluginContext) => {
return {
async init() {
const { addBuiltinComponentAction } = ctx.material;
@ -46,8 +47,9 @@ await plugins.register(addHelloAction);
```typescript
import { plugins } from '@alilc/lowcode-engine';
import { IPublicModelPluginContext } from '@alilc/lowcode-types';
const removeCopyAction = (ctx: ILowCodePluginContext) => {
const removeCopyAction = (ctx: IPublicModelPluginContext) => {
return {
async init() {
const { removeBuiltinComponentAction } = ctx.material;

View File

@ -17,9 +17,10 @@ sidebar_position: 5
## 注册插件 API
```typescript
import { plugins, ILowCodePluginContext } from '@alilc/lowcode-engine';
import { plugins } from '@alilc/lowcode-engine';
import { IPublicModelPluginContext } from '@alilc/lowcode-types';
const pluginA = (ctx: ILowCodePluginContext, options: any) => {
const pluginA = (ctx: IPublicModelPluginContext, options: any) => {
return {
init() {
console.log(options.key);

View File

@ -34,10 +34,11 @@ material.setAssets(assets);
也可以通过异步加载物料中心上的物料。
```typescript
import { ILowCodePluginContext, material, plugins } from '@alilc/lowcode-engine';
import { material, plugins } from '@alilc/lowcode-engine';
import { IPublicModelPluginContext } from '@alilc/lowcode-types';
// 动态加载 assets
plugins.register((ctx: ILowCodePluginContext) => {
plugins.register((ctx: IPublicModelPluginContext) => {
return {
name: 'ext-assets',
async init() {
@ -57,7 +58,8 @@ plugins.register((ctx: ILowCodePluginContext) => {
### 配置插件
可以通过 npm 包的方式引入社区插件,配置如下所示:
```typescript
import { ILowCodePluginContext, plugins } from '@alilc/lowcode-engine';
import { plugins } from '@alilc/lowcode-engine';
import { IPublicModelPluginContext } from '@alilc/lowcode-types';
import PluginIssueTracker from '@alilc/lowcode-plugin-issue-tracker';
// 注册一个提 issue 组件到您的编辑器中,方位默认在左栏下侧

View File

@ -125,9 +125,9 @@ Demo 根据**不同的设计器所需要的物料不同**,分为了下面的 8
可以在 demo/sample-plugins 直接新增插件,这里我新增的插件目录是 plugin-demo。并且新增了 index.tsx 文件,将下面的代码粘贴到 index.tsx 中。
```javascript
import * as React from 'react';
import { ILowCodePluginContext } from '@alilc/lowcode-engine';
import { IPublicModelPluginContext } from '@alilc/lowcode-types';
const LowcodePluginPluginDemo = (ctx: ILowCodePluginContext) => {
const LowcodePluginPluginDemo = (ctx: IPublicModelPluginContext) => {
return {
// 插件对外暴露的数据和方法
exports() {

View File

@ -0,0 +1,22 @@
const babelJest = require('babel-jest');
const getBabelConfig = require('build-scripts-config/lib/config/babel/index.js');
const formatWinPath = require('build-scripts-config/lib/config/jest/formatWinPath');
const babelConfig = getBabelConfig();
babelConfig.plugins.push(['@babel/plugin-proposal-class-properties', { loose: true }]);
const jestBabelConfig = {
...babelConfig,
presets: babelConfig.presets.map((preset) => {
if (Array.isArray(preset) && formatWinPath(preset[0]).indexOf('@babel/preset-env') > -1) {
return [preset[0], {
targets: {
node: 'current',
},
}];
}
return preset;
}),
};
module.exports = babelJest.createTransformer(jestBabelConfig);

View File

@ -1,4 +1,8 @@
module.exports = {
transform: {
'^.+\\.(js|jsx|ts|tsx)$': './babelTransform.js',
'^(?!.*\\.(js|jsx|ts|tsx|css|json)$)': 'build-scripts-config/lib/config/jest/fileTransform.js',
},
preset: 'ts-jest',
testEnvironment: 'node',
transformIgnorePatterns: ['/node_modules/(?!core-js)/'],

View File

@ -98,6 +98,7 @@
"qs": "^6.10.1",
"semver": "^7.3.4",
"short-uuid": "^3.1.1",
"babel-jest": "^26.5.2",
"tslib": "^2.3.1"
},
"browser": {
@ -125,11 +126,11 @@
"eslint-plugin-import": "^2.22.1",
"eslint-plugin-react": "^7.22.0",
"eslint-plugin-react-hooks": "^4.2.0",
"jest": "^27.4.7",
"jest": "^26.5.2",
"jest-util": "^27.4.2",
"rimraf": "^3.0.2",
"standard-version": "^9.1.1",
"ts-jest": "^27.1.3",
"ts-jest": "^26.5.2",
"ts-loader": "^6.2.2",
"ts-node": "^8.10.2",
"tsconfig-paths": "^3.9.0",

View File

@ -1,13 +1,13 @@
import type { NodeSchema, CompositeObject } from '@alilc/lowcode-types';
import type { IPublicTypeNodeSchema, IPublicTypeCompositeObject } from '@alilc/lowcode-types';
import type { TComponentAnalyzer } from '../types';
import { handleSubNodes } from '../utils/schema';
export const componentAnalyzer: TComponentAnalyzer = (container) => {
let hasRefAttr = false;
const nodeValidator = (n: NodeSchema) => {
const nodeValidator = (n: IPublicTypeNodeSchema) => {
if (n.props) {
const props = n.props as CompositeObject;
const props = n.props as IPublicTypeCompositeObject;
if (props.ref) {
hasRefAttr = true;
}

View File

@ -9,7 +9,7 @@ import * as path from 'path';
import { getErrorMessage } from '../utils/errors';
import CodeGenerator from '..';
import type { IProjectBuilder } from '..';
import type { ProjectSchema } from '@alilc/lowcode-types';
import type { IPublicTypeProjectSchema } from '@alilc/lowcode-types';
/**
* CLI
@ -131,7 +131,7 @@ function isLocalSolution(solution: string) {
return solution.startsWith('.') || solution.startsWith('/') || solution.startsWith('~');
}
async function loadSchemaFile(schemaFile: string): Promise<ProjectSchema> {
async function loadSchemaFile(schemaFile: string): Promise<IPublicTypeProjectSchema> {
if (!schemaFile) {
throw new Error('invalid schema file name');
}

View File

@ -1,4 +1,4 @@
import { ProjectSchema, ResultFile, ResultDir } from '@alilc/lowcode-types';
import { IPublicTypeProjectSchema, ResultFile, ResultDir } from '@alilc/lowcode-types';
import {
BuilderComponentPlugin,
@ -77,7 +77,7 @@ export function createModuleBuilder(
};
};
const generateModuleCode = async (schema: ProjectSchema | string): Promise<ResultDir> => {
const generateModuleCode = async (schema: IPublicTypeProjectSchema | string): Promise<ResultDir> => {
// Init
const schemaParser: ISchemaParser = new SchemaParser();
const parseResult: IParseResult = schemaParser.parse(schema);

View File

@ -1,4 +1,4 @@
import { ResultDir, ResultFile, ProjectSchema } from '@alilc/lowcode-types';
import { ResultDir, ResultFile, IPublicTypeProjectSchema } from '@alilc/lowcode-types';
import {
IModuleBuilder,
@ -87,13 +87,13 @@ export class ProjectBuilder implements IProjectBuilder {
this.extraContextData = extraContextData;
}
async generateProject(originalSchema: ProjectSchema | string): Promise<ResultDir> {
async generateProject(originalSchema: IPublicTypeProjectSchema | string): Promise<ResultDir> {
// Init
const { schemaParser } = this;
const projectRoot = await this.template.generateTemplate();
let schema: ProjectSchema =
let schema: IPublicTypeProjectSchema =
typeof originalSchema === 'string' ? JSON.parse(originalSchema) : originalSchema;
// Parse / Format

View File

@ -4,14 +4,14 @@
*/
import changeCase from 'change-case';
import {
UtilItem,
IPublicTypeUtilItem,
NodeDataType,
NodeSchema,
ContainerSchema,
ProjectSchema,
PropsMap,
NodeData,
NpmInfo,
IPublicTypeNodeSchema,
IPublicTypeContainerSchema,
IPublicTypeProjectSchema,
IPublicTypePropsMap,
IPublicTypeNodeData,
IPublicTypeNpmInfo,
} from '@alilc/lowcode-types';
import {
IPageMeta,
@ -72,18 +72,18 @@ function getRootComponentName(typeName: string, maps: Record<string, IExternalDe
return typeName;
}
function processChildren(schema: NodeSchema): void {
function processChildren(schema: IPublicTypeNodeSchema): void {
if (schema.props) {
if (Array.isArray(schema.props)) {
// FIXME: is array type props description
} else {
const nodeProps = schema.props as PropsMap;
const nodeProps = schema.props as IPublicTypePropsMap;
if (nodeProps.children) {
if (!schema.children) {
// eslint-disable-next-line no-param-reassign
schema.children = nodeProps.children as NodeDataType;
} else {
let _children: NodeData[] = [];
let _children: IPublicTypeNodeData[] = [];
if (Array.isArray(schema.children)) {
_children = _children.concat(schema.children);
@ -92,9 +92,9 @@ function processChildren(schema: NodeSchema): void {
}
if (Array.isArray(nodeProps.children)) {
_children = _children.concat(nodeProps.children as NodeData[]);
_children = _children.concat(nodeProps.children as IPublicTypeNodeData[]);
} else {
_children.push(nodeProps.children as NodeData);
_children.push(nodeProps.children as IPublicTypeNodeData);
}
// eslint-disable-next-line no-param-reassign
@ -107,7 +107,7 @@ function processChildren(schema: NodeSchema): void {
}
export class SchemaParser implements ISchemaParser {
validate(schema: ProjectSchema): boolean {
validate(schema: IPublicTypeProjectSchema): boolean {
if (SUPPORT_SCHEMA_VERSION_LIST.indexOf(schema.version) < 0) {
throw new CompatibilityError(`Not support schema with version [${schema.version}]`);
}
@ -115,7 +115,7 @@ export class SchemaParser implements ISchemaParser {
return true;
}
parse(schemaSrc: ProjectSchema | string): IParseResult {
parse(schemaSrc: IPublicTypeProjectSchema | string): IParseResult {
// TODO: collect utils depends in JSExpression
const compDeps: Record<string, IExternalDependency> = {};
const internalDeps: Record<string, IInternalDependency> = {};
@ -140,7 +140,7 @@ export class SchemaParser implements ISchemaParser {
let containers: IContainerInfo[];
// Test if this is a lowcode component without container
if (schema.componentsTree.length > 0) {
const firstRoot: ContainerSchema = schema.componentsTree[0] as ContainerSchema;
const firstRoot: IPublicTypeContainerSchema = schema.componentsTree[0] as IPublicTypeContainerSchema;
if (!firstRoot.fileName && !isValidContainerType(firstRoot)) {
// 整个 schema 描述一个容器,且无根节点定义
@ -150,13 +150,13 @@ export class SchemaParser implements ISchemaParser {
props: firstRoot.props || defaultContainer.props,
css: firstRoot.css || defaultContainer.css,
moduleName: (firstRoot as IContainerInfo).moduleName || defaultContainer.moduleName,
children: schema.componentsTree as NodeSchema[],
children: schema.componentsTree as IPublicTypeNodeSchema[],
};
containers = [container];
} else {
// 普通带 1 到多个容器的 schema
containers = schema.componentsTree.map((n) => {
const subRoot = n as ContainerSchema;
const subRoot = n as IPublicTypeContainerSchema;
const container: IContainerInfo = {
...subRoot,
componentName: getRootComponentName(subRoot.componentName, compDeps),
@ -173,7 +173,7 @@ export class SchemaParser implements ISchemaParser {
// 分析引用能力的依赖
containers = containers.map((con) => ({
...con,
analyzeResult: componentAnalyzer(con as ContainerSchema),
analyzeResult: componentAnalyzer(con as IPublicTypeContainerSchema),
}));
// 建立所有容器的内部依赖索引
@ -211,7 +211,7 @@ export class SchemaParser implements ISchemaParser {
handleSubNodes<void>(
container.children,
{
node: (i: NodeSchema) => processChildren(i),
node: (i: IPublicTypeNodeSchema) => processChildren(i),
},
{
rerun: true,
@ -255,12 +255,12 @@ export class SchemaParser implements ISchemaParser {
.filter((dep) => !!dep);
// 分析 Utils 依赖
let utils: UtilItem[];
let utils: IPublicTypeUtilItem[];
if (schema.utils) {
utils = schema.utils;
utilsDeps = schema.utils
.filter(
(u): u is { name: string; type: 'npm' | 'tnpm'; content: NpmInfo } => u.type !== 'function',
(u): u is { name: string; type: 'npm' | 'tnpm'; content: IPublicTypeNpmInfo } => u.type !== 'function',
)
.map(
(u): IExternalDependency => ({
@ -335,7 +335,7 @@ export class SchemaParser implements ISchemaParser {
return handleSubNodes<string>(
children,
{
node: (i: NodeSchema) => i.componentName,
node: (i: IPublicTypeNodeSchema) => i.componentName,
},
{
rerun: true,
@ -343,8 +343,8 @@ export class SchemaParser implements ISchemaParser {
);
}
decodeSchema(schemaSrc: string | ProjectSchema): ProjectSchema {
let schema: ProjectSchema;
decodeSchema(schemaSrc: string | IPublicTypeProjectSchema): IPublicTypeProjectSchema {
let schema: IPublicTypeProjectSchema;
if (typeof schemaSrc === 'string') {
try {
schema = JSON.parse(schemaSrc);
@ -359,7 +359,7 @@ export class SchemaParser implements ISchemaParser {
return schema;
}
private collectDataSourcesTypes(schema: ProjectSchema): string[] {
private collectDataSourcesTypes(schema: IPublicTypeProjectSchema): string[] {
const dataSourcesTypes = new Set<string>();
// 数据源的默认类型为 fetch

View File

@ -1,8 +1,8 @@
/* eslint-disable @typescript-eslint/indent */
import {
CompositeValue,
JSExpression,
IPublicTypeCompositeValue,
IPublicTypeJSExpression,
InterpretDataSourceConfig,
isJSExpression,
isJSFunction,
@ -56,7 +56,7 @@ const pluginFactory: BuilderComponentPluginFactory<PluginConfig> = (config?) =>
(dataSourceConfig && dataSourceConfig.list) || [];
const dataSourceEngineOptions = { runtimeConfig: true };
if (dataSourceItems.length > 0) {
const requestHandlersMap: Record<string, JSExpression> = {};
const requestHandlersMap: Record<string, IPublicTypeJSExpression> = {};
dataSourceItems.forEach((ds) => {
const dsType = ds.type || 'fetch';
@ -178,7 +178,7 @@ _defineDataSourceConfig() {
export default pluginFactory;
function wrapAsFunction(value: CompositeValue, scope: IScope): CompositeValue {
function wrapAsFunction(value: IPublicTypeCompositeValue, scope: IScope): IPublicTypeCompositeValue {
if (isJSExpression(value) || isJSFunction(value)) {
return {
type: 'JSExpression',

View File

@ -1,8 +1,8 @@
import {
NodeSchema,
JSExpression,
NpmInfo,
CompositeValue,
IPublicTypeNodeSchema,
IPublicTypeJSExpression,
IPublicTypeNpmInfo,
IPublicTypeCompositeValue,
isJSExpression,
} from '@alilc/lowcode-types';
@ -86,7 +86,7 @@ const pluginFactory: BuilderComponentPluginFactory<PluginConfig> = (config?) =>
// 2. 小程序出码的时候,很容易出现 Uncaught TypeError: Cannot read property 'avatar' of undefined 这样的异常(如下图的 50 行) -- 因为若直接出码Rax 构建到小程序的时候会立即计算所有在视图中用到的变量
// 3. 通过 this.xxx 能拿到的东西太多了,而且自定义的 methods 可能会无意间破坏 Rax 框架或小程序框架在页面 this 上的东东
const customHandlers: HandlerSet<string> = {
expression(input: JSExpression, scope: IScope) {
expression(input: IPublicTypeJSExpression, scope: IScope) {
return transformJsExpr(generateExpression(input, scope), scope, {
dontWrapEval: !tolerateEvalErrors,
});
@ -171,7 +171,7 @@ const pluginFactory: BuilderComponentPluginFactory<PluginConfig> = (config?) =>
return next;
function generateRaxLoopCtrl(
nodeItem: NodeSchema,
nodeItem: IPublicTypeNodeSchema,
scope: IScope,
config?: NodeGeneratorConfig,
next?: NodePlugin,
@ -218,7 +218,7 @@ function isImportAliasDefineChunk(chunk: ICodeChunk): chunk is ICodeChunk & {
ext: {
aliasName: string;
originalName: string;
dependency: NpmInfo;
dependency: IPublicTypeNpmInfo;
};
} {
return (
@ -226,13 +226,13 @@ function isImportAliasDefineChunk(chunk: ICodeChunk): chunk is ICodeChunk & {
!!chunk.ext &&
typeof chunk.ext.aliasName === 'string' &&
typeof chunk.ext.originalName === 'string' &&
!!(chunk.ext.dependency as NpmInfo | null)?.componentName
!!(chunk.ext.dependency as IPublicTypeNpmInfo | null)?.componentName
);
}
function generateNodeAttrForRax(
this: { cfg: PluginConfig },
attrData: { attrName: string; attrValue: CompositeValue },
attrData: { attrName: string; attrValue: IPublicTypeCompositeValue },
scope: IScope,
config?: NodeGeneratorConfig,
next?: AttrPlugin,
@ -257,7 +257,7 @@ function generateNodeAttrForRax(
function generateEventHandlerAttrForRax(
attrName: string,
attrValue: CompositeValue,
attrValue: IPublicTypeCompositeValue,
scope: IScope,
config?: NodeGeneratorConfig,
): CodePiece[] {

View File

@ -1,8 +1,8 @@
/* eslint-disable @typescript-eslint/indent */
import {
CompositeValue,
JSExpression,
IPublicTypeCompositeValue,
IPublicTypeJSExpression,
InterpretDataSourceConfig,
isJSExpression,
isJSFunction,
@ -72,7 +72,7 @@ const pluginFactory: BuilderComponentPluginFactory<PluginConfig> = (config?) =>
(dataSourceConfig && dataSourceConfig.list) || [];
const dataSourceEngineOptions = { runtimeConfig: true };
if (dataSourceItems.length > 0) {
const requestHandlersMap: Record<string, JSExpression> = {};
const requestHandlersMap: Record<string, IPublicTypeJSExpression> = {};
dataSourceItems.forEach((ds) => {
const dsType = ds.type || 'fetch';
@ -187,7 +187,7 @@ const pluginFactory: BuilderComponentPluginFactory<PluginConfig> = (config?) =>
export default pluginFactory;
function wrapAsFunction(value: CompositeValue, scope: IScope): CompositeValue {
function wrapAsFunction(value: IPublicTypeCompositeValue, scope: IScope): IPublicTypeCompositeValue {
if (isJSExpression(value) || isJSFunction(value)) {
return {
type: 'JSExpression',

View File

@ -16,7 +16,7 @@ import { COMMON_CHUNK_NAME } from '../../../const/generator';
import { createReactNodeGenerator } from '../../../utils/nodeToJSX';
import { Scope } from '../../../utils/Scope';
import { JSExpression } from '@alilc/lowcode-types';
import { IPublicTypeJSExpression } from '@alilc/lowcode-types';
import { generateExpression } from '../../../utils/jsExpression';
import { transformJsExpr } from '../../../core/jsx/handlers/transformJsExpression';
import { transformThis2Context } from '../../../core/jsx/handlers/transformThis2Context';
@ -47,7 +47,7 @@ const pluginFactory: BuilderComponentPluginFactory<PluginConfig> = (config?) =>
// 这里会将内部的一些子上下文的访问(this.xxx)转换为 __$$context.xxx 的形式
// 与 Rax 所不同的是,这里不会将最顶层的 this 转换掉
const customHandlers: HandlerSet<string> = {
expression(input: JSExpression, scope: IScope, config) {
expression(input: IPublicTypeJSExpression, scope: IScope, config) {
return transformJsExpr(generateExpression(input, scope), scope, {
dontWrapEval: !(config?.tolerateEvalErrors ?? tolerateEvalErrors),
dontTransformThis2ContextAtRootScope: true,
@ -120,7 +120,7 @@ const pluginFactory: BuilderComponentPluginFactory<PluginConfig> = (config?) =>
function __$$eval(expr) {
try {
return expr();
} catch (error) {
} catch (error) {
${evalErrorsHandler}
}
}

View File

@ -1,4 +1,4 @@
import { NpmInfo, PackageJSON } from '@alilc/lowcode-types';
import { IPublicTypeNpmInfo, PackageJSON } from '@alilc/lowcode-types';
import { COMMON_CHUNK_NAME } from '../../../../../const/generator';
import {
@ -84,9 +84,9 @@ const pluginFactory: BuilderComponentPluginFactory<RaxFrameworkOptions> = (cfg)
export default pluginFactory;
function getNpmDependencies(project: IProjectInfo): NpmInfo[] {
const npmDeps: NpmInfo[] = [];
const npmNameToPkgMap = new Map<string, NpmInfo>();
function getNpmDependencies(project: IProjectInfo): IPublicTypeNpmInfo[] {
const npmDeps: IPublicTypeNpmInfo[] = [];
const npmNameToPkgMap = new Map<string, IPublicTypeNpmInfo>();
const allDeps = project.packages;

View File

@ -1,5 +1,5 @@
import fetch from 'node-fetch';
import type { ProjectSchema, ResultDir } from '@alilc/lowcode-types';
import type { IPublicTypeProjectSchema, ResultDir } from '@alilc/lowcode-types';
import type { FlattenFile } from './types/file';
declare const Worker: any;
@ -26,7 +26,7 @@ export type Result = ResultDir | FlattenFile[];
export async function generateCode(options: {
solution: 'icejs' | 'rax';
schema: ProjectSchema;
schema: IPublicTypeProjectSchema;
flattenResult?: boolean;
workerJsUrl?: string;
timeoutInMs?: number;

View File

@ -1,5 +1,5 @@
/* eslint-disable no-console */
import type { ProjectSchema } from '@alilc/lowcode-types';
import type { IPublicTypeProjectSchema } from '@alilc/lowcode-types';
import CodeGen from './standalone';
declare const self: any;
@ -13,7 +13,7 @@ self.onmessage = (event: any) => {
self.postMessage({ type: 'ready' });
async function run(msg: { solution: string; schema: ProjectSchema; flattenResult?: boolean }) {
async function run(msg: { solution: string; schema: IPublicTypeProjectSchema; flattenResult?: boolean }) {
try {
print('begin run...');
self.postMessage({ type: 'run:begin' });

View File

@ -1,7 +1,7 @@
import type { ContainerSchema } from '@alilc/lowcode-types';
import type { IPublicTypeContainerSchema } from '@alilc/lowcode-types';
export interface ICompAnalyzeResult {
isUsingRef: boolean;
}
export type TComponentAnalyzer = (container: ContainerSchema) => ICompAnalyzeResult;
export type TComponentAnalyzer = (container: IPublicTypeContainerSchema) => ICompAnalyzeResult;

View File

@ -1,15 +1,15 @@
import {
JSONArray,
JSONObject,
CompositeArray,
CompositeObject,
IPublicTypeJSONArray,
IPublicTypeJSONObject,
IPublicTypeCompositeArray,
IPublicTypeCompositeObject,
ResultDir,
ResultFile,
NodeDataType,
ProjectSchema,
JSExpression,
JSFunction,
JSSlot,
IPublicTypeProjectSchema,
IPublicTypeJSExpression,
IPublicTypeJSFunction,
IPublicTypeJSSlot,
} from '@alilc/lowcode-types';
import { IParseResult } from './intermediate';
@ -96,7 +96,7 @@ export interface ICompiledModule {
export interface IModuleBuilder {
generateModule: (input: unknown) => Promise<ICompiledModule>;
generateModuleCode: (schema: ProjectSchema | string) => Promise<ResultDir>;
generateModuleCode: (schema: IPublicTypeProjectSchema | string) => Promise<ResultDir>;
linkCodeChunks: (chunks: Record<string, ICodeChunk[]>, fileName: string) => ResultFile[];
addPlugin: (plugin: BuilderComponentPlugin) => void;
}
@ -111,16 +111,16 @@ export interface ICodeGenerator {
/**
* Schema
*
* @param {(ProjectSchema)} schema Schema
* @param {(IPublicTypeProjectSchema)} schema Schema
* @returns {ResultDir}
* @memberof ICodeGenerator
*/
toCode: (schema: ProjectSchema) => Promise<ResultDir>;
toCode: (schema: IPublicTypeProjectSchema) => Promise<ResultDir>;
}
export interface ISchemaParser {
validate: (schema: ProjectSchema) => boolean;
parse: (schema: ProjectSchema | string) => IParseResult;
validate: (schema: IPublicTypeProjectSchema) => boolean;
parse: (schema: IPublicTypeProjectSchema | string) => IParseResult;
}
export interface IProjectTemplate {
@ -165,11 +165,11 @@ export interface IProjectBuilderOptions {
}
export interface IProjectBuilder {
generateProject: (schema: ProjectSchema | string) => Promise<ResultDir>;
generateProject: (schema: IPublicTypeProjectSchema | string) => Promise<ResultDir>;
}
/** 项目级别的前置处理器 */
export type ProjectPreProcessor = (schema: ProjectSchema) => Promise<ProjectSchema> | ProjectSchema;
export type ProjectPreProcessor = (schema: IPublicTypeProjectSchema) => Promise<IPublicTypeProjectSchema> | IPublicTypeProjectSchema;
export interface ProjectPostProcessorOptions {
parseResult?: IParseResult;
@ -179,8 +179,8 @@ export interface ProjectPostProcessorOptions {
/** 项目级别的后置处理器 */
export type ProjectPostProcessor = (
result: ResultDir,
schema: ProjectSchema,
originalSchema: ProjectSchema | string,
schema: IPublicTypeProjectSchema,
originalSchema: IPublicTypeProjectSchema | string,
options: ProjectPostProcessorOptions,
) => Promise<ResultDir> | ResultDir;
@ -209,16 +209,16 @@ export type NodeGenerator<T> = (nodeItem: NodeDataType, scope: IScope) => T;
// FIXME: 在新的实现中,添加了第一参数 this: CustomHandlerSet 作为上下文。究其本质
// scopeBindings?: IScopeBindings;
// 这个组合只用来用来处理 CompositeValue 类型,不是这个类型的不要放在这里
// 这个组合只用来用来处理 IPublicTypeCompositeValue 类型,不是这个类型的不要放在这里
export interface HandlerSet<T> {
string?: CompositeTypeGenerator<string, T>;
boolean?: CompositeTypeGenerator<boolean, T>;
number?: CompositeTypeGenerator<number, T>;
expression?: CompositeTypeGenerator<JSExpression, T>;
function?: CompositeTypeGenerator<JSFunction, T>;
slot?: CompositeTypeGenerator<JSSlot, T>;
array?: CompositeTypeGenerator<JSONArray | CompositeArray, T>;
object?: CompositeTypeGenerator<JSONObject | CompositeObject, T>;
expression?: CompositeTypeGenerator<IPublicTypeJSExpression, T>;
function?: CompositeTypeGenerator<IPublicTypeJSFunction, T>;
slot?: CompositeTypeGenerator<IPublicTypeJSSlot, T>;
array?: CompositeTypeGenerator<IPublicTypeJSONArray | IPublicTypeCompositeArray, T>;
object?: CompositeTypeGenerator<IPublicTypeJSONObject | IPublicTypeCompositeObject, T>;
}
export interface CompositeValueGeneratorOptions {

View File

@ -1,4 +1,9 @@
import { I18nMap, UtilsMap, ContainerSchema, JSONObject } from '@alilc/lowcode-types';
import {
IPublicTypeI18nMap,
IPublicTypeUtilsMap,
IPublicTypeContainerSchema,
IPublicTypeJSONObject,
} from '@alilc/lowcode-types';
import { IDependency, INpmPackage } from './deps';
import { ICompAnalyzeResult } from './analyze';
@ -6,7 +11,7 @@ import { ICompAnalyzeResult } from './analyze';
export interface IParseResult {
containers: IContainerInfo[];
globalUtils?: IUtilInfo;
globalI18n?: I18nMap;
globalI18n?: IPublicTypeI18nMap;
globalRouter?: IRouterInfo;
project?: IProjectInfo;
}
@ -15,14 +20,14 @@ export interface IWithDependency {
deps?: IDependency[];
}
export interface IContainerInfo extends ContainerSchema, IWithDependency {
export interface IContainerInfo extends IPublicTypeContainerSchema, IWithDependency {
containerType: string;
moduleName: string;
analyzeResult?: ICompAnalyzeResult;
}
export interface IUtilInfo extends IWithDependency {
utils: UtilsMap;
utils: IPublicTypeUtilsMap;
}
export interface IRouterInfo extends IWithDependency {
@ -45,8 +50,8 @@ export interface IProjectInfo {
css?: string;
containersDeps?: IDependency[];
utilsDeps?: IDependency[];
constants?: JSONObject;
i18n?: I18nMap;
constants?: IPublicTypeJSONObject;
i18n?: IPublicTypeI18nMap;
packages: INpmPackage[];
meta?: { name?: string; title?: string } | Record<string, any>;
config?: Record<string, any>;

View File

@ -1,4 +1,4 @@
import { NodeSchema, CompositeValue } from '@alilc/lowcode-types';
import { IPublicTypeNodeSchema, IPublicTypeCompositeValue } from '@alilc/lowcode-types';
import { HandlerSet, BaseGenerator, NodeGenerator } from './core';
export enum PIECE_TYPE {
@ -17,11 +17,11 @@ export interface CodePiece {
export interface AttrData {
attrName: string;
attrValue: CompositeValue;
attrValue: IPublicTypeCompositeValue;
}
// 对 JSX 出码的理解,目前定制点包含 【包装】【标签名】【属性】
// 对 JSX 出码的理解,目前定制点包含【包装】【标签名】【属性】
export type AttrPlugin = BaseGenerator<AttrData, CodePiece[], NodeGeneratorConfig>;
export type NodePlugin = BaseGenerator<NodeSchema, CodePiece[], NodeGeneratorConfig>;
export type NodePlugin = BaseGenerator<IPublicTypeNodeSchema, CodePiece[], NodeGeneratorConfig>;
export interface NodeGeneratorConfig {
handlers?: HandlerSet<string>;
@ -33,7 +33,7 @@ export interface NodeGeneratorConfig {
/**
* JSExpression
* true
* : 如果容忍异 try-catch -- __$$eval / __$$evalArray
* try-catch -- __$$eval / __$$evalArray
* catch CustomEvent
*/
tolerateEvalErrors?: boolean;

View File

@ -1,13 +1,13 @@
import {
CompositeArray,
CompositeValue,
CompositeObject,
JSFunction,
JSExpression,
IPublicTypeCompositeArray,
IPublicTypeCompositeValue,
IPublicTypeCompositeObject,
IPublicTypeJSFunction,
IPublicTypeJSExpression,
isJSExpression,
isJSFunction,
isJSSlot,
JSSlot,
IPublicTypeJSSlot,
} from '@alilc/lowcode-types';
import _ from 'lodash';
@ -43,7 +43,7 @@ function isDataSource(v: unknown): v is DataSource {
}
function generateArray(
value: CompositeArray,
value: IPublicTypeCompositeArray,
scope: IScope,
options: CompositeValueGeneratorOptions = {},
): string {
@ -52,7 +52,7 @@ function generateArray(
}
function generateObject(
value: CompositeObject,
value: IPublicTypeCompositeObject,
scope: IScope,
options: CompositeValueGeneratorOptions = {},
): string {
@ -88,7 +88,7 @@ function generateBool(value: boolean): string {
return value ? 'true' : 'false';
}
function genFunction(value: JSFunction): string {
function genFunction(value: IPublicTypeJSFunction): string {
const globalVars = parseExpressionGetKeywords(value.value);
if (globalVars.includes('arguments')) {
@ -98,7 +98,7 @@ function genFunction(value: JSFunction): string {
return generateFunction(value, { isArrow: true });
}
function genJsSlot(value: JSSlot, scope: IScope, options: CompositeValueGeneratorOptions = {}) {
function genJsSlot(value: IPublicTypeJSSlot, scope: IScope, options: CompositeValueGeneratorOptions = {}) {
if (options.nodeGenerator) {
return generateJsSlot(value, scope, options.nodeGenerator);
}
@ -106,7 +106,7 @@ function genJsSlot(value: JSSlot, scope: IScope, options: CompositeValueGenerato
}
function generateUnknownType(
value: CompositeValue,
value: IPublicTypeCompositeValue,
scope: IScope,
options: CompositeValueGeneratorOptions = {},
): string {
@ -128,7 +128,7 @@ function generateUnknownType(
// FIXME: 这个是临时方案
// 在遇到 type variable 私有类型时,转换为 JSExpression
if (isVariable(value)) {
const transValue: JSExpression = {
const transValue: IPublicTypeJSExpression = {
type: 'JSExpression',
value: value.variable,
};
@ -188,7 +188,7 @@ function generateUnknownType(
if (options.handlers?.object) {
return executeFunctionStack(value, scope, options.handlers.object, generateObject, options);
}
return generateObject(value as CompositeObject, scope, options);
return generateObject(value as IPublicTypeCompositeObject, scope, options);
}
if (_.isString(value)) {
@ -218,7 +218,7 @@ function generateUnknownType(
// 这一层曾经是对产出做最外层包装的,但其实包装逻辑不应该属于这一层
// 这一层先不去掉,做冗余,方便后续重构
export function generateCompositeType(
value: CompositeValue,
value: IPublicTypeCompositeValue,
scope: IScope,
options: CompositeValueGeneratorOptions = {},
): string {

View File

@ -2,7 +2,7 @@ import * as parser from '@babel/parser';
import generate from '@babel/generator';
import traverse from '@babel/traverse';
import * as t from '@babel/types';
import { JSExpression, JSFunction, isJSExpression, isJSFunction } from '@alilc/lowcode-types';
import { IPublicTypeJSExpression, IPublicTypeJSFunction, isJSExpression, isJSFunction } from '@alilc/lowcode-types';
import { CodeGeneratorError, IScope } from '../types';
import { transformExpressionLocalRef, ParseError } from './expressionParser';
@ -84,7 +84,7 @@ export function isJsCode(value: unknown): boolean {
export function generateExpression(value: any, scope: IScope): string {
if (isJSExpression(value)) {
const exprVal = (value as JSExpression).value.trim();
const exprVal = (value as IPublicTypeJSExpression).value.trim();
if (!exprVal) {
return 'null';
}
@ -113,7 +113,7 @@ export function generateFunction(
},
) {
if (isJsCode(value)) {
const functionCfg = value as JSFunction;
const functionCfg = value as IPublicTypeJSFunction;
if (config.isMember) {
return transformFuncExpr2MethodMember(config.name || '', functionCfg.value);
}

View File

@ -1,4 +1,4 @@
import { JSSlot, isJSSlot, NodeData } from '@alilc/lowcode-types';
import { IPublicTypeJSSlot, isJSSlot, IPublicTypeNodeData } from '@alilc/lowcode-types';
import { CodeGeneratorError, NodeGenerator, IScope } from '../types';
import { unwrapJsExprQuoteInJsx } from './jsxHelpers';
@ -8,7 +8,7 @@ function generateSingleLineComment(commentText: string): string {
export function generateJsSlot(slot: any, scope: IScope, generator: NodeGenerator<string>): string {
if (isJSSlot(slot)) {
const { title, params, value } = slot as JSSlot;
const { title, params, value } = slot as IPublicTypeJSSlot;
// slot 也是分有参数和无参数的
// - 有参数的 slot 就是类似一个 render 函数,需要创建子作用域
@ -39,7 +39,7 @@ export function generateJsSlot(slot: any, scope: IScope, generator: NodeGenerato
}
function generateNodeDataOrArrayForJsSlot(
value: NodeData | NodeData[],
value: IPublicTypeNodeData | IPublicTypeNodeData[],
generator: NodeGenerator<string>,
scope: IScope,
) {

View File

@ -1,6 +1,6 @@
import _ from 'lodash';
import { pipe } from 'fp-ts/function';
import { NodeSchema, isNodeSchema, NodeDataType, CompositeValue } from '@alilc/lowcode-types';
import { IPublicTypeNodeSchema, isNodeSchema, NodeDataType, IPublicTypeCompositeValue } from '@alilc/lowcode-types';
import {
IScope,
@ -57,7 +57,7 @@ export function isPureString(v: string) {
}
function generateAttrValue(
attrData: { attrName: string; attrValue: CompositeValue },
attrData: { attrName: string; attrValue: IPublicTypeCompositeValue },
scope: IScope,
config?: NodeGeneratorConfig,
): CodePiece[] {
@ -76,7 +76,7 @@ function generateAttrValue(
function generateAttr(
attrName: string,
attrValue: CompositeValue,
attrValue: IPublicTypeCompositeValue,
scope: IScope,
config?: NodeGeneratorConfig,
): CodePiece[] {
@ -115,7 +115,7 @@ function generateAttr(
}
function generateAttrs(
nodeItem: NodeSchema,
nodeItem: IPublicTypeNodeSchema,
scope: IScope,
config?: NodeGeneratorConfig,
): CodePiece[] {
@ -144,7 +144,7 @@ function generateAttrs(
}
function generateBasicNode(
nodeItem: NodeSchema,
nodeItem: IPublicTypeNodeSchema,
scope: IScope,
config?: NodeGeneratorConfig,
): CodePiece[] {
@ -160,7 +160,7 @@ function generateBasicNode(
}
function generateSimpleNode(
nodeItem: NodeSchema,
nodeItem: IPublicTypeNodeSchema,
scope: IScope,
config?: NodeGeneratorConfig,
): CodePiece[] {
@ -216,13 +216,13 @@ function linkPieces(pieces: CodePiece[]): string {
}
function generateNodeSchema(
nodeItem: NodeSchema,
nodeItem: IPublicTypeNodeSchema,
scope: IScope,
config?: NodeGeneratorConfig,
): string {
const pieces: CodePiece[] = [];
if (config?.nodePlugins) {
const res = executeFunctionStack<NodeSchema, CodePiece[], NodeGeneratorConfig>(
const res = executeFunctionStack<IPublicTypeNodeSchema, CodePiece[], NodeGeneratorConfig>(
nodeItem,
scope,
config.nodePlugins,
@ -247,11 +247,11 @@ function generateNodeSchema(
* @type NodePlugin Extended
*
* @export
* @param {NodeSchema} nodeItem UI
* @param {IPublicTypeNodeSchema} nodeItem UI
* @returns {CodePiece[]}
*/
export function generateReactLoopCtrl(
nodeItem: NodeSchema,
nodeItem: IPublicTypeNodeSchema,
scope: IScope,
config?: NodeGeneratorConfig,
next?: NodePlugin,
@ -301,11 +301,11 @@ export function generateReactLoopCtrl(
* @type NodePlugin
*
* @export
* @param {NodeSchema} nodeItem UI
* @param {IPublicTypeNodeSchema} nodeItem UI
* @returns {CodePiece[]}
*/
export function generateConditionReactCtrl(
nodeItem: NodeSchema,
nodeItem: IPublicTypeNodeSchema,
scope: IScope,
config?: NodeGeneratorConfig,
next?: NodePlugin,
@ -336,11 +336,11 @@ export function generateConditionReactCtrl(
* @type NodePlugin
*
* @export
* @param {NodeSchema} nodeItem UI
* @param {IPublicTypeNodeSchema} nodeItem UI
* @returns {CodePiece[]}
*/
export function generateReactExprInJS(
nodeItem: NodeSchema,
nodeItem: IPublicTypeNodeSchema,
scope: IScope,
config?: NodeGeneratorConfig,
next?: NodePlugin,

View File

@ -1,20 +1,20 @@
import * as _ from 'lodash';
import {
JSExpression,
NodeData,
NodeSchema,
IPublicTypeJSExpression,
IPublicTypeNodeData,
IPublicTypeNodeSchema,
isJSExpression,
isJSSlot,
isDOMText,
ContainerSchema,
NpmInfo,
CompositeValue,
IPublicTypeContainerSchema,
IPublicTypeNpmInfo,
IPublicTypeCompositeValue,
isNodeSchema,
isJSFunction,
} from '@alilc/lowcode-types';
import { CodeGeneratorError } from '../types/error';
export function isContainerSchema(x: any): x is ContainerSchema {
export function isContainerSchema(x: any): x is IPublicTypeContainerSchema {
return (
typeof x === 'object' &&
x &&
@ -23,7 +23,7 @@ export function isContainerSchema(x: any): x is ContainerSchema {
);
}
export function isNpmInfo(x: any): x is NpmInfo {
export function isNpmInfo(x: any): x is IPublicTypeNpmInfo {
return typeof x === 'object' && x && typeof x.package === 'string';
}
@ -43,11 +43,11 @@ const DEFAULT_MAX_DEPTH = 100000;
* @returns
*/
export function handleSubNodes<T>(
children: NodeSchema['children'],
children: IPublicTypeNodeSchema['children'],
handlers: {
string?: (i: string) => T;
expression?: (i: JSExpression) => T;
node?: (i: NodeSchema) => T;
expression?: (i: IPublicTypeJSExpression) => T;
node?: (i: IPublicTypeNodeSchema) => T;
},
options?: {
rerun?: boolean;
@ -64,7 +64,7 @@ export function handleSubNodes<T>(
}
if (Array.isArray(children)) {
const list: NodeData[] = children as NodeData[];
const list: IPublicTypeNodeData[] = children as IPublicTypeNodeData[];
return list
.map((child) => handleSubNodes(child, handlers, { ...opt, maxDepth: maxDepth - 1 }))
.reduce((p, c) => p.concat(c), []);
@ -84,7 +84,7 @@ export function handleSubNodes<T>(
return handleSubNodes(children.value, handlers, { ...opt, maxDepth: maxDepth - 1 });
} else if (isNodeSchema(children)) {
const handler = handlers.node || noop;
const child = children as NodeSchema;
const child = children as IPublicTypeNodeSchema;
result = handler(child);
if (child.children) {
@ -115,7 +115,7 @@ export function handleSubNodes<T>(
return childrenRes;
function handleCompositeValueInProps(value: CompositeValue): T[] {
function handleCompositeValueInProps(value: IPublicTypeCompositeValue): T[] {
if (isJSSlot(value)) {
return handleSubNodes(value.value, handlers, { ...opt, maxDepth: maxDepth - 1 });
}
@ -125,7 +125,7 @@ export function handleSubNodes<T>(
return _.flatMap(value, (v) => handleCompositeValueInProps(v));
}
// CompositeObject
// IPublicTypeCompositeObject
if (
!isJSExpression(value) &&
!isJSFunction(value) &&
@ -139,7 +139,7 @@ export function handleSubNodes<T>(
}
}
export function isValidContainerType(schema: NodeSchema) {
export function isValidContainerType(schema: IPublicTypeNodeSchema) {
return [
'Page',
'Component',

View File

@ -1,7 +1,7 @@
import CodeGenerator from '../../src';
import * as fs from 'fs';
import * as path from 'path';
import { ProjectSchema } from '@alilc/lowcode-types';
import { IPublicTypeProjectSchema } from '@alilc/lowcode-types';
import { createDiskPublisher } from '../helpers/solutionHelper';
const testCaseBaseName = path.basename(__filename, '.test.ts');
@ -31,7 +31,7 @@ describe(testCaseBaseName, () => {
function exportProject(
importPath: string,
outputPath: string,
mergeSchema?: Partial<ProjectSchema>,
mergeSchema?: Partial<IPublicTypeProjectSchema>,
) {
const schemaJsonStr = fs.readFileSync(importPath, { encoding: 'utf8' });
const schema = { ...JSON.parse(schemaJsonStr), ...mergeSchema };

View File

@ -1,7 +1,7 @@
import CodeGenerator from '../../src';
import * as fs from 'fs';
import * as path from 'path';
import { ProjectSchema } from '@alilc/lowcode-types';
import { IPublicTypeProjectSchema } from '@alilc/lowcode-types';
import { createDiskPublisher } from '../helpers/solutionHelper';
const testCaseBaseName = path.basename(__filename, '.test.ts');
@ -198,7 +198,7 @@ describe(testCaseBaseName, () => {
function exportProject(
importPath: string,
outputPath: string,
mergeSchema?: Partial<ProjectSchema>,
mergeSchema?: Partial<IPublicTypeProjectSchema>,
) {
const schemaJsonStr = fs.readFileSync(importPath, { encoding: 'utf8' });
const schema = { ...JSON.parse(schemaJsonStr), ...mergeSchema };

View File

@ -1,7 +1,7 @@
import CodeGenerator from '../../src';
import * as fs from 'fs';
import * as path from 'path';
import { ProjectSchema } from '@alilc/lowcode-types';
import { IPublicTypeProjectSchema } from '@alilc/lowcode-types';
import { createDiskPublisher } from '../helpers/solutionHelper';
import { IceJsProjectBuilderOptions } from '../../src/solutions/icejs';
@ -33,7 +33,7 @@ describe(testCaseBaseName, () => {
function exportProject(
importPath: string,
outputPath: string,
mergeSchema?: Partial<ProjectSchema>,
mergeSchema?: Partial<IPublicTypeProjectSchema>,
options?: IceJsProjectBuilderOptions,
) {
const schemaJsonStr = fs.readFileSync(importPath, { encoding: 'utf8' });

View File

@ -1,7 +1,7 @@
import CodeGenerator from '../../src';
import * as fs from 'fs';
import * as path from 'path';
import { ProjectSchema } from '@alilc/lowcode-types';
import { IPublicTypeProjectSchema } from '@alilc/lowcode-types';
import { createDiskPublisher } from '../helpers/solutionHelper';
import { IceJsProjectBuilderOptions } from '../../src/solutions/icejs';
@ -28,7 +28,7 @@ test('loop should be generated link __$$evalArray(xxx).map', async () => {
function exportProject(
importPath: string,
outputPath: string,
mergeSchema?: Partial<ProjectSchema>,
mergeSchema?: Partial<IPublicTypeProjectSchema>,
options?: IceJsProjectBuilderOptions,
) {
const schemaJsonStr = fs.readFileSync(importPath, { encoding: 'utf8' });

View File

@ -1,7 +1,7 @@
import CodeGenerator from '../../src';
import * as fs from 'fs';
import * as path from 'path';
import { ProjectSchema } from '@alilc/lowcode-types';
import { IPublicTypeProjectSchema } from '@alilc/lowcode-types';
import { createDiskPublisher } from '../helpers/solutionHelper';
import { IceJsProjectBuilderOptions } from '../../src/solutions/icejs';
@ -32,7 +32,7 @@ test('loop should be generated link __$$evalArray(xxx).map', async () => {
function exportProject(
importPath: string,
outputPath: string,
mergeSchema?: Partial<ProjectSchema>,
mergeSchema?: Partial<IPublicTypeProjectSchema>,
options?: IceJsProjectBuilderOptions,
) {
const schemaJsonStr = fs.readFileSync(importPath, { encoding: 'utf8' });

View File

@ -22,7 +22,7 @@ Object {
function __$$eval(expr) {
try {
return expr();
} catch (error) {
} catch (error) {
}
}
@ -97,7 +97,7 @@ Object {
function __$$eval(expr) {
try {
return expr();
} catch (error) {
} catch (error) {
}
}
@ -168,7 +168,7 @@ Object {
function __$$eval(expr) {
try {
return expr();
} catch (error) {
} catch (error) {
}
}
@ -236,7 +236,7 @@ Object {
function __$$eval(expr) {
try {
return expr();
} catch (error) {
} catch (error) {
}
}

View File

@ -1,11 +1,11 @@
import { ProjectSchema } from '@alilc/lowcode-types';
import { IPublicTypeProjectSchema } from '@alilc/lowcode-types';
import { SchemaParser } from '../../../src';
import SCHEMA_WITH_SLOT from './data/schema-with-slot.json';
describe('tests/public/SchemaParser/p0-basics', () => {
it('should be able to get dependencies in slots', () => {
const schemaParser = new SchemaParser();
const result = schemaParser.parse(SCHEMA_WITH_SLOT as ProjectSchema);
const result = schemaParser.parse(SCHEMA_WITH_SLOT as IPublicTypeProjectSchema);
expect(result.containers.map((c) => c.deps)).toMatchSnapshot();
expect(result.containers[0].deps?.some((dep) => dep.componentName === 'Tooltip')).toBeTruthy();
expect(result.containers[0].deps?.some((dep) => dep.componentName === 'Icon')).toBeTruthy();

View File

@ -13,7 +13,7 @@ import {
import CodeGenerator from '../../../src';
import type { ProjectSchema } from '@alilc/lowcode-types';
import type { IPublicTypeProjectSchema } from '@alilc/lowcode-types';
jest.setTimeout(15 * 1000);
@ -50,7 +50,7 @@ function defineTest(caseDirName: string) {
});
}
async function exportProject(schemaJson: ProjectSchema, targetPath: string, projectName: string) {
async function exportProject(schemaJson: IPublicTypeProjectSchema, targetPath: string, projectName: string) {
const raxAppBuilder = CodeGenerator.solutions.rax();
const result = await raxAppBuilder.generateProject(schemaJson);

View File

@ -12,7 +12,7 @@ import {
import CodeGenerator from '../../../src';
import type { ProjectSchema } from '@alilc/lowcode-types';
import type { IPublicTypeProjectSchema } from '@alilc/lowcode-types';
jest.setTimeout(15 * 1000);
@ -49,7 +49,7 @@ function defineTest(caseDirName: string) {
});
}
async function exportProject(schemaJson: ProjectSchema, targetPath: string, projectName: string) {
async function exportProject(schemaJson: IPublicTypeProjectSchema, targetPath: string, projectName: string) {
const reactAppBuilder = CodeGenerator.solutions.icejs();
const result = await reactAppBuilder.generateProject(schemaJson);

View File

@ -1,10 +1,10 @@
import { NodeData } from '@alilc/lowcode-types';
import { IPublicTypeNodeData } from '@alilc/lowcode-types';
import { handleSubNodes } from '../../../src/utils/schema';
import SCHEMA_WITH_SLOT from './data/schema-with-slot.json';
describe('utils/schema/handleSubNodes', () => {
it('should be able to visit nodes in JSSlot(1)', () => {
const nodes: NodeData[] = [
const nodes: IPublicTypeNodeData[] = [
{
componentName: 'Foo',
props: {
@ -28,7 +28,7 @@ describe('utils/schema/handleSubNodes', () => {
});
it('should be able to visit nodes in JSSlot(2)', () => {
const nodes: NodeData[] = (SCHEMA_WITH_SLOT as any).componentsTree[0].children;
const nodes: IPublicTypeNodeData[] = (SCHEMA_WITH_SLOT as any).componentsTree[0].children;
const result = handleSubNodes(nodes, {
node: (node) => node.componentName,

View File

@ -44,7 +44,6 @@
"@alife/build-plugin-lowcode": "^1.0.17",
"@iceworks/spec": "^1.0.0",
"@types/rax": "^1.0.0",
"build-plugin-component": "^1.0.0",
"driver-universal": "^3.1.0",
"eslint": "^6.8.0",
"rax": "^1.1.0",

View File

@ -34,7 +34,6 @@
"@alife/build-plugin-lowcode": "^1.0.7",
"@alib/build-scripts": "^0.1.3",
"@alifd/adaptor-generate": "^0.1.3",
"build-plugin-component": "^0.2.0",
"build-plugin-fusion": "^0.1.0",
"build-plugin-fusion-cool": "^0.1.0",
"build-plugin-moment-locales": "^0.1.0",

View File

@ -51,7 +51,10 @@
"typescript": "4.6.2",
"yarn": "^1.22.17",
"rimraf": "^3.0.2",
"@types/react-router": "5.1.18"
"@types/react-router": "5.1.18",
"build-plugin-component": "^1.12.0",
"babel-jest": "^26.5.2",
"@alilc/lowcode-test-mate": "^1.0.1"
},
"engines": {
"node": ">=14.17.0 <18"
@ -61,7 +64,6 @@
"lockfile": "enable"
},
"resolutions": {
"@builder/babel-preset-ice": "1.0.1",
"typescript": "4.6.2"
},
"repository": "git@github.com:alibaba/lowcode-engine.git"

View File

@ -0,0 +1,22 @@
const babelJest = require('babel-jest');
const getBabelConfig = require('build-scripts-config/lib/config/babel/index.js');
const formatWinPath = require('build-scripts-config/lib/config/jest/formatWinPath');
const babelConfig = getBabelConfig();
babelConfig.plugins.push(['@babel/plugin-proposal-class-properties', { loose: true }]);
const jestBabelConfig = {
...babelConfig,
presets: babelConfig.presets.map((preset) => {
if (Array.isArray(preset) && formatWinPath(preset[0]).indexOf('@babel/preset-env') > -1) {
return [preset[0], {
targets: {
node: 'current',
},
}];
}
return preset;
}),
};
module.exports = babelJest.createTransformer(jestBabelConfig);

View File

@ -1,9 +1,13 @@
const fs = require('fs');
const { join } = require('path');
const esModules = ['zen-logger'].join('|');
const esModules = [].join('|');
const pkgNames = fs.readdirSync(join('..')).filter(pkgName => !pkgName.startsWith('.'));
const jestConfig = {
transform: {
'^.+\\.(js|jsx|ts|tsx)$': './babelTransform.js',
'^(?!.*\\.(js|jsx|ts|tsx|css|json)$)': 'build-scripts-config/lib/config/jest/fileTransform.js',
},
// transform: {
// '^.+\\.[jt]sx?$': 'babel-jest',
// // '^.+\\.(ts|tsx)$': 'ts-jest',

View File

@ -22,13 +22,10 @@
"react": "^16",
"react-dom": "^16.7.0",
"ric-shim": "^1.0.1",
"semver": "^7.3.5",
"zen-logger": "^1.1.0"
"semver": "^7.3.5"
},
"devDependencies": {
"@alib/build-scripts": "^0.1.29",
"@alilc/lowcode-shell": "1.0.18",
"@alilc/lowcode-test-mate": "^1.0.1",
"@testing-library/react": "^11.2.2",
"@types/classnames": "^2.2.7",
"@types/enzyme": "^3.10.12",
@ -40,9 +37,6 @@
"@types/react": "^16",
"@types/react-dom": "^16",
"@types/semver": "7.3.9",
"babel-jest": "^26.5.2",
"build-plugin-component": "^1.0.0",
"build-scripts-config": "^3.0.3",
"enzyme": "^3.11.0",
"enzyme-adapter-react-16": "^1.15.5",
"jest": "^26.6.3",
@ -54,9 +48,6 @@
"access": "public",
"registry": "https://registry.npmjs.org/"
},
"resolutions": {
"@builder/babel-preset-ice": "1.0.1"
},
"repository": {
"type": "http",
"url": "https://github.com/alibaba/lowcode-engine/tree/main/packages/designer"

View File

@ -2,14 +2,14 @@ import * as React from 'react';
import { Component, Fragment, ReactElement, PureComponent } from 'react';
import classNames from 'classnames';
import { computed, observer, Title, globalLocale } from '@alilc/lowcode-editor-core';
import { I18nData, TitleContent } from '@alilc/lowcode-types';
import { IPublicTypeI18nData, IPublicTypeTitleContent } from '@alilc/lowcode-types';
import { isI18nData } from '@alilc/lowcode-utils';
import { DropLocation } from '../../designer';
import { BuiltinSimulatorHost } from '../../builtin-simulator/host';
import { ParentalNode } from '../../document/node';
import { INode } from '../../document/node';
export class BorderContainerInstance extends PureComponent<{
title: TitleContent;
title: IPublicTypeTitleContent;
rect: DOMRect | null;
scale: number;
scrollX: number;
@ -37,7 +37,7 @@ export class BorderContainerInstance extends PureComponent<{
}
}
function getTitle(title: string | I18nData | ReactElement) {
function getTitle(title: string | IPublicTypeI18nData | ReactElement) {
if (typeof title === 'string') return title;
if (isI18nData(title)) {
const locale = globalLocale.getLocale() || 'zh-CN';
@ -50,9 +50,8 @@ function getTitle(title: string | I18nData | ReactElement) {
export class BorderContainer extends Component<{
host: BuiltinSimulatorHost;
}, {
target?: ParentalNode;
target?: INode;
}> {
state = {} as any;
@computed get scale() {
@ -70,7 +69,7 @@ export class BorderContainer extends Component<{
componentDidMount() {
const { host } = this.props;
host.designer.editor.on('designer.dropLocation.change', (loc: DropLocation) => {
host.designer.editor.eventBus.on('designer.dropLocation.change', (loc: DropLocation) => {
let { target } = this.state;
if (target === loc?.target) return;
this.setState({

View File

@ -1,14 +1,14 @@
import { Component, Fragment, PureComponent } from 'react';
import classNames from 'classnames';
import { computed, observer, Title } from '@alilc/lowcode-editor-core';
import { TitleContent } from '@alilc/lowcode-types';
import { IPublicTypeTitleContent } from '@alilc/lowcode-types';
import { getClosestNode } from '@alilc/lowcode-utils';
import { BuiltinSimulatorHost } from '../host';
export class BorderDetectingInstance extends PureComponent<{
title: TitleContent;
title: IPublicTypeTitleContent;
rect: DOMRect | null;
scale: number;
scrollX: number;

View File

@ -1,6 +1,6 @@
import React, { Component, Fragment } from 'react';
import DragResizeEngine from './drag-resize-engine';
import { observer, computed, globalContext, Editor } from '@alilc/lowcode-editor-core';
import { observer, computed, globalContext } from '@alilc/lowcode-editor-core';
import classNames from 'classnames';
import { SimulatorContext } from '../context';
import { BuiltinSimulatorHost } from '../host';
@ -172,13 +172,14 @@ export class BoxResizingInstance extends Component<{
metadata.configure.advanced.callbacks.onResizeEnd(e, cbNode);
}
const editor = globalContext.get(Editor);
const workspace = globalContext.get('workspace');
const editor = workspace.isActive ? workspace.window.editor : globalContext.get('editor');
const npm = node?.componentMeta?.npm;
const selected =
[npm?.package, npm?.componentName].filter((item) => !!item).join('-') ||
node?.componentMeta?.componentName ||
'';
editor?.emit('designer.border.resize', {
editor?.eventBus.emit('designer.border.resize', {
selected,
layout: node?.parent?.getPropValue('layout') || '',
});

View File

@ -11,7 +11,7 @@ import {
import classNames from 'classnames';
import { observer, computed, Tip, globalContext } from '@alilc/lowcode-editor-core';
import { createIcon, isReactComponent, isActionContentObject } from '@alilc/lowcode-utils';
import { ActionContentObject } from '@alilc/lowcode-types';
import { IPublicTypeActionContentObject } from '@alilc/lowcode-types';
import { BuiltinSimulatorHost } from '../host';
import { OffsetObserver } from '../../designer';
import { Node } from '../../document';
@ -116,7 +116,7 @@ class Toolbar extends Component<{ observed: OffsetObserver }> {
}
}
function createAction(content: ReactNode | ComponentType<any> | ActionContentObject, key: string, node: Node) {
function createAction(content: ReactNode | ComponentType<any> | IPublicTypeActionContentObject, key: string, node: Node) {
if (isValidElement(content)) {
return cloneElement(content, { key, node });
}
@ -131,13 +131,14 @@ function createAction(content: ReactNode | ComponentType<any> | ActionContentObj
className="lc-borders-action"
onClick={() => {
action && action(node);
const editor = globalContext.get('editor');
const workspace = globalContext.get('workspace');
const editor = workspace.isActive ? workspace.window.editor : globalContext.get('editor');
const npm = node?.componentMeta?.npm;
const selected =
[npm?.package, npm?.componentName].filter((item) => !!item).join('-') ||
node?.componentMeta?.componentName ||
'';
editor?.emit('designer.border.action', {
editor?.eventBus.emit('designer.border.action', {
name: key,
selected,
});

View File

@ -1,12 +1,12 @@
import { EventEmitter } from 'events';
import { ISimulatorHost } from '../../simulator';
import { Designer, Point } from '../../designer';
import { cursor } from '@alilc/lowcode-utils';
import { makeEventsHandler } from '../../utils/misc';
import { createModuleEventBus, IEventBus } from '@alilc/lowcode-editor-core';
// 拖动缩放
export default class DragResizeEngine {
private emitter: EventEmitter;
private emitter: IEventBus;
private dragResizing = false;
@ -14,7 +14,7 @@ export default class DragResizeEngine {
constructor(designer: Designer) {
this.designer = designer;
this.emitter = new EventEmitter();
this.emitter = createModuleEventBus('DragResizeEngine');
}
isDragResizing() {

View File

@ -3,29 +3,27 @@ import { observer } from '@alilc/lowcode-editor-core';
import { BuiltinSimulatorHost } from '../host';
import {
DropLocation,
Rect,
isLocationChildrenDetail,
LocationChildrenDetail,
isVertical,
} from '../../designer';
import { ISimulatorHost } from '../../simulator';
import { ParentalNode } from '../../document';
import { INode } from '../../document';
import './insertion.less';
import { NodeData, NodeSchema } from '@alilc/lowcode-types';
import { IPublicTypeNodeData, IPublicTypeNodeSchema, IPublicTypeLocationChildrenDetail, IPublicTypeRect } from '@alilc/lowcode-types';
import { isLocationChildrenDetail } from '@alilc/lowcode-utils';
interface InsertionData {
edge?: DOMRect;
insertType?: string;
vertical?: boolean;
nearRect?: Rect;
nearRect?: IPublicTypeRect;
coverRect?: DOMRect;
nearNode?: NodeData;
nearNode?: IPublicTypeNodeData;
}
/**
* (INode)
*/
function processChildrenDetail(sim: ISimulatorHost, container: ParentalNode, detail: LocationChildrenDetail): InsertionData {
function processChildrenDetail(sim: ISimulatorHost, container: INode, detail: IPublicTypeLocationChildrenDetail): InsertionData {
let edge = detail.edge || null;
if (!edge) {
@ -161,7 +159,7 @@ export class InsertionView extends Component<{ host: BuiltinSimulatorHost }> {
y = ((insertType === 'before' ? nearRect.top : nearRect.bottom) + scrollY) * scale;
style.width = nearRect.width * scale;
}
if (y === 0 && (nearNode as NodeSchema)?.componentMeta?.isTopFixed) {
if (y === 0 && (nearNode as IPublicTypeNodeSchema)?.componentMeta?.isTopFixed) {
return null;
}
}

View File

@ -22,6 +22,7 @@ export function createSimulator(
const doc = iframe.contentDocument!;
win.LCSimulatorHost = host;
win._ = window._;
const styles: any = {};
const scripts: any = {};

View File

@ -6,7 +6,7 @@ import { Project } from '../project';
import './host.less';
/*
Simulator , 使 Canvas Canvas
Simulator 使 Canvas Canvas
Canvas(DeviceShell) CanvasViewport
CanvasViewport Canvas
Content(Shell) CanvasViewport margin
@ -23,8 +23,8 @@ export class BuiltinSimulatorHostView extends Component<SimulatorHostProps> {
constructor(props: any) {
super(props);
const { project, onMount } = this.props;
this.host = (project.simulator as BuiltinSimulatorHost) || new BuiltinSimulatorHost(project);
const { project, onMount, designer } = this.props;
this.host = (project.simulator as BuiltinSimulatorHost) || new BuiltinSimulatorHost(project, designer);
this.host.setProps(this.props);
onMount?.(this.host);
}
@ -76,14 +76,15 @@ class Content extends Component<{ host: BuiltinSimulatorHost }> {
private dispose?: () => void;
componentDidMount() {
const editor = globalContext.get('editor');
const workspace = globalContext.get('workspace');
const editor = workspace.isActive ? workspace.window.editor : globalContext.get('editor');
const onEnableEvents = (type: boolean) => {
this.setState({
disabledEvents: type,
});
};
editor.on('designer.builtinSimulator.disabledEvents', onEnableEvents);
editor.eventBus.on('designer.builtinSimulator.disabledEvents', onEnableEvents);
this.dispose = () => {
editor.removeListener('designer.builtinSimulator.disabledEvents', onEnableEvents);
@ -97,7 +98,7 @@ class Content extends Component<{ host: BuiltinSimulatorHost }> {
render() {
const sim = this.props.host;
const { disabledEvents } = this.state;
const { viewport } = sim;
const { viewport, designer } = sim;
const frameStyle: any = {
transform: `scale(${viewport.scale})`,
height: viewport.contentHeight,
@ -107,10 +108,12 @@ class Content extends Component<{ host: BuiltinSimulatorHost }> {
frameStyle.pointerEvents = 'none';
}
const { viewName } = designer;
return (
<div className="lc-simulator-content">
<iframe
name="SimulatorRenderer"
name={`${viewName}-SimulatorRenderer`}
className="lc-simulator-content-frame"
style={frameStyle}
ref={(frame) => sim.mountContentFrame(frame)}

View File

@ -4,25 +4,23 @@ import {
reaction,
computed,
getPublicPath,
hotkey,
focusTracker,
engineConfig,
IReactionPublic,
IReactionOptions,
IReactionDisposer,
makeObservable,
createModuleEventBus,
IEventBus,
} from '@alilc/lowcode-editor-core';
import { EventEmitter } from 'events';
import {
ISimulatorHost,
Component,
NodeInstance,
ComponentInstance,
DropContainer,
} from '../simulator';
import Viewport from './viewport';
import { createSimulator } from './create-simulator';
import { Node, ParentalNode, contains, isRootNode, isLowCodeComponent } from '../document';
import { Node, INode, contains, isRootNode, isLowCodeComponent } from '../document';
import ResourceConsumer from './resource-consumer';
import {
AssetLevel,
@ -37,46 +35,48 @@ import {
UtilsMetadata,
getClosestNode,
transactionManager,
} from '@alilc/lowcode-utils';
import {
isShaken,
LocateEvent,
isDragAnyObject,
isDragNodeObject,
isLocationData,
LocationChildrenDetail,
LocationDetailType,
} from '@alilc/lowcode-utils';
import {
isShaken,
ILocateEvent,
isChildInline,
isRowContainer,
getRectTarget,
Rect,
CanvasPoint,
Designer,
} from '../designer';
import { parseMetadata } from './utils/parse-metadata';
import { getClosestClickableNode } from './utils/clickable';
import {
ComponentMetadata,
ComponentSchema,
Package,
TransitionType,
DragObjectType,
DragNodeObject,
IPublicTypeComponentMetadata,
IPublicTypeComponentSchema,
IPublicTypePackage,
IPublicEnumTransitionType,
IPublicEnumDragObjectType,
IPublicTypeDragNodeObject,
NodeInstance,
IPublicTypeComponentInstance,
IPublicTypeLocationChildrenDetail,
IPublicTypeLocationDetailType,
IPublicTypeRect,
} from '@alilc/lowcode-types';
import { BuiltinSimulatorRenderer } from './renderer';
import clipboard from '../designer/clipboard';
import { clipboard } from '../designer/clipboard';
import { LiveEditing } from './live-editing/live-editing';
import { Project } from '../project';
import { Scroller } from '../designer/scroller';
import { isElementNode, isDOMNodeVisible } from '../utils/misc';
import { debounce } from 'lodash';
export interface LibraryItem extends Package{
export type LibraryItem = IPublicTypePackage & {
package: string;
library: string;
urls?: Asset;
editUrls?: Asset;
}
};
export interface DeviceStyleProps {
canvas?: object;
@ -171,7 +171,8 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
readonly scroller: Scroller;
readonly emitter: EventEmitter = new EventEmitter();
readonly emitter: IEventBus = createModuleEventBus('BuiltinSimulatorHost');
readonly componentsConsumer: ResourceConsumer;
@ -192,10 +193,10 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
this.renderer?.enableAutoRepaintNode();
}
constructor(project: Project) {
constructor(project: Project, designer: Designer) {
makeObservable(this);
this.project = project;
this.designer = project?.designer;
this.designer = designer;
this.scroller = this.designer.createScroller(this.viewport);
this.autoRender = !engineConfig.get('disableAutoRender', false);
this.componentsConsumer = new ResourceConsumer<Asset | undefined>(() => this.componentsAsset);
@ -209,13 +210,13 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
transactionManager.onStartTransaction(() => {
this.stopAutoRepaintNode();
}, TransitionType.REPAINT);
}, IPublicEnumTransitionType.REPAINT);
// 防止批量调用 transaction 时,执行多次 rerender
const rerender = debounce(this.rerender.bind(this), 28);
transactionManager.onEndTransaction(() => {
rerender();
this.enableAutoRepaintNode();
}, TransitionType.REPAINT);
}, IPublicEnumTransitionType.REPAINT);
}
get currentDocument() {
@ -372,11 +373,11 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
* ],
* "library":"BizCharts"
* }
* packageString npm包名
* exportNameString umd包导出名字define name不一致的问题BizCharts改成bizchartsdefine声明的bizcharts
* packageString npm
* exportNameString umd define name BizCharts bizcharts define bizcharts
* versionString
* urlsArray cdn地址umd类型.js或者.css
* libraryString umd包直接导出的name
* urlsArray cdn umd .js .css
* libraryString umd name
*/
buildLibrary(library?: LibraryItem[]) {
const _library = library || (this.get('library') as LibraryItem[]);
@ -418,7 +419,7 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
this.renderer?.rerender?.();
}
async mountContentFrame(iframe: HTMLIFrameElement | null) {
async mountContentFrame(iframe: HTMLIFrameElement | null): Promise<void> {
if (!iframe || this._iframe === iframe) {
return;
}
@ -463,7 +464,7 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
await this.injectionConsumer.waitFirstConsume();
if (Object.keys(this.asyncLibraryMap).length > 0) {
// 加载异步Library
// 加载异步 Library
await renderer.loadAsyncLibrary(this.asyncLibraryMap);
Object.keys(this.asyncLibraryMap).forEach(key => {
delete this.asyncLibraryMap[key];
@ -478,6 +479,7 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
this.setupEvents();
// bind hotkey & clipboard
const hotkey = this.designer.editor.get('innerHotkey');
hotkey.mount(this._contentWindow);
focusTracker.mount(this._contentWindow);
clipboard.injectCopyPaster(this._contentDocument);
@ -489,7 +491,7 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
const libraryAsset: AssetList = this.buildLibrary(library);
await this.renderer?.load(libraryAsset);
if (Object.keys(this.asyncLibraryMap).length > 0) {
// 加载异步Library
// 加载异步 Library
await this.renderer?.loadAsyncLibrary(this.asyncLibraryMap);
Object.keys(this.asyncLibraryMap).forEach(key => {
delete this.asyncLibraryMap[key];
@ -536,9 +538,9 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
// FIXME: dirty fix remove label-for fro liveEditing
(downEvent.target as HTMLElement).removeAttribute('for');
const nodeInst = this.getNodeInstanceFromElement(downEvent.target as Element);
const focusNode = documentModel.focusNode;
const { focusNode } = documentModel;
const node = getClosestClickableNode(nodeInst?.node || focusNode, downEvent);
// 如果找不到可点击的节点, 直接返回
// 如果找不到可点击的节点直接返回
if (!node) {
return;
}
@ -550,7 +552,7 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
const rglNode = node?.getParent();
const isRGLNode = rglNode?.isRGLContainer;
if (isRGLNode) {
// 如果拖拽的是磁铁块的右下角handle则直接跳过
// 如果拖拽的是磁铁块的右下角 handle则直接跳过
if (downEvent.target.classList.contains('react-resizable-handle')) return;
// 禁止多选
isMulti = false;
@ -576,7 +578,7 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
action: 'end',
rglNode,
});
// 鼠标是否移动 ? - 鼠标抖动应该也需要支持选中事件,偶尔点击不能选中,磁帖块移除shaken检测
// 鼠标是否移动 ? - 鼠标抖动应该也需要支持选中事件,偶尔点击不能选中,磁帖块移除 shaken 检测
if (!isShaken(downEvent, e) || isRGLNode) {
let { id } = node;
designer.activeTracker.track({ node, instance: nodeInst?.instance });
@ -597,7 +599,7 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
[npm?.package, npm?.componentName].filter((item) => !!item).join('-') ||
node?.componentMeta?.componentName ||
'';
editor?.emit('designer.builtinSimulator.select', {
editor?.eventBus.emit('designer.builtinSimulator.select', {
selected,
});
}
@ -624,7 +626,7 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
}
designer.dragon.boost(
{
type: DragObjectType.Node,
type: IPublicEnumDragObjectType.Node,
nodes,
},
downEvent,
@ -704,7 +706,7 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
}
const nodeInst = this.getNodeInstanceFromElement(e.target as Element);
if (nodeInst?.node) {
let node = nodeInst.node;
let { node } = nodeInst;
const focusNode = node.document?.focusNode;
if (node.contains(focusNode)) {
node = focusNode;
@ -828,7 +830,7 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
[npm?.package, npm?.componentName].filter((item) => !!item).join('-') ||
node?.componentMeta?.componentName ||
'';
editor?.emit('designer.builtinSimulator.contextmenu', {
editor?.eventBus.emit('designer.builtinSimulator.contextmenu', {
selected,
...nodeInst,
instanceRect: this.computeComponentInstanceRect(nodeInst.instance),
@ -840,7 +842,7 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
/**
* @see ISimulator
*/
generateComponentMetadata(componentName: string): ComponentMetadata {
generateComponentMetadata(componentName: string): IPublicTypeComponentMetadata {
// if html tags
if (isHTMLTag(componentName)) {
return {
@ -874,15 +876,15 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
return this.renderer?.getComponent(componentName) || null;
}
createComponent(schema: ComponentSchema): Component | null {
createComponent(schema: IPublicTypeComponentSchema): Component | null {
return null;
// return this.renderer?.createComponent(schema) || null;
}
@obx private instancesMap: {
[docId: string]: Map<string, ComponentInstance[]>;
[docId: string]: Map<string, IPublicTypeComponentInstance[]>;
} = {};
setInstance(docId: string, id: string, instances: ComponentInstance[] | null) {
setInstance(docId: string, id: string, instances: IPublicTypeComponentInstance[] | null) {
if (!hasOwnProperty(this.instancesMap, docId)) {
this.instancesMap[docId] = new Map();
}
@ -896,7 +898,7 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
/**
* @see ISimulator
*/
getComponentInstances(node: Node, context?: NodeInstance): ComponentInstance[] | null {
getComponentInstances(node: Node, context?: NodeInstance): IPublicTypeComponentInstance[] | null {
const docId = node.document.id;
const instances = this.instancesMap[docId]?.get(node.id) || null;
@ -921,16 +923,16 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
* @see ISimulator
*/
getClosestNodeInstance(
from: ComponentInstance,
from: IPublicTypeComponentInstance,
specId?: string,
): NodeInstance<ComponentInstance> | null {
): NodeInstance<IPublicTypeComponentInstance> | null {
return this.renderer?.getClosestNodeInstance(from, specId) || null;
}
/**
* @see ISimulator
*/
computeRect(node: Node): Rect | null {
computeRect(node: Node): IPublicTypeRect | null {
const instances = this.getComponentInstances(node);
if (!instances) {
return null;
@ -941,7 +943,7 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
/**
* @see ISimulator
*/
computeComponentInstanceRect(instance: ComponentInstance, selector?: string): Rect | null {
computeComponentInstanceRect(instance: IPublicTypeComponentInstance, selector?: string): IPublicTypeRect | null {
const renderer = this.renderer!;
const elements = this.findDOMNodes(instance, selector);
if (!elements) {
@ -1007,7 +1009,7 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
/**
* @see ISimulator
*/
findDOMNodes(instance: ComponentInstance, selector?: string): Array<Element | Text> | null {
findDOMNodes(instance: IPublicTypeComponentInstance, selector?: string): Array<Element | Text> | null {
const elements = this._renderer?.findDOMNodes(instance);
if (!elements) {
return null;
@ -1026,7 +1028,7 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
/**
* DOM simulator
*/
getNodeInstanceFromElement(target: Element | null): NodeInstance<ComponentInstance> | null {
getNodeInstanceFromElement(target: Element | null): NodeInstance<IPublicTypeComponentInstance> | null {
if (!target) {
return null;
}
@ -1071,29 +1073,6 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
opt.top = top + scrollTop;
scroll = true;
}
/*
const rect = this.document.computeRect(node);
if (!rect || rect.width === 0 || rect.height === 0) {
if (!this.tryScrollAgain && tryTimes < 3) {
this.tryScrollAgain = requestAnimationFrame(() => this.scrollToNode(node, null, tryTimes + 1));
}
return;
}
const scrollTarget = this.viewport.scrollTarget!;
const st = scrollTarget.top;
const sl = scrollTarget.left;
const { scrollHeight, scrollWidth } = scrollTarget;
const { height, width, top, bottom, left, right } = this.viewport.contentBounds;
if (rect.height > height ? rect.top > bottom || rect.bottom < top : rect.top < top || rect.bottom > bottom) {
opt.top = Math.min(rect.top + rect.height / 2 + st - top - height / 2, scrollHeight - height);
scroll = true;
}
if (rect.width > width ? rect.left > right || rect.right < left : rect.left < left || rect.right > right) {
opt.left = Math.min(rect.left + rect.width / 2 + sl - left - width / 2, scrollWidth - width);
scroll = true;
} */
if (scroll && this.scroller) {
this.scroller.scrollTo(opt);
@ -1141,7 +1120,7 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
/**
* @see ISensor
*/
fixEvent(e: LocateEvent): LocateEvent {
fixEvent(e: ILocateEvent): ILocateEvent {
if (e.fixed) {
return e;
}
@ -1172,7 +1151,7 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
/**
* @see ISensor
*/
isEnter(e: LocateEvent): boolean {
isEnter(e: ILocateEvent): boolean {
const rect = this.viewport.bounds;
return (
e.globalY >= rect.top &&
@ -1197,9 +1176,9 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
/**
* @see ISensor
*/
locate(e: LocateEvent): any {
locate(e: ILocateEvent): any {
const { dragObject } = e;
const { nodes } = dragObject as DragNodeObject;
const { nodes } = dragObject as IPublicTypeDragNodeObject;
const operationalNodes = nodes?.filter((node) => {
const onMoveHook = node.componentMeta?.getMetadata()?.configure.advanced?.callbacks?.onMoveHook;
@ -1260,14 +1239,14 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
const { children } = container;
const detail: LocationChildrenDetail = {
type: LocationDetailType.Children,
const detail: IPublicTypeLocationChildrenDetail = {
type: IPublicTypeLocationDetailType.Children,
index: 0,
edge,
};
const locationData = {
target: container as ParentalNode,
target: container as INode,
detail,
source: `simulator${document.id}`,
event: e,
@ -1387,13 +1366,13 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
/**
*
*/
getDropContainer(e: LocateEvent): DropContainer | null {
getDropContainer(e: ILocateEvent): DropContainer | null {
const { target, dragObject } = e;
const isAny = isDragAnyObject(dragObject);
const document = this.project.currentDocument!;
const { currentRoot } = document;
let container: Node;
let nodeInstance: NodeInstance<ComponentInstance> | undefined;
let container: INode;
let nodeInstance: NodeInstance<IPublicTypeComponentInstance> | undefined;
if (target) {
const ref = this.getNodeInstanceFromElement(target);
@ -1477,49 +1456,25 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
container = container.parent;
instance = this.getClosestNodeInstance(dropContainer.instance, container.id)?.instance;
dropContainer = {
container: container as ParentalNode,
container: container as INode,
instance,
};
} else {
return null;
}
} /* else if (res === DRILL_DOWN) {
if (!upward) {
container = container.parent;
instance = this.getClosestNodeInstance(dropContainer.instance, container.id)?.instance;
upward = {
container,
instance
};
}
dropContainer = this.getNearByContainer(dropContainer, drillDownExcludes, e);
if (!dropContainer) {
dropContainer = upward;
upward = null;
}
} else if (isNode(res)) {
// TODO:
} */
}
}
return null;
}
isAcceptable(/* container: ParentalNode */): boolean {
isAcceptable(): boolean {
return false;
/*
const meta = container.componentMeta;
const instance: any = this.document.getView(container);
if (instance && '$accept' in instance) {
return true;
}
return meta.acceptable;
*/
}
/**
*
*/
handleAccept({ container, instance }: DropContainer, e: LocateEvent): boolean {
handleAccept({ container, instance }: DropContainer, e: ILocateEvent): boolean {
const { dragObject } = e;
const document = this.currentDocument!;
const focusNode = document.focusNode;
@ -1535,33 +1490,6 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
return false;
}
// first use accept
if (acceptable) {
/*
const view: any = this.document.getView(container);
if (view && '$accept' in view) {
if (view.$accept === false) {
return false;
}
if (view.$accept === AT_CHILD || view.$accept === '@CHILD') {
return AT_CHILD;
}
if (typeof view.$accept === 'function') {
const ret = view.$accept(container, e);
if (ret || ret === false) {
return ret;
}
}
}
if (proto.acceptable) {
const ret = proto.accept(container, e);
if (ret || ret === false) {
return ret;
}
}
*/
}
// check nesting
return document.checkNesting(container, dragObject as any);
}
@ -1572,7 +1500,7 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
getNearByContainer(
{ container, instance }: DropContainer,
drillDownExcludes: Set<Node>,
e: LocateEvent,
e: ILocateEvent,
) {
const { children } = container;
const document = this.project.currentDocument!;
@ -1603,17 +1531,6 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
if (!rect) {
continue;
}
/*
if (isPointInRect(e, rect)) {
return child;
}
const distance = distanceToRect(e, rect);
if (nearDistance === null || distance < nearDistance) {
nearDistance = distance;
nearBy = child;
} */
}
return nearBy;
@ -1625,7 +1542,7 @@ function isHTMLTag(name: string) {
return /^[a-z]\w*$/.test(name);
}
function isPointInRect(point: CanvasPoint, rect: Rect) {
function isPointInRect(point: CanvasPoint, rect: IPublicTypeRect) {
return (
point.canvasY >= rect.top &&
point.canvasY <= rect.bottom &&
@ -1634,7 +1551,7 @@ function isPointInRect(point: CanvasPoint, rect: Rect) {
);
}
function distanceToRect(point: CanvasPoint, rect: Rect) {
function distanceToRect(point: CanvasPoint, rect: IPublicTypeRect) {
let minX = Math.min(Math.abs(point.canvasX - rect.left), Math.abs(point.canvasX - rect.right));
let minY = Math.min(Math.abs(point.canvasY - rect.top), Math.abs(point.canvasY - rect.bottom));
if (point.canvasX >= rect.left && point.canvasX <= rect.right) {
@ -1647,7 +1564,7 @@ function distanceToRect(point: CanvasPoint, rect: Rect) {
return Math.sqrt(minX ** 2 + minY ** 2);
}
function distanceToEdge(point: CanvasPoint, rect: Rect) {
function distanceToEdge(point: CanvasPoint, rect: IPublicTypeRect) {
const distanceTop = Math.abs(point.canvasY - rect.top);
const distanceBottom = Math.abs(point.canvasY - rect.bottom);
@ -1657,7 +1574,7 @@ function distanceToEdge(point: CanvasPoint, rect: Rect) {
};
}
function isNearAfter(point: CanvasPoint, rect: Rect, inline: boolean) {
function isNearAfter(point: CanvasPoint, rect: IPublicTypeRect, inline: boolean) {
if (inline) {
return (
Math.abs(point.canvasX - rect.left) + Math.abs(point.canvasY - rect.top) >

View File

@ -1,5 +1,5 @@
import { obx, globalContext, Editor } from '@alilc/lowcode-editor-core';
import { LiveTextEditingConfig } from '@alilc/lowcode-types';
import { obx, globalContext } from '@alilc/lowcode-editor-core';
import { IPublicTypePluginConfig, IPublicTypeLiveTextEditingConfig } from '@alilc/lowcode-types';
import { Node, Prop } from '../../document';
const EDITOR_KEY = 'data-setter-prop';
@ -52,17 +52,18 @@ export class LiveEditing {
const targetElement = event.target as HTMLElement;
const { liveTextEditing } = node.componentMeta;
const editor = globalContext.get(Editor);
const workspace = globalContext.get('workspace');
const editor = workspace.isActive ? workspace.window.editor : globalContext.get('editor');
const npm = node?.componentMeta?.npm;
const selected =
[npm?.package, npm?.componentName].filter((item) => !!item).join('-') || node?.componentMeta?.componentName || '';
editor?.emit('designer.builtinSimulator.liveEditing', {
editor?.eventBus.emit('designer.builtinSimulator.liveEditing', {
selected,
});
let setterPropElement = getSetterPropElement(targetElement, rootElement);
let propTarget = setterPropElement?.dataset.setterProp;
let matched: (LiveTextEditingConfig & { propElement?: HTMLElement }) | undefined | null;
let matched: (IPublicTypePluginConfig & { propElement?: HTMLElement }) | undefined | null;
if (liveTextEditing) {
if (propTarget) {
// 已埋点命中 data-setter-prop="proptarget", 从 liveTextEditing 读取配置mode|onSaveContent
@ -107,7 +108,7 @@ export class LiveEditing {
}
// 进入编辑
// 1. 设置contentEditable="plaintext|..."
// 1. 设置 contentEditable="plaintext|..."
// 2. 添加类名
// 3. focus & cursor locate
// 4. 监听 blur 事件
@ -186,7 +187,7 @@ export class LiveEditing {
}
}
export type SpecificRule = (target: EditingTarget) => (LiveTextEditingConfig & {
export type SpecificRule = (target: EditingTarget) => (IPublicTypeLiveTextEditingConfig & {
propElement?: HTMLElement;
}) | null;

View File

@ -1,10 +1,10 @@
import { Overlay } from '@alifd/next';
import React from 'react';
import { Title, globalContext, Editor } from '@alilc/lowcode-editor-core';
import { Title, globalContext } from '@alilc/lowcode-editor-core';
import { canClickNode } from '@alilc/lowcode-utils';
import './index.less';
import { Node, ParentalNode } from '@alilc/lowcode-designer';
import { Node, INode } from '@alilc/lowcode-designer';
const { Popup } = Overlay;
@ -16,7 +16,7 @@ export interface IState {
parentNodes: Node[];
}
type UnionNode = Node | ParentalNode | null;
type UnionNode = INode | null;
export default class InstanceNodeSelector extends React.Component<IProps, IState> {
state: IState = {
@ -30,7 +30,7 @@ export default class InstanceNodeSelector extends React.Component<IProps, IState
});
}
// 获取节点的父级节点(最多获取5层
// 获取节点的父级节点(最多获取 5 层)
getParentNodes = (node: Node) => {
const parentNodes: any[] = [];
const { focusNode } = node.document;
@ -62,13 +62,14 @@ export default class InstanceNodeSelector extends React.Component<IProps, IState
if (canClick && typeof node.select === 'function') {
node.select();
const editor = globalContext.get(Editor);
const workspace = globalContext.get('workspace');
const editor = workspace.isActive ? workspace.window.editor : globalContext.get('editor');
const npm = node?.componentMeta?.npm;
const selected =
[npm?.package, npm?.componentName].filter((item) => !!item).join('-') ||
node?.componentMeta?.componentName ||
'';
editor?.emit('designer.border.action', {
editor?.eventBus.emit('designer.border.action', {
name: 'select',
selected,
});

View File

@ -1,12 +1,15 @@
import { ComponentInstance, NodeInstance, Component } from '../simulator';
import { NodeSchema } from '@alilc/lowcode-types';
import { Component } from '../simulator';
import { IPublicTypeNodeSchema, IPublicTypeComponentInstance, NodeInstance } from '@alilc/lowcode-types';
export interface BuiltinSimulatorRenderer {
readonly isSimulatorRenderer: true;
createComponent(schema: NodeSchema): Component | null;
createComponent(schema: IPublicTypeNodeSchema): Component | null;
getComponent(componentName: string): Component;
getClosestNodeInstance(from: ComponentInstance, nodeId?: string): NodeInstance<ComponentInstance> | null;
findDOMNodes(instance: ComponentInstance): Array<Element | Text> | null;
getClosestNodeInstance(
from: IPublicTypeComponentInstance,
nodeId?: string,
): NodeInstance<IPublicTypeComponentInstance> | null;
findDOMNodes(instance: IPublicTypeComponentInstance): Array<Element | Text> | null;
getClientRects(element: Element | Text): DOMRect[];
setNativeSelection(enableFlag: boolean): void;
setDraggingState(state: boolean): void;

View File

@ -1,6 +1,5 @@
import { autorun, makeObservable, obx } from '@alilc/lowcode-editor-core';
import { autorun, makeObservable, obx, createModuleEventBus, IEventBus } from '@alilc/lowcode-editor-core';
import { BuiltinSimulatorHost } from './host';
import { EventEmitter } from 'events';
import { BuiltinSimulatorRenderer, isSimulatorRenderer } from './renderer';
const UNSET = Symbol('unset');
@ -20,7 +19,7 @@ export type RendererConsumer<T> = (renderer: BuiltinSimulatorRenderer, data: T)
// 2. 消费机制(渲染进程自定 + 传递进入)
export default class ResourceConsumer<T = any> {
private emitter = new EventEmitter();
private emitter: IEventBus = createModuleEventBus('ResourceConsumer');
@obx.ref private _data: T | typeof UNSET = UNSET;

View File

@ -25,7 +25,7 @@ export const getClosestClickableNode = (
if (canClick) {
break;
}
// 对于不可点击的节点, 继续向上找
// 对于不可点击的节点继续向上找
node = node.parent;
}
return node;

View File

@ -1,7 +1,7 @@
import PropTypes from 'prop-types';
import { isValidElement } from 'react';
import { isElement } from '@alilc/lowcode-utils';
import { PropConfig } from '@alilc/lowcode-types';
import { IPublicTypePropConfig } from '@alilc/lowcode-types';
export const primitiveTypes = [
'string',
@ -55,7 +55,7 @@ const LowcodeTypes: any = {
(window as any).React.PropTypes = LowcodeTypes;
// override primitive type checkers
primitiveTypes.forEach(type => {
primitiveTypes.forEach((type) => {
const propType = (PropTypes as any)[type];
if (!propType) {
return;
@ -91,7 +91,7 @@ LowcodeTypes.objectOf = (type: any) => {
// An object that could be one of many types
LowcodeTypes.oneOfType = (types: any[]) => {
const itemTypes = types.map(type => type.lowcodeType || 'any');
const itemTypes = types.map((type) => type.lowcodeType || 'any');
return define(PropTypes.oneOfType(types), {
type: 'oneOfType',
value: itemTypes,
@ -100,7 +100,7 @@ LowcodeTypes.oneOfType = (types: any[]) => {
// An object with warnings on extra properties
LowcodeTypes.exact = (typesMap: any) => {
const configs = Object.keys(typesMap).map(key => {
const configs = Object.keys(typesMap).map((key) => {
return {
name: key,
propType: typesMap[key]?.lowcodeType || 'any',
@ -114,7 +114,7 @@ LowcodeTypes.exact = (typesMap: any) => {
// An object taking on a particular shape
LowcodeTypes.shape = (typesMap: any = {}) => {
const configs = Object.keys(typesMap).map(key => {
const configs = Object.keys(typesMap).map((key) => {
return {
name: key,
propType: typesMap[key]?.lowcodeType || 'any',
@ -127,7 +127,7 @@ LowcodeTypes.shape = (typesMap: any = {}) => {
};
const BasicTypes = ['string', 'number', 'object'];
export function parseProps(component: any): PropConfig[] {
export function parseProps(component: any): IPublicTypePropConfig[] {
if (!component) {
return [];
}
@ -135,7 +135,7 @@ export function parseProps(component: any): PropConfig[] {
const defaultProps = component.defaultProps || ({} as any);
const result: any = {};
if (!propTypes) return [];
Object.keys(propTypes).forEach(key => {
Object.keys(propTypes).forEach((key) => {
const propTypeItem = propTypes[key];
const defaultValue = defaultProps[key];
const { lowcodeType } = propTypeItem;
@ -173,7 +173,7 @@ export function parseProps(component: any): PropConfig[] {
}
});
Object.keys(defaultProps).forEach(key => {
Object.keys(defaultProps).forEach((key) => {
if (result[key]) return;
const defaultValue = defaultProps[key];
let type: string = typeof defaultValue;
@ -198,7 +198,7 @@ export function parseProps(component: any): PropConfig[] {
};
});
return Object.keys(result).map(key => result[key]);
return Object.keys(result).map((key) => result[key]);
}
export function parseMetadata(component: any): any {

View File

@ -13,7 +13,7 @@ export function isPackagePath(path: string): boolean {
export function toTitleCase(s: string): string {
return s
.split(/[-_ .]+/)
.map(token => token[0].toUpperCase() + token.substring(1))
.map((token) => token[0].toUpperCase() + token.substring(1))
.join('');
}

View File

@ -1,23 +1,23 @@
import { ReactElement } from 'react';
import {
ComponentMetadata,
NpmInfo,
NodeData,
NodeSchema,
ComponentAction,
TitleContent,
TransformedComponentMetadata,
NestingFilter,
I18nData,
LiveTextEditingConfig,
FieldConfig,
MetadataTransducer,
IPublicTypeComponentMetadata,
IPublicTypeNpmInfo,
IPublicTypeNodeData,
IPublicTypeNodeSchema,
IPublicTypeComponentAction,
IPublicTypeTitleContent,
IPublicTypeTransformedComponentMetadata,
IPublicTypeNestingFilter,
IPublicTypeI18nData,
IPublicTypePluginConfig,
IPublicTypeFieldConfig,
IPublicTypeMetadataTransducer,
IPublicModelComponentMeta,
} from '@alilc/lowcode-types';
import { deprecate, isRegExp, isTitleConfig } from '@alilc/lowcode-utils';
import { computed, engineConfig } from '@alilc/lowcode-editor-core';
import EventEmitter from 'events';
import { computed, engineConfig, createModuleEventBus, IEventBus } from '@alilc/lowcode-editor-core';
import { componentDefaults, legacyIssues } from './transducers';
import { isNode, Node, ParentalNode } from './document';
import { isNode, Node, INode } from './document';
import { Designer } from './designer';
import { intlNode } from './locale';
import {
@ -47,7 +47,7 @@ export function ensureAList(list?: string | string[]): string[] | null {
return list;
}
export function buildFilter(rule?: string | string[] | RegExp | NestingFilter) {
export function buildFilter(rule?: string | string[] | RegExp | IPublicTypeNestingFilter) {
if (!rule) {
return null;
}
@ -55,21 +55,25 @@ export function buildFilter(rule?: string | string[] | RegExp | NestingFilter) {
return rule;
}
if (isRegExp(rule)) {
return (testNode: Node | NodeSchema) => rule.test(testNode.componentName);
return (testNode: Node | IPublicTypeNodeSchema) => rule.test(testNode.componentName);
}
const list = ensureAList(rule);
if (!list) {
return null;
}
return (testNode: Node | NodeSchema) => list.includes(testNode.componentName);
return (testNode: Node | IPublicTypeNodeSchema) => list.includes(testNode.componentName);
}
export class ComponentMeta {
export interface IComponentMeta extends IPublicModelComponentMeta {
}
export class ComponentMeta implements IComponentMeta {
readonly isComponentMeta = true;
private _npm?: NpmInfo;
private _npm?: IPublicTypeNpmInfo;
private emitter: EventEmitter = new EventEmitter();
private emitter: IEventBus = createModuleEventBus('ComponentMeta');
get npm() {
return this._npm;
@ -113,14 +117,14 @@ export class ComponentMeta {
return this._rootSelector;
}
private _transformedMetadata?: TransformedComponentMetadata;
private _transformedMetadata?: IPublicTypeTransformedComponentMetadata;
get configure() {
const config = this._transformedMetadata?.configure;
return config?.combined || config?.props || [];
}
private _liveTextEditing?: LiveTextEditingConfig[];
private _liveTextEditing?: IPublicTypePluginConfig[];
get liveTextEditing() {
return this._liveTextEditing;
@ -132,15 +136,15 @@ export class ComponentMeta {
return !!(this._isTopFixed);
}
private parentWhitelist?: NestingFilter | null;
private parentWhitelist?: IPublicTypeNestingFilter | null;
private childWhitelist?: NestingFilter | null;
private childWhitelist?: IPublicTypeNestingFilter | null;
private _title?: TitleContent;
private _title?: IPublicTypeTitleContent;
private _isMinimalRenderUnit?: boolean;
get title(): string | I18nData | ReactElement {
get title(): string | IPublicTypeI18nData | ReactElement {
// string | i18nData | ReactElement
// TitleConfig title.label
if (isTitleConfig(this._title)) {
@ -165,19 +169,22 @@ export class ComponentMeta {
return this._acceptable!;
}
constructor(readonly designer: Designer, metadata: ComponentMetadata) {
constructor(readonly designer: Designer, metadata: IPublicTypeComponentMetadata) {
this.parseMetadata(metadata);
}
setNpm(info: NpmInfo) {
setNpm(info: IPublicTypeNpmInfo) {
if (!this._npm) {
this._npm = info;
}
}
private parseMetadata(metadata: ComponentMetadata) {
private parseMetadata(metadata: IPublicTypeComponentMetadata) {
const { componentName, npm, ...others } = metadata;
let _metadata = metadata;
if ((metadata as any).prototype) {
this.prototype = (metadata as any).prototype;
}
if (!npm && !Object.keys(others).length) {
// 没有注册的组件,只能删除,不支持复制、移动等操作
_metadata = {
@ -214,7 +221,7 @@ export class ComponentMeta {
const liveTextEditing = this._transformedMetadata.configure.advanced?.liveTextEditing || [];
function collectLiveTextEditing(items: FieldConfig[]) {
function collectLiveTextEditing(items: IPublicTypeFieldConfig[]) {
items.forEach((config) => {
if (config?.items) {
collectLiveTextEditing(config.items);
@ -264,7 +271,7 @@ export class ComponentMeta {
this.parseMetadata(this.getMetadata());
}
private transformMetadata(metadta: ComponentMetadata): TransformedComponentMetadata {
private transformMetadata(metadta: IPublicTypeComponentMetadata): IPublicTypeTransformedComponentMetadata {
const result = getRegisteredMetadataTransducers().reduce((prevMetadata, current) => {
return current(prevMetadata);
}, preprocessMetadata(metadta));
@ -307,15 +314,15 @@ export class ComponentMeta {
return actions;
}
setMetadata(metadata: ComponentMetadata) {
setMetadata(metadata: IPublicTypeComponentMetadata) {
this.parseMetadata(metadata);
}
getMetadata(): TransformedComponentMetadata {
getMetadata(): IPublicTypeTransformedComponentMetadata {
return this._transformedMetadata!;
}
checkNestingUp(my: Node | NodeData, parent: ParentalNode) {
checkNestingUp(my: INode | IPublicTypeNodeData, parent: INode) {
// 检查父子关系,直接约束型,在画布中拖拽直接掠过目标容器
if (this.parentWhitelist) {
return this.parentWhitelist(
@ -326,11 +333,11 @@ export class ComponentMeta {
return true;
}
checkNestingDown(my: Node, target: Node | NodeSchema | NodeSchema[]): boolean {
checkNestingDown(my: INode, target: INode | IPublicTypeNodeSchema | IPublicTypeNodeSchema[]): boolean {
// 检查父子关系,直接约束型,在画布中拖拽直接掠过目标容器
if (this.childWhitelist) {
const _target: any = !Array.isArray(target) ? [target] : target;
return _target.every((item: Node | NodeSchema) => {
return _target.every((item: Node | IPublicTypeNodeSchema) => {
const _item = !isNode(item) ? new Node(my.document, item) : item;
return (
this.childWhitelist &&
@ -356,7 +363,7 @@ export function isComponentMeta(obj: any): obj is ComponentMeta {
return obj && obj.isComponentMeta;
}
function preprocessMetadata(metadata: ComponentMetadata): TransformedComponentMetadata {
function preprocessMetadata(metadata: IPublicTypeComponentMetadata): IPublicTypeTransformedComponentMetadata {
if (metadata.configure) {
if (Array.isArray(metadata.configure)) {
return {
@ -376,10 +383,10 @@ function preprocessMetadata(metadata: ComponentMetadata): TransformedComponentMe
}
const metadataTransducers: MetadataTransducer[] = [];
const metadataTransducers: IPublicTypeMetadataTransducer[] = [];
export function registerMetadataTransducer(
transducer: MetadataTransducer,
transducer: IPublicTypeMetadataTransducer,
level = 100,
id?: string,
) {
@ -393,11 +400,11 @@ export function registerMetadataTransducer(
}
}
export function getRegisteredMetadataTransducers(): MetadataTransducer[] {
export function getRegisteredMetadataTransducers(): IPublicTypeMetadataTransducer[] {
return metadataTransducers;
}
const builtinComponentActions: ComponentAction[] = [
const builtinComponentActions: IPublicTypeComponentAction[] = [
{
name: 'remove',
content: {
@ -440,7 +447,7 @@ const builtinComponentActions: ComponentAction[] = [
newNode.select();
const { isRGL, rglNode } = node.getRGL();
if (isRGL) {
// 复制layout信息
// 复制 layout 信息
let layout = rglNode.getPropValue('layout') || [];
let curLayout = layout.filter((item) => item.i === node.getPropValue('fieldId'));
if (curLayout && curLayout[0]) {
@ -498,13 +505,13 @@ export function removeBuiltinComponentAction(name: string) {
builtinComponentActions.splice(i, 1);
}
}
export function addBuiltinComponentAction(action: ComponentAction) {
export function addBuiltinComponentAction(action: IPublicTypeComponentAction) {
builtinComponentActions.push(action);
}
export function modifyBuiltinComponentAction(
actionName: string,
handle: (action: ComponentAction) => void,
handle: (action: IPublicTypeComponentAction) => void,
) {
const builtinAction = builtinComponentActions.find((action) => action.name === actionName);
if (builtinAction) {

View File

@ -1,34 +1,34 @@
import { EventEmitter } from 'events';
import { LocationDetail } from './location';
import { Node, isNode } from '../document/node/node';
import { ComponentInstance } from '../simulator';
import { obx } from '@alilc/lowcode-editor-core';
import { INode } from '../document/node/node';
import { obx, IEventBus, createModuleEventBus } from '@alilc/lowcode-editor-core';
import {
IPublicTypeActiveTarget,
IPublicModelActiveTracker,
} from '@alilc/lowcode-types';
import { isNode } from '@alilc/lowcode-utils';
export interface IActiveTracker extends IPublicModelActiveTracker {
export interface ActiveTarget {
node: Node;
detail?: LocationDetail;
instance?: ComponentInstance;
}
export class ActiveTracker implements IActiveTracker {
private emitter: IEventBus = createModuleEventBus('ActiveTracker');
export class ActiveTracker {
private emitter = new EventEmitter();
@obx.ref private _target?: IPublicTypeActiveTarget | INode;
@obx.ref private _target?: ActiveTarget;
track(target: ActiveTarget | Node) {
if (isNode(target)) {
target = { node: target };
track(originalTarget: IPublicTypeActiveTarget | INode) {
let target = originalTarget;
if (isNode(originalTarget)) {
target = { node: originalTarget as INode };
}
this._target = target;
this.emitter.emit('change', target);
}
get currentNode() {
return this._target?.node;
return (this._target as IPublicTypeActiveTarget)?.node;
}
get detail() {
return this._target?.detail;
return (this._target as IPublicTypeActiveTarget)?.detail;
}
/**
@ -40,10 +40,10 @@ export class ActiveTracker {
}
get instance() {
return this._target?.instance;
return (this._target as IPublicTypeActiveTarget)?.instance;
}
onChange(fn: (target: ActiveTarget) => void): () => void {
onChange(fn: (target: IPublicTypeActiveTarget) => void): () => void {
this.emitter.addListener('change', fn);
return () => {
this.emitter.removeListener('change', fn);

View File

@ -1,354 +0,0 @@
import { hotkey, Editor, globalContext } from '@alilc/lowcode-editor-core';
import { isFormEvent } from '@alilc/lowcode-utils';
import { focusing } from './focusing';
import { insertChildren, TransformStage } from '../document';
import clipboard from './clipboard';
export function isInLiveEditing() {
if (globalContext.has(Editor)) {
return Boolean(
globalContext.get(Editor).get('designer')?.project?.simulator?.liveEditing?.editing,
);
}
}
/* istanbul ignore next */
function getNextForSelect(next: any, head?: any, parent?: any): any {
if (next) {
if (!head) {
return next;
}
let ret;
if (next.isContainer()) {
const children = next.getChildren() || [];
if (children && !children.isEmpty()) {
ret = getNextForSelect(children.get(0));
if (ret) {
return ret;
}
}
}
ret = getNextForSelect(next.nextSibling);
if (ret) {
return ret;
}
}
if (parent) {
return getNextForSelect(parent.nextSibling, false, parent.getParent());
}
return null;
}
/* istanbul ignore next */
function getPrevForSelect(prev: any, head?: any, parent?: any): any {
if (prev) {
let ret;
if (!head && prev.isContainer()) {
const children = prev.getChildren() || [];
const lastChild = children && !children.isEmpty() ? children.get(children.size - 1) : null;
ret = getPrevForSelect(lastChild);
if (ret) {
return ret;
}
}
if (!head) {
return prev;
}
ret = getPrevForSelect(prev.prevSibling);
if (ret) {
return ret;
}
}
if (parent) {
return parent;
}
return null;
}
// hotkey binding
hotkey.bind(['backspace', 'del'], (e: KeyboardEvent) => {
if (isInLiveEditing()) return;
// TODO: use focus-tracker
const doc = focusing.focusDesigner?.currentDocument;
if (isFormEvent(e) || !doc) {
return;
}
e.preventDefault();
const sel = doc.selection;
const topItems = sel.getTopNodes();
// TODO: check can remove
topItems.forEach((node) => {
if (node.canPerformAction('remove')) {
doc.removeNode(node);
}
});
sel.clear();
});
hotkey.bind('escape', (e: KeyboardEvent) => {
// const currentFocus = focusing.current;
if (isInLiveEditing()) return;
const sel = focusing.focusDesigner?.currentDocument?.selection;
if (isFormEvent(e) || !sel) {
return;
}
e.preventDefault();
sel.clear();
// currentFocus.esc();
});
// command + c copy command + x cut
hotkey.bind(['command+c', 'ctrl+c', 'command+x', 'ctrl+x'], (e, action) => {
if (isInLiveEditing()) return;
const doc = focusing.focusDesigner?.currentDocument;
if (isFormEvent(e) || !doc) {
return;
}
e.preventDefault();
let selected = doc.selection.getTopNodes(true);
selected = selected.filter((node) => {
return node.canPerformAction('copy');
});
if (!selected || selected.length < 1) {
return;
}
const componentsMap = {};
const componentsTree = selected.map((item) => item.export(TransformStage.Clone));
// FIXME: clear node.id
const data = { type: 'nodeSchema', componentsMap, componentsTree };
clipboard.setData(data);
const cutMode = action && action.indexOf('x') > 0;
if (cutMode) {
selected.forEach((node) => {
const parentNode = node.getParent();
parentNode?.select();
node.remove();
});
}
});
// command + v paste
hotkey.bind(['command+v', 'ctrl+v'], (e) => {
if (isInLiveEditing()) return;
const designer = focusing.focusDesigner;
const doc = designer?.currentDocument;
if (isFormEvent(e) || !designer || !doc) {
return;
}
/* istanbul ignore next */
clipboard.waitPasteData(e, ({ componentsTree }) => {
if (componentsTree) {
const { target, index } = designer.getSuitableInsertion(componentsTree) || {};
if (!target) {
return;
}
let canAddComponentsTree = componentsTree.filter((i) => {
return doc.checkNestingUp(target, i);
});
if (canAddComponentsTree.length === 0) return;
const nodes = insertChildren(target, canAddComponentsTree, index);
if (nodes) {
doc.selection.selectAll(nodes.map((o) => o.id));
setTimeout(() => designer.activeTracker.track(nodes[0]), 10);
}
}
});
});
// command + z undo
hotkey.bind(['command+z', 'ctrl+z'], (e) => {
if (isInLiveEditing()) return;
const his = focusing.focusDesigner?.currentHistory;
if (isFormEvent(e) || !his) {
return;
}
e.preventDefault();
const selection = focusing.focusDesigner?.currentSelection;
const curSelected = Array.from(selection?.selected);
his.back();
selection?.selectAll(curSelected);
});
// command + shift + z redo
hotkey.bind(['command+y', 'ctrl+y', 'command+shift+z'], (e) => {
if (isInLiveEditing()) return;
const his = focusing.focusDesigner?.currentHistory;
if (isFormEvent(e) || !his) {
return;
}
e.preventDefault();
const selection = focusing.focusDesigner?.currentSelection;
const curSelected = Array.from(selection?.selected);
his.forward();
selection?.selectAll(curSelected);
});
// sibling selection
hotkey.bind(['left', 'right'], (e, action) => {
if (isInLiveEditing()) return;
const designer = focusing.focusDesigner;
const doc = designer?.currentDocument;
if (isFormEvent(e) || !doc) {
return;
}
e.preventDefault();
const selected = doc.selection.getTopNodes(true);
if (!selected || selected.length < 1) {
return;
}
const firstNode = selected[0];
const silbing = action === 'left' ? firstNode?.prevSibling : firstNode?.nextSibling;
silbing?.select();
});
hotkey.bind(['up', 'down'], (e, action) => {
if (isInLiveEditing()) return;
const designer = focusing.focusDesigner;
const doc = designer?.currentDocument;
if (isFormEvent(e) || !doc) {
return;
}
e.preventDefault();
const selected = doc.selection.getTopNodes(true);
if (!selected || selected.length < 1) {
return;
}
const firstNode = selected[0];
if (action === 'down') {
const next = getNextForSelect(firstNode, true, firstNode.getParent());
next?.select();
} else if (action === 'up') {
const prev = getPrevForSelect(firstNode, true, firstNode.getParent());
prev?.select();
}
});
hotkey.bind(['option+left', 'option+right'], (e, action) => {
if (isInLiveEditing()) return;
const designer = focusing.focusDesigner;
const doc = designer?.currentDocument;
if (isFormEvent(e) || !doc) {
return;
}
e.preventDefault();
const selected = doc.selection.getTopNodes(true);
if (!selected || selected.length < 1) {
return;
}
// TODO: 此处需要增加判断当前节点是否可被操作移动原ve里是用 node.canOperating()来判断
// TODO: 移动逻辑也需要重新梳理,对于移动目标位置的选择,是否可以移入,需要增加判断
const firstNode = selected[0];
const parent = firstNode.getParent();
if (!parent) return;
const isPrev = action && /(left)$/.test(action);
const silbing = isPrev ? firstNode.prevSibling : firstNode.nextSibling;
if (silbing) {
if (isPrev) {
parent.insertBefore(firstNode, silbing);
} else {
parent.insertAfter(firstNode, silbing);
}
firstNode?.select();
}
});
hotkey.bind(['option+up'], (e) => {
if (isInLiveEditing()) return;
const designer = focusing.focusDesigner;
const doc = designer?.currentDocument;
if (isFormEvent(e) || !doc) {
return;
}
e.preventDefault();
const selected = doc.selection.getTopNodes(true);
if (!selected || selected.length < 1) {
return;
}
// TODO: 此处需要增加判断当前节点是否可被操作移动原ve里是用 node.canOperating()来判断
// TODO: 移动逻辑也需要重新梳理,对于移动目标位置的选择,是否可以移入,需要增加判断
const firstNode = selected[0];
const parent = firstNode.getParent();
if (!parent) {
return;
}
const silbing = firstNode.prevSibling;
if (silbing) {
if (silbing.isContainer()) {
const place = silbing.getSuitablePlace(firstNode, null);
place.container.insertAfter(firstNode, place.ref);
} else {
parent.insertBefore(firstNode, silbing);
}
firstNode?.select();
} else {
const place = parent.getSuitablePlace(firstNode, null); // upwards
if (place) {
place.container.insertBefore(firstNode, place.ref);
firstNode?.select();
}
}
});
hotkey.bind(['option+down'], (e) => {
if (isInLiveEditing()) return;
const designer = focusing.focusDesigner;
const doc = designer?.currentDocument;
if (isFormEvent(e) || !doc) {
return;
}
e.preventDefault();
const selected = doc.selection.getTopNodes(true);
if (!selected || selected.length < 1) {
return;
}
// TODO: 此处需要增加判断当前节点是否可被操作移动原ve里是用 node.canOperating()来判断
// TODO: 移动逻辑也需要重新梳理,对于移动目标位置的选择,是否可以移入,需要增加判断
const firstNode = selected[0];
const parent = firstNode.getParent();
if (!parent) {
return;
}
const silbing = firstNode.nextSibling;
if (silbing) {
if (silbing.isContainer()) {
// const place = silbing.getSuitablePlace(firstNode, null);
silbing.insertBefore(firstNode, undefined);
// place.container.insertBefore(firstNode, place.ref);
} else {
parent.insertAfter(firstNode, silbing);
}
firstNode?.select();
} else {
const place = parent.getSuitablePlace(firstNode, null); // upwards
if (place) {
place.container.insertAfter(firstNode, place.ref);
firstNode?.select();
}
}
});

View File

@ -109,4 +109,4 @@ class Clipboard {
}
}
export default new Clipboard();
export const clipboard = new Clipboard();

View File

@ -4,16 +4,20 @@ import BuiltinDragGhostComponent from './drag-ghost';
import { Designer, DesignerProps } from './designer';
import { ProjectView } from '../project';
import './designer.less';
import clipboard from './clipboard';
import { clipboard } from './clipboard';
export class DesignerView extends Component<DesignerProps & {
type IProps = DesignerProps & {
designer?: Designer;
}> {
readonly designer: Designer;
};
constructor(props: any) {
export class DesignerView extends Component<IProps> {
readonly designer: Designer;
readonly viewName: string | undefined;
constructor(props: IProps) {
super(props);
const { designer, ...designerProps } = props;
this.viewName = designer?.viewName;
if (designer) {
this.designer = designer;
designer.setProps(designerProps);

View File

@ -1,53 +1,63 @@
import { ComponentType } from 'react';
import { obx, computed, autorun, makeObservable, IReactionPublic, IReactionOptions, IReactionDisposer } from '@alilc/lowcode-editor-core';
import {
ProjectSchema,
ComponentMetadata,
ComponentAction,
NpmInfo,
IEditor,
CompositeObject,
PropsList,
NodeSchema,
PropsTransducer,
IPublicTypeProjectSchema,
IPublicTypeComponentMetadata,
IPublicTypeComponentAction,
IPublicTypeNpmInfo,
IPublicModelEditor,
IPublicTypeCompositeObject,
IPublicTypePropsList,
IPublicTypeNodeSchema,
IPublicTypePropsTransducer,
IShellModelFactory,
IPublicModelDragObject,
IPublicModelScrollable,
IDesigner,
IPublicModelScroller,
IPublicTypeLocationData,
IPublicEnumTransformStage,
} from '@alilc/lowcode-types';
import { megreAssets, AssetsJson, isNodeSchema } from '@alilc/lowcode-utils';
import { megreAssets, IPublicTypeAssetsJson, isNodeSchema, isDragNodeObject, isDragNodeDataObject, isLocationChildrenDetail, Logger } from '@alilc/lowcode-utils';
import { Project } from '../project';
import { Node, DocumentModel, insertChildren, ParentalNode, TransformStage } from '../document';
import { Node, DocumentModel, insertChildren, INode } from '../document';
import { ComponentMeta } from '../component-meta';
import { INodeSelector, Component } from '../simulator';
import { Scroller, IScrollable } from './scroller';
import { Dragon, isDragNodeObject, isDragNodeDataObject, LocateEvent, DragObject } from './dragon';
import { Scroller } from './scroller';
import { Dragon, ILocateEvent } from './dragon';
import { ActiveTracker } from './active-tracker';
import { Detecting } from './detecting';
import { DropLocation, LocationData, isLocationChildrenDetail } from './location';
import { DropLocation } from './location';
import { OffsetObserver, createOffsetObserver } from './offset-observer';
import { focusing } from './focusing';
import { SettingTopEntry } from './setting';
import { BemToolsManager } from '../builtin-simulator/bem-tools/manager';
const logger = new Logger({ level: 'warn', bizName: 'designer' });
export interface DesignerProps {
editor: IEditor;
editor: IPublicModelEditor;
shellModelFactory: IShellModelFactory;
className?: string;
style?: object;
defaultSchema?: ProjectSchema;
defaultSchema?: IPublicTypeProjectSchema;
hotkeys?: object;
simulatorProps?: object | ((document: DocumentModel) => object);
simulatorComponent?: ComponentType<any>;
dragGhostComponent?: ComponentType<any>;
suspensed?: boolean;
componentMetadatas?: ComponentMetadata[];
globalComponentActions?: ComponentAction[];
componentMetadatas?: IPublicTypeComponentMetadata[];
globalComponentActions?: IPublicTypeComponentAction[];
onMount?: (designer: Designer) => void;
onDragstart?: (e: LocateEvent) => void;
onDrag?: (e: LocateEvent) => void;
onDragend?: (e: { dragObject: DragObject; copy: boolean }, loc?: DropLocation) => void;
onDragstart?: (e: ILocateEvent) => void;
onDrag?: (e: ILocateEvent) => void;
onDragend?: (e: { dragObject: IPublicModelDragObject; copy: boolean }, loc?: DropLocation) => void;
viewName?: string;
[key: string]: any;
}
export class Designer {
export class Designer implements IDesigner {
readonly dragon = new Dragon(this);
readonly activeTracker = new ActiveTracker();
@ -56,7 +66,7 @@ export class Designer {
readonly project: Project;
readonly editor: IEditor;
readonly editor: IPublicModelEditor;
readonly bemToolsManager = new BemToolsManager(this);
@ -74,14 +84,17 @@ export class Designer {
return this.currentDocument?.selection;
}
viewName: string | undefined;
constructor(props: DesignerProps) {
makeObservable(this);
const { editor, shellModelFactory } = props;
const { editor, viewName, shellModelFactory } = props;
this.editor = editor;
this.viewName = viewName;
this.shellModelFactory = shellModelFactory;
this.setProps(props);
this.project = new Project(this, props.defaultSchema);
this.project = new Project(this, props.defaultSchema, viewName);
this.dragon.onDragstart((e) => {
this.detecting.enable = false;
@ -113,6 +126,7 @@ export class Designer {
this.dragon.onDragend((e) => {
const { dragObject, copy } = e;
logger.debug('onDragend: dragObject ', dragObject, ' copy ', copy);
const loc = this._dropLocation;
if (loc) {
if (isLocationChildrenDetail(loc.detail) && loc.detail.valid !== false) {
@ -201,7 +215,7 @@ export class Designer {
};
postEvent(event: string, ...args: any[]) {
this.editor.emit(`designer.${event}`, ...args);
this.editor.eventBus.emit(`designer.${event}`, ...args);
}
private _dropLocation?: DropLocation;
@ -213,14 +227,14 @@ export class Designer {
/**
* dragon
*/
createLocation(locationData: LocationData): DropLocation {
createLocation(locationData: IPublicTypeLocationData): DropLocation {
const loc = new DropLocation(locationData);
if (this._dropLocation && this._dropLocation.document !== loc.document) {
this._dropLocation.document.internalSetDropLocation(null);
this._dropLocation.document.dropLocation = null;
}
this._dropLocation = loc;
this.postEvent('dropLocation.change', loc);
loc.document.internalSetDropLocation(loc);
loc.document.dropLocation = loc;
this.activeTracker.track({ node: loc.target, detail: loc.detail });
return loc;
}
@ -230,13 +244,13 @@ export class Designer {
*/
clearLocation() {
if (this._dropLocation) {
this._dropLocation.document.internalSetDropLocation(null);
this._dropLocation.document.dropLocation = null;
}
this.postEvent('dropLocation.change', undefined);
this._dropLocation = undefined;
}
createScroller(scrollable: IScrollable) {
createScroller(scrollable: IPublicModelScrollable): IPublicModelScroller {
return new Scroller(scrollable);
}
@ -275,8 +289,8 @@ export class Designer {
*
*/
getSuitableInsertion(
insertNode?: Node | NodeSchema | NodeSchema[],
): { target: ParentalNode; index?: number } | null {
insertNode?: INode | IPublicTypeNodeSchema | IPublicTypeNodeSchema[],
): { target: INode; index?: number } | null {
const activeDoc = this.project.currentDocument;
if (!activeDoc) {
return null;
@ -287,12 +301,12 @@ export class Designer {
this.getComponentMeta(insertNode[0].componentName).isModal
) {
return {
target: activeDoc.rootNode as ParentalNode,
target: activeDoc.rootNode as INode,
};
}
const focusNode = activeDoc.focusNode!;
const nodes = activeDoc.selection.getNodes();
const refNode = nodes.find(item => focusNode.contains(item));
const refNode = nodes.find((item) => focusNode.contains(item));
let target;
let index: number | undefined;
if (!refNode || refNode === focusNode) {
@ -360,16 +374,16 @@ export class Designer {
this.props = props;
}
async loadIncrementalAssets(incrementalAssets: AssetsJson): Promise<void> {
async loadIncrementalAssets(incrementalAssets: IPublicTypeAssetsJson): Promise<void> {
const { components, packages } = incrementalAssets;
components && this.buildComponentMetasMap(components);
if (packages) {
await this.project.simulator!.setupComponents(packages);
await this.project.simulator?.setupComponents(packages);
}
if (components) {
// 合并assets
let assets = this.editor.get('assets');
// 合并 assets
let assets = this.editor.get('assets') || {};
let newAssets = megreAssets(assets, incrementalAssets);
// 对于 assets 存在需要二次网络下载的过程,必须 await 等待结束之后,再进行事件触发
await this.editor.set('assets', newAssets);
@ -377,7 +391,7 @@ export class Designer {
// TODO: 因为涉及修改 prototype.view之后在 renderer 里修改了 vc 的 view 获取逻辑后,可删除
this.refreshComponentMetasMap();
// 完成加载增量资源后发送事件,方便插件监听并处理相关逻辑
this.editor.emit('designer.incrementalAssetsReady');
this.editor.eventBus.emit('designer.incrementalAssetsReady');
}
/**
@ -397,12 +411,30 @@ export class Designer {
return this._simulatorComponent;
}
@obx.ref private _simulatorProps?: object | ((document: DocumentModel) => object);
@obx.ref private _simulatorProps?: object | ((project: Project) => object);
@computed get simulatorProps(): object | ((project: Project) => object) {
@computed get simulatorProps(): object {
if (typeof this._simulatorProps === 'function') {
return this._simulatorProps(this.project);
}
return this._simulatorProps || {};
}
/**
*
*/
@computed get projectSimulatorProps(): any {
return {
...this.simulatorProps,
project: this.project,
designer: this,
onMount: (simulator: any) => {
this.project.mountSimulator(simulator);
this.editor.set('simulator', simulator);
},
};
}
@obx.ref private _suspensed = false;
get suspensed(): boolean {
@ -417,11 +449,11 @@ export class Designer {
}
}
get schema(): ProjectSchema {
get schema(): IPublicTypeProjectSchema {
return this.project.getSchema();
}
setSchema(schema?: ProjectSchema) {
setSchema(schema?: IPublicTypeProjectSchema) {
this.project.load(schema);
}
@ -429,11 +461,11 @@ export class Designer {
private _lostComponentMetasMap = new Map<string, ComponentMeta>();
buildComponentMetasMap(metas: ComponentMetadata[]) {
buildComponentMetasMap(metas: IPublicTypeComponentMetadata[]) {
metas.forEach((data) => this.createComponentMeta(data));
}
createComponentMeta(data: ComponentMetadata): ComponentMeta {
createComponentMeta(data: IPublicTypeComponentMetadata): ComponentMeta {
const key = data.componentName;
let meta = this._componentMetasMap.get(key);
if (meta) {
@ -455,13 +487,13 @@ export class Designer {
return meta;
}
getGlobalComponentActions(): ComponentAction[] | null {
getGlobalComponentActions(): IPublicTypeComponentAction[] | null {
return this.props?.globalComponentActions || null;
}
getComponentMeta(
componentName: string,
generateMetadata?: () => ComponentMetadata | null,
generateMetadata?: () => IPublicTypeComponentMetadata | null,
) {
if (this._componentMetasMap.has(componentName)) {
return this._componentMetasMap.get(componentName)!;
@ -485,7 +517,7 @@ export class Designer {
return this._componentMetasMap;
}
@computed get componentsMap(): { [key: string]: NpmInfo | Component } {
@computed get componentsMap(): { [key: string]: IPublicTypeNpmInfo | Component } {
const maps: any = {};
const designer = this;
designer._componentMetasMap.forEach((config, key) => {
@ -504,9 +536,9 @@ export class Designer {
return maps;
}
private propsReducers = new Map<TransformStage, PropsTransducer[]>();
private propsReducers = new Map<IPublicEnumTransformStage, IPublicTypePropsTransducer[]>();
transformProps(props: CompositeObject | PropsList, node: Node, stage: TransformStage) {
transformProps(props: IPublicTypeCompositeObject | IPublicTypePropsList, node: Node, stage: IPublicEnumTransformStage) {
if (Array.isArray(props)) {
// current not support, make this future
return props;
@ -528,7 +560,7 @@ export class Designer {
}, props);
}
addPropsReducer(reducer: PropsTransducer, stage: TransformStage) {
addPropsReducer(reducer: IPublicTypePropsTransducer, stage: IPublicEnumTransformStage) {
const reducers = this.propsReducers.get(stage);
if (reducers) {
reducers.push(reducer);

View File

@ -1,10 +1,12 @@
import { makeObservable, obx } from '@alilc/lowcode-editor-core';
import { EventEmitter } from 'events';
import { Node, DocumentModel } from '../document';
import { makeObservable, obx, IEventBus, createModuleEventBus } from '@alilc/lowcode-editor-core';
import { IPublicModelDetecting, IPublicModelNode, IPublicModelDocumentModel } from '@alilc/lowcode-types';
const DETECTING_CHANGE_EVENT = 'detectingChange';
export interface IDetecting extends IPublicModelDetecting {
export class Detecting {
}
export class Detecting implements IDetecting {
@obx.ref private _enable = true;
/**
@ -24,9 +26,9 @@ export class Detecting {
@obx.ref xRayMode = false;
@obx.ref private _current: Node | null = null;
@obx.ref private _current: IPublicModelNode | null = null;
private emitter: EventEmitter = new EventEmitter();
private emitter: IEventBus = createModuleEventBus('Detecting');
constructor() {
makeObservable(this);
@ -36,27 +38,27 @@ export class Detecting {
return this._current;
}
capture(node: Node | null) {
capture(node: IPublicModelNode | null) {
if (this._current !== node) {
this._current = node;
this.emitter.emit(DETECTING_CHANGE_EVENT, this.current);
}
}
release(node: Node | null) {
release(node: IPublicModelNode | null) {
if (this._current === node) {
this._current = null;
this.emitter.emit(DETECTING_CHANGE_EVENT, this.current);
}
}
leave(document: DocumentModel | undefined) {
leave(document: IPublicModelDocumentModel | undefined) {
if (this.current && this.current.document === document) {
this._current = null;
}
}
onDetectingChange(fn: (node: Node) => void) {
onDetectingChange(fn: (node: IPublicModelNode) => void) {
this.emitter.on(DETECTING_CHANGE_EVENT, fn);
return () => {
this.emitter.off(DETECTING_CHANGE_EVENT, fn);

View File

@ -1,10 +1,10 @@
import { Component, ReactElement } from 'react';
import { observer, obx, Title, makeObservable } from '@alilc/lowcode-editor-core';
import { Designer } from '../designer';
import { DragObject, isDragNodeObject } from '../dragon';
import { isDragNodeObject } from '../dragon';
import { isSimulatorHost } from '../../simulator';
import './ghost.less';
import { I18nData, NodeSchema } from '@alilc/lowcode-types';
import { IPublicTypeI18nData, IPublicTypeNodeSchema, IPublicModelDragObject } from '@alilc/lowcode-types';
type offBinding = () => any;
@ -12,7 +12,7 @@ type offBinding = () => any;
export default class DragGhost extends Component<{ designer: Designer }> {
private dispose: offBinding[] = [];
@obx.ref private titles: (string | I18nData | ReactElement)[] | null = null;
@obx.ref private titles: (string | IPublicTypeI18nData | ReactElement)[] | null = null;
@obx.ref private x = 0;
@ -54,14 +54,14 @@ export default class DragGhost extends Component<{ designer: Designer }> {
];
}
getTitles(dragObject: DragObject) {
getTitles(dragObject: IPublicModelDragObject) {
if (isDragNodeObject(dragObject)) {
return dragObject.nodes.map((node) => node.title);
}
const dataList = Array.isArray(dragObject.data) ? dragObject.data : [dragObject.data];
return dataList.map((item: NodeSchema, i) => (this.props.designer.getComponentMeta(item.componentName).title));
return dataList.map((item: IPublicTypeNodeSchema, i) => (this.props.designer.getComponentMeta(item.componentName).title));
}
componentWillUnmount() {

View File

@ -1,97 +1,53 @@
import { EventEmitter } from 'events';
import { obx, makeObservable } from '@alilc/lowcode-editor-core';
import { DragNodeObject, DragAnyObject, DragObjectType, DragNodeDataObject, DragObject, IPublicModelNode } from '@alilc/lowcode-types';
import { obx, makeObservable, IEventBus, createModuleEventBus } from '@alilc/lowcode-editor-core';
import {
IPublicTypeDragNodeObject,
IPublicTypeDragAnyObject,
IPublicEnumDragObjectType,
IPublicTypeDragNodeDataObject,
IPublicModelDragObject,
IPublicModelNode,
IPublicModelDragon,
IPublicModelLocateEvent,
ISensor,
} from '@alilc/lowcode-types';
import { setNativeSelection, cursor } from '@alilc/lowcode-utils';
import { DropLocation } from './location';
import { Node, DocumentModel } from '../document';
import { ISimulatorHost, isSimulatorHost, NodeInstance, ComponentInstance } from '../simulator';
import { Node } from '../document';
import { ISimulatorHost, isSimulatorHost } from '../simulator';
import { Designer } from './designer';
import { makeEventsHandler } from '../utils/misc';
export interface LocateEvent {
export interface ILocateEvent extends IPublicModelLocateEvent {
readonly type: 'LocateEvent';
/**
*
*/
readonly globalX: number;
readonly globalY: number;
/**
*
*/
readonly originalEvent: MouseEvent | DragEvent;
/**
*
*/
readonly dragObject: DragObject;
/**
*
*/
sensor?: ISensor;
// ======= 以下是 激活的 sensor 将填充的值 ========
/**
*
*/
target?: Element | null;
/**
*
*/
canvasX?: number;
canvasY?: number;
/**
*
*/
documentModel?: DocumentModel;
/**
* canvasX,canvasY,
*/
fixed?: true;
}
/**
*
* @deprecated use same function in @alilc/lowcode-utils
*/
export interface ISensor {
/**
* false
*/
readonly sensorAvailable: boolean;
/**
*
*/
fixEvent(e: LocateEvent): LocateEvent;
/**
*
*/
locate(e: LocateEvent): DropLocation | undefined | null;
/**
*
*/
isEnter(e: LocateEvent): boolean;
/**
*
*/
deactiveSensor(): void;
/**
*
*/
getNodeInstanceFromElement(e: Element | null): NodeInstance<ComponentInstance> | null;
export function isDragNodeObject(obj: any): obj is IPublicTypeDragNodeObject {
return obj && obj.type === IPublicEnumDragObjectType.Node;
}
export function isDragNodeObject(obj: any): obj is DragNodeObject {
return obj && obj.type === DragObjectType.Node;
/**
* @deprecated use same function in @alilc/lowcode-utils
*/
export function isDragNodeDataObject(obj: any): obj is IPublicTypeDragNodeDataObject {
return obj && obj.type === IPublicEnumDragObjectType.NodeData;
}
export function isDragNodeDataObject(obj: any): obj is DragNodeDataObject {
return obj && obj.type === DragObjectType.NodeData;
/**
* @deprecated use same function in @alilc/lowcode-utils
*/
export function isDragAnyObject(obj: any): obj is IPublicTypeDragAnyObject {
return obj && obj.type !== IPublicEnumDragObjectType.NodeData && obj.type !== IPublicEnumDragObjectType.Node;
}
export function isDragAnyObject(obj: any): obj is DragAnyObject {
return obj && obj.type !== DragObjectType.NodeData && obj.type !== DragObjectType.Node;
}
export function isLocateEvent(e: any): e is LocateEvent {
export function isLocateEvent(e: any): e is ILocateEvent {
return e && e.type === 'LocateEvent';
}
@ -128,7 +84,7 @@ export function setShaken(e: any) {
e.shaken = true;
}
function getSourceSensor(dragObject: DragObject): ISimulatorHost | null {
function getSourceSensor(dragObject: IPublicModelDragObject): ISimulatorHost | null {
if (!isDragNodeObject(dragObject)) {
return null;
}
@ -139,12 +95,18 @@ function isDragEvent(e: any): e is DragEvent {
return e?.type?.startsWith('drag');
}
export interface IDragon extends IPublicModelDragon {
}
/**
* Drag-on
*/
export class Dragon {
export class Dragon implements IDragon {
private sensors: ISensor[] = [];
key = Math.random();
/**
* current active sensor,
*/
@ -162,10 +124,13 @@ export class Dragon {
return this._dragging;
}
private emitter = new EventEmitter();
viewName: string | undefined;
private emitter: IEventBus = createModuleEventBus('Dragon');
constructor(readonly designer: Designer) {
makeObservable(this);
this.viewName = designer.viewName;
}
/**
@ -173,7 +138,7 @@ export class Dragon {
* @param shell container element
* @param boost boost got a drag object
*/
from(shell: Element, boost: (e: MouseEvent) => DragObject | null) {
from(shell: Element, boost: (e: MouseEvent) => IPublicModelDragObject | null) {
const mousedown = (e: MouseEvent) => {
// ESC or RightClick
if (e.which === 3 || e.button === 2) {
@ -200,7 +165,7 @@ export class Dragon {
* @param dragObject
* @param boostEvent
*/
boost(dragObject: DragObject, boostEvent: MouseEvent | DragEvent, fromRglNode?: Node | IPublicModelNode) {
boost(dragObject: IPublicModelDragObject, boostEvent: MouseEvent | DragEvent, fromRglNode?: Node | IPublicModelNode) {
const { designer } = this;
const masterSensors = this.getMasterSensors();
const handleEvents = makeEventsHandler(boostEvent, masterSensors);
@ -449,7 +414,7 @@ export class Dragon {
};
// create drag locate event
const createLocateEvent = (e: MouseEvent | DragEvent): LocateEvent => {
const createLocateEvent = (e: MouseEvent | DragEvent): ILocateEvent => {
const evt: any = {
type: 'LocateEvent',
dragObject,
@ -495,7 +460,7 @@ export class Dragon {
const sourceSensor = getSourceSensor(dragObject);
/* istanbul ignore next */
const chooseSensor = (e: LocateEvent) => {
const chooseSensor = (e: ILocateEvent) => {
// this.sensors will change on dragstart
const sensors: ISensor[] = this.sensors.concat(masterSensors as ISensor[]);
let sensor =
@ -646,21 +611,21 @@ export class Dragon {
}
}
onDragstart(func: (e: LocateEvent) => any) {
onDragstart(func: (e: ILocateEvent) => any) {
this.emitter.on('dragstart', func);
return () => {
this.emitter.removeListener('dragstart', func);
};
}
onDrag(func: (e: LocateEvent) => any) {
onDrag(func: (e: ILocateEvent) => any) {
this.emitter.on('drag', func);
return () => {
this.emitter.removeListener('drag', func);
};
}
onDragend(func: (x: { dragObject: DragObject; copy: boolean }) => any) {
onDragend(func: (x: { dragObject: IPublicModelDragObject; copy: boolean }) => any) {
this.emitter.on('dragend', func);
return () => {
this.emitter.removeListener('dragend', func);

View File

@ -1,5 +1,3 @@
import './builtin-hotkey';
export * from './designer';
export * from './designer-view';
export * from './dragon';
@ -9,3 +7,6 @@ export * from './offset-observer';
export * from './scroller';
export * from './setting';
export * from './active-tracker';
export * from './focusing';
export * from '../document';
export * from './clipboard';

View File

@ -1,43 +1,14 @@
import { DocumentModel, Node as ComponentNode, ParentalNode } from '../document';
import { LocateEvent } from './dragon';
import { INode } from '../document';
import { ILocateEvent } from './dragon';
import {
IPublicModelDocumentModel,
IPublicModelDropLocation,
IPublicTypeLocationDetailType,
IPublicTypeRect,
IPublicTypeLocationDetail,
IPublicTypeLocationData,
} from '@alilc/lowcode-types';
export interface LocationData {
target: ParentalNode; // shadowNode | ConditionFlow | ElementNode | RootNode
detail: LocationDetail;
source: string;
event: LocateEvent;
}
export enum LocationDetailType {
Children = 'Children',
Prop = 'Prop',
}
export interface LocationChildrenDetail {
type: LocationDetailType.Children;
index?: number | null;
/**
*
*/
valid?: boolean;
edge?: DOMRect;
near?: {
node: ComponentNode;
pos: 'before' | 'after' | 'replace';
rect?: Rect;
align?: 'V' | 'H';
};
focus?: { type: 'slots' } | { type: 'node'; node: ParentalNode };
}
export interface LocationPropDetail {
// cover 形态,高亮 domNode如果 domNode 为空,取 container 的值
type: LocationDetailType.Prop;
name: string;
domNode?: HTMLElement;
}
export type LocationDetail = LocationChildrenDetail | LocationPropDetail | { type: string; [key: string]: any };
export interface Point {
clientX: number;
@ -53,17 +24,18 @@ export type Rects = DOMRect[] & {
elements: Array<Element | Text>;
};
export type Rect = DOMRect & {
elements: Array<Element | Text>;
computed?: boolean;
};
export function isLocationData(obj: any): obj is LocationData {
/**
* @deprecated use same function in @alilc/lowcode-utils
*/
export function isLocationData(obj: any): boolean {
return obj && obj.target && obj.detail;
}
export function isLocationChildrenDetail(obj: any): obj is LocationChildrenDetail {
return obj && obj.type === LocationDetailType.Children;
/**
* @deprecated use same function in @alilc/lowcode-utils
*/
export function isLocationChildrenDetail(obj: any): boolean {
return obj && obj.type === IPublicTypeLocationDetailType.Children;
}
export function isRowContainer(container: Element | Text, win?: Window) {
@ -92,7 +64,7 @@ export function isChildInline(child: Element | Text, win?: Window) {
return /^inline/.test(style.getPropertyValue('display')) || /^(left|right)$/.test(style.getPropertyValue('float'));
}
export function getRectTarget(rect: Rect | null) {
export function getRectTarget(rect: IPublicTypeRect | null) {
if (!rect || rect.computed) {
return null;
}
@ -100,7 +72,7 @@ export function getRectTarget(rect: Rect | null) {
return els && els.length > 0 ? els[0]! : null;
}
export function isVerticalContainer(rect: Rect | null) {
export function isVerticalContainer(rect: IPublicTypeRect | null) {
const el = getRectTarget(rect);
if (!el) {
return false;
@ -108,7 +80,7 @@ export function isVerticalContainer(rect: Rect | null) {
return isRowContainer(el);
}
export function isVertical(rect: Rect | null) {
export function isVertical(rect: IPublicTypeRect | null) {
const el = getRectTarget(rect);
if (!el) {
return false;
@ -127,28 +99,42 @@ function isDocument(elem: any): elem is Document {
export function getWindow(elem: Element | Document): Window {
return (isDocument(elem) ? elem : elem.ownerDocument!).defaultView!;
}
export interface IDropLocation extends IPublicModelDropLocation {
export class DropLocation {
readonly target: ParentalNode;
readonly target: INode;
readonly detail: LocationDetail;
readonly detail: IPublicTypeLocationDetail;
readonly event: LocateEvent;
readonly event: ILocateEvent;
readonly source: string;
get document(): DocumentModel {
get document(): IPublicModelDocumentModel;
clone(event: ILocateEvent): IDropLocation;
}
export class DropLocation implements IDropLocation {
readonly target: INode;
readonly detail: IPublicTypeLocationDetail;
readonly event: ILocateEvent;
readonly source: string;
get document(): IPublicModelDocumentModel {
return this.target.document;
}
constructor({ target, detail, source, event }: LocationData) {
constructor({ target, detail, source, event }: IPublicTypeLocationData) {
this.target = target;
this.detail = detail;
this.source = source;
this.event = event;
}
clone(event: LocateEvent): DropLocation {
clone(event: ILocateEvent): DropLocation {
return new DropLocation({
target: this.target,
detail: this.detail,

View File

@ -2,7 +2,7 @@ import requestIdleCallback, { cancelIdleCallback } from 'ric-shim';
import { obx, computed, makeObservable } from '@alilc/lowcode-editor-core';
import { uniqueId } from '@alilc/lowcode-utils';
import { INodeSelector, IViewport } from '../simulator';
import { isRootNode, Node } from '../document';
import { Node } from '../document';
export class OffsetObserver {
readonly id = uniqueId('oobx');

View File

@ -1,6 +1,10 @@
import { isElement } from '@alilc/lowcode-utils';
import { IPublicModelScrollTarget, IPublicModelScrollable, IPublicModelScroller } from '@alilc/lowcode-types';
export class ScrollTarget {
export interface IScrollTarget extends IPublicModelScrollTarget {
}
export class ScrollTarget implements IScrollTarget {
get left() {
return 'scrollX' in this.target ? this.target.scrollX : this.target.scrollLeft;
}
@ -44,18 +48,18 @@ function easing(n: number) {
const SCROLL_ACCURCY = 30;
export interface IScrollable {
scrollTarget?: ScrollTarget | Element;
bounds?: DOMRect | null;
scale?: number;
export interface IScroller extends IPublicModelScroller {
}
export class Scroller {
export class Scroller implements IScroller {
private pid: number | undefined;
scrollable: IPublicModelScrollable;
constructor(private scrollable: IScrollable) {}
constructor(scrollable: IPublicModelScrollable) {
this.scrollable = scrollable;
}
get scrollTarget(): ScrollTarget | null {
get scrollTarget(): IScrollTarget | null {
let target = this.scrollable.scrollTarget;
if (!target) {
return null;

View File

@ -1,9 +1,9 @@
import { SettingTarget } from '@alilc/lowcode-types';
import { IPublicModelSettingTarget } from '@alilc/lowcode-types';
import { ComponentMeta } from '../../component-meta';
import { Designer } from '../designer';
import { Node } from '../../document';
export interface SettingEntry extends SettingTarget {
export interface SettingEntry extends IPublicModelSettingTarget {
readonly nodes: Node[];
readonly componentMeta: ComponentMeta | null;
readonly designer: Designer;

View File

@ -1,11 +1,11 @@
import { TitleContent, SetterType, DynamicSetter, FieldExtraProps, FieldConfig, CustomView, ISetValueOptions } from '@alilc/lowcode-types';
import { IPublicTypeTitleContent, IPublicTypeSetterType, IPublicTypeDynamicSetter, IPublicTypeFieldExtraProps, IPublicTypeFieldConfig, IPublicTypeCustomView, IPublicTypeSetValueOptions } from '@alilc/lowcode-types';
import { Transducer } from './utils';
import { SettingPropEntry } from './setting-prop-entry';
import { SettingEntry } from './setting-entry';
import { computed, obx, makeObservable, action } from '@alilc/lowcode-editor-core';
import { cloneDeep, isCustomView, isDynamicSetter } from '@alilc/lowcode-utils';
function getSettingFieldCollectorKey(parent: SettingEntry, config: FieldConfig) {
function getSettingFieldCollectorKey(parent: SettingEntry, config: IPublicTypeFieldConfig) {
let cur = parent;
const path = [config.name];
while (cur !== parent.top) {
@ -24,21 +24,21 @@ export class SettingField extends SettingPropEntry implements SettingEntry {
readonly transducer: Transducer;
private _config: FieldConfig;
private _config: IPublicTypeFieldConfig;
extraProps: FieldExtraProps;
extraProps: IPublicTypeFieldExtraProps;
// ==== dynamic properties ====
private _title?: TitleContent;
private _title?: IPublicTypeTitleContent;
get title() {
// FIXME! intl
return this._title || (typeof this.name === 'number' ? `项目 ${this.name}` : this.name);
}
private _setter?: SetterType | DynamicSetter;
private _setter?: IPublicTypeSetterType | IPublicTypeDynamicSetter;
@computed get setter(): SetterType | null {
@computed get setter(): IPublicTypeSetterType | null {
if (!this._setter) {
return null;
}
@ -61,7 +61,7 @@ export class SettingField extends SettingPropEntry implements SettingEntry {
parent: SettingEntry;
constructor(parent: SettingEntry, config: FieldConfig, settingFieldCollector?: (name: string | number, field: SettingField) => void) {
constructor(parent: SettingEntry, config: IPublicTypeFieldConfig, settingFieldCollector?: (name: string | number, field: SettingField) => void) {
super(parent, config.name, config.type);
makeObservable(this);
const { title, items, setter, extraProps, ...rest } = config;
@ -88,17 +88,17 @@ export class SettingField extends SettingPropEntry implements SettingEntry {
this.transducer = new Transducer(this, { setter });
}
private _items: Array<SettingField | CustomView> = [];
private _items: Array<SettingField | IPublicTypeCustomView> = [];
get items(): Array<SettingField | CustomView> {
get items(): Array<SettingField | IPublicTypeCustomView> {
return this._items;
}
get config(): FieldConfig {
get config(): IPublicTypeFieldConfig {
return this._config;
}
private initItems(items: Array<FieldConfig | CustomView>, settingFieldCollector?: { (name: string | number, field: SettingField): void; (name: string, field: SettingField): void }) {
private initItems(items: Array<IPublicTypeFieldConfig | IPublicTypeCustomView>, settingFieldCollector?: { (name: string | number, field: SettingField): void; (name: string, field: SettingField): void }) {
this._items = items.map((item) => {
if (isCustomView(item)) {
return item;
@ -113,7 +113,7 @@ export class SettingField extends SettingPropEntry implements SettingEntry {
}
// 创建子配置项,通常用于 object/array 类型数据
createField(config: FieldConfig): SettingField {
createField(config: IPublicTypeFieldConfig): SettingField {
return new SettingField(this, config);
}
@ -123,14 +123,14 @@ export class SettingField extends SettingPropEntry implements SettingEntry {
// ======= compatibles for vision ======
getConfig<K extends keyof FieldConfig>(configName?: K): FieldConfig[K] | FieldConfig {
getConfig<K extends keyof IPublicTypeFieldConfig>(configName?: K): IPublicTypeFieldConfig[K] | IPublicTypeFieldConfig {
if (configName) {
return this.config[configName];
}
return this._config;
}
getItems(filter?: (item: SettingField | CustomView) => boolean): Array<SettingField | CustomView> {
getItems(filter?: (item: SettingField | IPublicTypeCustomView) => boolean): Array<SettingField | IPublicTypeCustomView> {
return this._items.filter(item => {
if (filter) {
return filter(item);
@ -142,7 +142,7 @@ export class SettingField extends SettingPropEntry implements SettingEntry {
private hotValue: any;
@action
setValue(val: any, isHotValue?: boolean, force?: boolean, extraOptions?: ISetValueOptions) {
setValue(val: any, isHotValue?: boolean, force?: boolean, extraOptions?: IPublicTypeSetValueOptions) {
if (isHotValue) {
this.setHotValue(val, extraOptions);
return;
@ -177,7 +177,7 @@ export class SettingField extends SettingPropEntry implements SettingEntry {
}
@action
setHotValue(data: any, options?: ISetValueOptions) {
setHotValue(data: any, options?: IPublicTypeSetValueOptions) {
this.hotValue = data;
const value = this.transducer.toNative(data);
if (options) {
@ -208,7 +208,9 @@ export class SettingField extends SettingPropEntry implements SettingEntry {
return this.designer.autorun(action, true);
}
}
/**
* @deprecated use same function from '@alilc/lowcode-utils' instead
*/
export function isSettingField(obj: any): obj is SettingField {
return obj && obj.isSettingField;
}

View File

@ -1,16 +1,15 @@
import { obx, computed, makeObservable, runInAction } from '@alilc/lowcode-editor-core';
import { GlobalEvent, IEditor, ISetValueOptions } from '@alilc/lowcode-types';
import { uniqueId, isJSExpression } from '@alilc/lowcode-utils';
import { obx, computed, makeObservable, runInAction, IEventBus, createModuleEventBus } from '@alilc/lowcode-editor-core';
import { GlobalEvent, IPublicModelEditor, IPublicTypeSetValueOptions } from '@alilc/lowcode-types';
import { uniqueId, isJSExpression, isSettingField } from '@alilc/lowcode-utils';
import { SettingEntry } from './setting-entry';
import { Node } from '../../document';
import { ComponentMeta } from '../../component-meta';
import { Designer } from '../designer';
import { EventEmitter } from 'events';
import { isSettingField } from './setting-field';
import { Setters } from '@alilc/lowcode-shell';
export class SettingPropEntry implements SettingEntry {
// === static properties ===
readonly editor: IEditor;
readonly editor: IPublicModelEditor;
readonly isSameComponent: boolean;
@ -18,6 +17,8 @@ export class SettingPropEntry implements SettingEntry {
readonly isSingle: boolean;
readonly setters: Setters;
readonly nodes: Node[];
readonly componentMeta: ComponentMeta | null;
@ -32,7 +33,7 @@ export class SettingPropEntry implements SettingEntry {
readonly id = uniqueId('entry');
readonly emitter = new EventEmitter();
readonly emitter: IEventBus = createModuleEventBus('SettingPropEntry');
// ==== dynamic properties ====
@obx.ref private _name: string | number;
@ -70,6 +71,7 @@ export class SettingPropEntry implements SettingEntry {
// copy parent static properties
this.editor = parent.editor;
this.nodes = parent.nodes;
this.setters = parent.setters;
this.componentMeta = parent.componentMeta;
this.isSameComponent = parent.isSameComponent;
this.isMultiple = parent.isMultiple;
@ -173,7 +175,7 @@ export class SettingPropEntry implements SettingEntry {
/**
*
*/
setValue(val: any, isHotValue?: boolean, force?: boolean, extraOptions?: ISetValueOptions) {
setValue(val: any, isHotValue?: boolean, force?: boolean, extraOptions?: IPublicTypeSetValueOptions) {
const oldValue = this.getValue();
if (this.type === 'field') {
this.parent.setPropValue(this.name, val);
@ -287,7 +289,7 @@ export class SettingPropEntry implements SettingEntry {
/**
* @deprecated
*/
valueChange(options: ISetValueOptions = {}) {
valueChange(options: IPublicTypeSetValueOptions = {}) {
this.emitter.emit('valuechange', options);
if (this.parent && isSettingField(this.parent)) {
@ -296,7 +298,7 @@ export class SettingPropEntry implements SettingEntry {
}
notifyValueChange(oldValue: any, newValue: any) {
this.editor.emit(GlobalEvent.Node.Prop.Change, {
this.editor.eventBus.emit(GlobalEvent.Node.Prop.Change, {
node: this.getNode(),
prop: this,
oldValue,

View File

@ -1,13 +1,13 @@
import { EventEmitter } from 'events';
import { CustomView, IEditor } from '@alilc/lowcode-types';
import { IPublicTypeCustomView, IPublicModelEditor } from '@alilc/lowcode-types';
import { isCustomView } from '@alilc/lowcode-utils';
import { computed } from '@alilc/lowcode-editor-core';
import { computed, IEventBus, createModuleEventBus } from '@alilc/lowcode-editor-core';
import { SettingEntry } from './setting-entry';
import { SettingField } from './setting-field';
import { SettingPropEntry } from './setting-prop-entry';
import { Node } from '../../document';
import { ComponentMeta } from '../../component-meta';
import { Designer } from '../designer';
import { Setters } from '@alilc/lowcode-shell';
function generateSessionId(nodes: Node[]) {
return nodes
@ -17,9 +17,9 @@ function generateSessionId(nodes: Node[]) {
}
export class SettingTopEntry implements SettingEntry {
private emitter = new EventEmitter();
private emitter: IEventBus = createModuleEventBus('SettingTopEntry');
private _items: Array<SettingField | CustomView> = [];
private _items: Array<SettingField | IPublicTypeCustomView> = [];
private _componentMeta: ComponentMeta | null = null;
@ -72,15 +72,18 @@ export class SettingTopEntry implements SettingEntry {
readonly designer: Designer;
readonly setters: Setters;
disposeFunctions: any[] = [];
constructor(readonly editor: IEditor, readonly nodes: Node[]) {
constructor(readonly editor: IPublicModelEditor, readonly nodes: Node[]) {
if (!Array.isArray(nodes) || nodes.length < 1) {
throw new ReferenceError('nodes should not be empty');
}
this.id = generateSessionId(nodes);
this.first = nodes[0];
this.designer = this.first.document.designer;
this.setters = editor.get('setters') as Setters;
// setups
this.setupComponentMeta();

View File

@ -1,8 +1,7 @@
// all this file for polyfill vision logic
import { isValidElement } from 'react';
import { FieldConfig, SetterConfig } from '@alilc/lowcode-types';
import { IPublicTypeFieldConfig, IPublicTypeSetterConfig } from '@alilc/lowcode-types';
import { isSetterConfig, isDynamicSetter } from '@alilc/lowcode-utils';
import { getSetter } from '@alilc/lowcode-editor-core';
import { SettingField } from './setting-field';
function getHotterFromSetter(setter) {
@ -36,12 +35,12 @@ export class Transducer {
context: any;
constructor(context: SettingField, config: { setter: FieldConfig['setter'] }) {
constructor(context: SettingField, config: { setter: IPublicTypeFieldConfig['setter'] }) {
let { setter } = config;
// 1. validElement
// 2. SetterConfig
// 3. SetterConfig[]
// 2. IPublicTypeSetterConfig
// 3. IPublicTypeSetterConfig[]
if (Array.isArray(setter)) {
setter = setter[0];
} else if (isValidElement(setter) && setter.type.displayName === 'MixedSetter') {
@ -59,12 +58,12 @@ export class Transducer {
let isDynamic = true;
if (isSetterConfig(setter)) {
const { componentName, isDynamic: dynamicFlag } = setter as SetterConfig;
const { componentName, isDynamic: dynamicFlag } = setter as IPublicTypeSetterConfig;
setter = componentName;
isDynamic = dynamicFlag !== false;
}
if (typeof setter === 'string') {
const { component, isDynamic: dynamicFlag } = getSetter(setter) || {};
const { component, isDynamic: dynamicFlag } = context.setters.getSetter(setter) || {};
setter = component;
// 如果在物料配置中声明了,在 registerSetter 没有声明,取物料配置中的声明
isDynamic = dynamicFlag === undefined ? isDynamic : dynamicFlag !== false;

View File

@ -1,15 +1,31 @@
import { makeObservable, obx, engineConfig, action, runWithGlobalEventOff, wrapWithEventSwitch } from '@alilc/lowcode-editor-core';
import { NodeData, NodeSchema, RootSchema, PageSchema, ComponentsMap, DragNodeObject, DragNodeDataObject } from '@alilc/lowcode-types';
import { EventEmitter } from 'events';
import { makeObservable, obx, engineConfig, action, runWithGlobalEventOff, wrapWithEventSwitch, createModuleEventBus, IEventBus } from '@alilc/lowcode-editor-core';
import {
IPublicTypeNodeData,
IPublicTypeNodeSchema,
IPublicTypeRootSchema,
IPublicTypePageSchema,
IPublicTypeComponentsMap,
IPublicTypeDragNodeObject,
IPublicTypeDragNodeDataObject,
IPublicModelDocumentModel,
IPublicModelSelection,
IPublicModelHistory,
IPublicModelModalNodesManager,
IPublicModelNode,
IPublicApiProject,
IPublicModelDropLocation,
IPublicEnumEventNames,
IPublicEnumTransformStage,
} from '@alilc/lowcode-types';
import { Project } from '../project';
import { ISimulatorHost } from '../simulator';
import { ComponentMeta } from '../component-meta';
import { isDragNodeDataObject, DropLocation, Designer, isDragNodeObject } from '../designer';
import { Node, insertChildren, insertChild, isNode, RootNode, ParentalNode } from './node/node';
import { IDropLocation, Designer } from '../designer';
import { Node, insertChildren, insertChild, isNode, RootNode, INode } from './node/node';
import { Selection } from './selection';
import { History } from './history';
import { TransformStage, ModalNodesManager } from './node';
import { uniqueId, isPlainObject, compatStage, isJSExpression, isDOMText, isNodeSchema } from '@alilc/lowcode-utils';
import { ModalNodesManager } from './node';
import { uniqueId, isPlainObject, compatStage, isJSExpression, isDOMText, isNodeSchema, isDragNodeObject, isDragNodeDataObject } from '@alilc/lowcode-utils';
export type GetDataType<T, NodeType> = T extends undefined
? NodeType extends {
@ -18,8 +34,11 @@ export type GetDataType<T, NodeType> = T extends undefined
? R
: any
: T;
export interface IDocumentModel extends IPublicModelDocumentModel {
export class DocumentModel {
}
export class DocumentModel implements IDocumentModel {
/**
* Page/Component/Block
*/
@ -33,29 +52,29 @@ export class DocumentModel {
/**
*
*/
readonly selection: Selection = new Selection(this);
readonly selection: IPublicModelSelection = new Selection(this);
/**
*
*/
readonly history: History;
readonly history: IPublicModelHistory;
/**
*
*/
readonly modalNodesManager: ModalNodesManager;
readonly modalNodesManager: IPublicModelModalNodesManager;
private _nodesMap = new Map<string, Node>();
private _nodesMap = new Map<string, IPublicModelNode>();
readonly project: Project;
readonly project: IPublicApiProject;
readonly designer: Designer;
@obx.shallow private nodes = new Set<Node>();
@obx.shallow private nodes = new Set<IPublicModelNode>();
private seqId = 0;
private emitter: EventEmitter;
private emitter: IEventBus;
private rootNodeVisitorMap: { [visitorName: string]: any } = {};
@ -100,17 +119,17 @@ export class DocumentModel {
this._drillDownNode = node;
}
private _modalNode?: ParentalNode;
private _modalNode?: INode;
private _blank?: boolean;
private inited = false;
constructor(project: Project, schema?: RootSchema) {
constructor(project: Project, schema?: IPublicTypeRootSchema) {
makeObservable(this);
this.project = project;
this.designer = this.project?.designer;
this.emitter = new EventEmitter();
this.emitter = createModuleEventBus('DocumentModel');
if (!schema) {
this._blank = true;
@ -128,9 +147,9 @@ export class DocumentModel {
);
this.history = new History(
() => this.export(TransformStage.Serilize),
() => this.export(IPublicEnumTransformStage.Serilize),
(schema) => {
this.import(schema as RootSchema, true);
this.import(schema as IPublicTypeRootSchema, true);
this.simulator?.rerender();
},
);
@ -255,14 +274,14 @@ export class DocumentModel {
/**
*
*/
insertNode(parent: ParentalNode, thing: Node | NodeData, at?: number | null, copy?: boolean): Node {
insertNode(parent: INode, thing: Node | IPublicTypeNodeData, at?: number | null, copy?: boolean): Node {
return insertChild(parent, thing, at, copy);
}
/**
*
*/
insertNodes(parent: ParentalNode, thing: Node[] | NodeData[], at?: number | null, copy?: boolean) {
insertNodes(parent: INode, thing: Node[] | IPublicTypeNodeData[], at?: number | null, copy?: boolean) {
return insertChildren(parent, thing, at, copy);
}
@ -275,9 +294,9 @@ export class DocumentModel {
if (typeof idOrNode === 'string') {
id = idOrNode;
node = this.getNode(id);
} else {
node = idOrNode;
id = node.id;
} else if (idOrNode.id) {
id = idOrNode.id;
node = this.getNode(id);
}
if (!node) {
return;
@ -300,13 +319,16 @@ export class DocumentModel {
this._nodesMap.delete(node.id);
}
@obx.ref private _dropLocation: DropLocation | null = null;
@obx.ref private _dropLocation: IDropLocation | null = null;
/**
*
*/
internalSetDropLocation(loc: DropLocation | null) {
set dropLocation(loc: IPublicModelDropLocation | null) {
this._dropLocation = loc;
// pub event
this.designer.editor.eventBus.emit(
IPublicEnumEventNames.DOCUMENT_DROPLOCATION_CHANGED,
{ document: this, location: loc },
);
}
/**
@ -319,7 +341,7 @@ export class DocumentModel {
/**
*
*/
wrapWith(schema: NodeSchema): Node | null {
wrapWith(schema: IPublicTypeNodeSchema): Node | null {
const nodes = this.selection.getTopNodes();
if (nodes.length < 1) {
return null;
@ -341,12 +363,12 @@ export class DocumentModel {
/**
* schema
*/
get schema(): RootSchema {
get schema(): IPublicTypeRootSchema {
return this.rootNode?.schema as any;
}
@action
import(schema: RootSchema, checkId = false) {
import(schema: IPublicTypeRootSchema, checkId = false) {
const drillDownNodeId = this._drillDownNode?.id;
runWithGlobalEventOff(() => {
// TODO: 暂时用饱和式删除,原因是 Slot 节点并不是树节点,无法正常递归删除
@ -363,14 +385,14 @@ export class DocumentModel {
});
}
export(stage: TransformStage = TransformStage.Serilize) {
export(stage: IPublicEnumTransformStage = IPublicEnumTransformStage.Serilize) {
stage = compatStage(stage);
// 置顶只作用于 Page 的第一级子节点,目前还用不到里层的置顶;如果后面有需要可以考虑将这段写到 node-children 中的 export
const currentSchema = this.rootNode?.export(stage);
if (Array.isArray(currentSchema?.children) && currentSchema?.children.length > 0) {
const FixedTopNodeIndex = currentSchema.children
.filter(i => isPlainObject(i))
.findIndex((i => (i as NodeSchema).props?.__isTopFixed__));
.findIndex((i => (i as IPublicTypeNodeSchema).props?.__isTopFixed__));
if (FixedTopNodeIndex > 0) {
const FixedTopNode = currentSchema.children.splice(FixedTopNodeIndex, 1);
currentSchema.children.unshift(FixedTopNode[0]);
@ -382,7 +404,7 @@ export class DocumentModel {
/**
*
*/
getNodeSchema(id: string): NodeData | null {
getNodeSchema(id: string): IPublicTypeNodeData | null {
const node = this.getNode(id);
if (node) {
return node.schema;
@ -505,8 +527,8 @@ export class DocumentModel {
this.rootNode = null;
}
checkNesting(dropTarget: ParentalNode, dragObject: DragNodeObject | NodeSchema | Node | DragNodeDataObject): boolean {
let items: Array<Node | NodeSchema>;
checkNesting(dropTarget: INode, dragObject: IPublicTypeDragNodeObject | IPublicTypeNodeSchema | Node | IPublicTypeDragNodeDataObject): boolean {
let items: Array<Node | IPublicTypeNodeSchema>;
if (isDragNodeDataObject(dragObject)) {
items = Array.isArray(dragObject.data) ? dragObject.data : [dragObject.data];
} else if (isDragNodeObject(dragObject)) {
@ -525,8 +547,8 @@ export class DocumentModel {
* Will be deleted in version 2.0.0.
* Use checkNesting method instead.
*/
checkDropTarget(dropTarget: ParentalNode, dragObject: DragNodeObject | DragNodeDataObject): boolean {
let items: Array<Node | NodeSchema>;
checkDropTarget(dropTarget: INode, dragObject: IPublicTypeDragNodeObject | IPublicTypeDragNodeDataObject): boolean {
let items: Array<Node | IPublicTypeNodeSchema>;
if (isDragNodeDataObject(dragObject)) {
items = Array.isArray(dragObject.data) ? dragObject.data : [dragObject.data];
} else {
@ -538,7 +560,7 @@ export class DocumentModel {
/**
* parentWhitelist
*/
checkNestingUp(parent: ParentalNode, obj: NodeSchema | Node): boolean {
checkNestingUp(parent: INode, obj: IPublicTypeNodeSchema | Node): boolean {
if (isNode(obj) || isNodeSchema(obj)) {
const config = isNode(obj) ? obj.componentMeta : this.getComponentMeta(obj.componentName);
if (config) {
@ -552,7 +574,7 @@ export class DocumentModel {
/**
* childWhitelist
*/
checkNestingDown(parent: ParentalNode, obj: NodeSchema | Node): boolean {
checkNestingDown(parent: INode, obj: IPublicTypeNodeSchema | Node): boolean {
const config = parent.componentMeta;
return config.checkNestingDown(parent, obj);
}
@ -564,7 +586,7 @@ export class DocumentModel {
// add toData
toData(extraComps?: string[]) {
const node = this.export(TransformStage.Save);
const node = this.export(IPublicEnumTransformStage.Save);
const data = {
componentsMap: this.getComponentsMap(extraComps),
utils: this.getUtilsMap(),
@ -653,7 +675,7 @@ export class DocumentModel {
}
getComponentsMap(extraComps?: string[]) {
const componentsMap: ComponentsMap = [];
const componentsMap: IPublicTypeComponentsMap = [];
// 组件去重
const exsitingMap: { [componentName: string]: boolean } = {};
for (const node of this._nodesMap.values()) {
@ -735,7 +757,7 @@ export class DocumentModel {
}
onReady(fn: Function) {
this.designer.editor.on('document-open', fn);
this.designer.editor.eventBus.on('document-open', fn);
return () => {
this.designer.editor.removeListener('document-open', fn);
};
@ -750,6 +772,6 @@ export function isDocumentModel(obj: any): obj is DocumentModel {
return obj && obj.rootNode;
}
export function isPageSchema(obj: any): obj is PageSchema {
export function isPageSchema(obj: any): obj is IPublicTypePageSchema {
return obj?.componentName === 'Page';
}

View File

@ -1,20 +1,23 @@
import { EventEmitter } from 'events';
import { reaction, untracked, globalContext, Editor } from '@alilc/lowcode-editor-core';
import { NodeSchema } from '@alilc/lowcode-types';
import { reaction, untracked, globalContext, IEventBus, createModuleEventBus } from '@alilc/lowcode-editor-core';
import { IPublicTypeNodeSchema, IPublicModelHistory } from '@alilc/lowcode-types';
export interface Serialization<K = NodeSchema, T = string> {
export interface Serialization<K = IPublicTypeNodeSchema, T = string> {
serialize(data: K): T;
unserialize(data: T): K;
}
export class History<T = NodeSchema> {
export interface IHistory extends IPublicModelHistory {
}
export class History<T = IPublicTypeNodeSchema> implements IHistory {
private session: Session;
private records: Session[];
private point = 0;
private emitter = new EventEmitter();
private emitter: IEventBus = createModuleEventBus('History');
private asleep = false;
@ -118,11 +121,12 @@ export class History<T = NodeSchema> {
}
const cursor = this.session.cursor - 1;
this.go(cursor);
const editor = globalContext.get(Editor);
const workspace = globalContext.get('workspace');
const editor = workspace.isActive ? workspace.window.editor : globalContext.get('editor');
if (!editor) {
return;
}
editor.emit('history.back', cursor);
editor.eventBus.emit('history.back', cursor);
}
forward() {
@ -131,11 +135,12 @@ export class History<T = NodeSchema> {
}
const cursor = this.session.cursor + 1;
this.go(cursor);
const editor = globalContext.get(Editor);
const workspace = globalContext.get('workspace');
const editor = workspace.isActive ? workspace.window.editor : globalContext.get('editor');
if (!editor) {
return;
}
editor.emit('history.forward', cursor);
editor.eventBus.emit('history.forward', cursor);
}
savePoint() {
@ -169,6 +174,14 @@ export class History<T = NodeSchema> {
}
return state;
}
/**
* state
* @param func
* @returns
*/
onChangeState(func: () => any): () => void {
return this.onStateChange(func);
}
onStateChange(func: () => any) {
this.emitter.on('statechange', func);
@ -177,6 +190,14 @@ export class History<T = NodeSchema> {
};
}
/**
*
* @param func
* @returns
*/
onChangeCursor(func: () => any): () => void {
return this.onCursor(func);
}
onCursor(func: () => any) {
this.emitter.on('cursor', func);
return () => {

View File

@ -1,12 +1,13 @@
import { obx, computed, makeObservable } from '@alilc/lowcode-editor-core';
import { uniqueId } from '@alilc/lowcode-utils';
import { TitleContent } from '@alilc/lowcode-types';
import { IPublicTypeTitleContent, IPublicModelExclusiveGroup } from '@alilc/lowcode-types';
import { Node } from './node';
import { intl } from '../../locale';
// modals assoc x-hide value, initial: check is Modal, yes will put it in modals, cross levels
// if-else-if assoc conditionGroup value, should be the same level, and siblings, need renderEngine support
export class ExclusiveGroup {
// if-else-if assoc conditionGroup value, should be the same level,
// and siblings, need renderEngine support
export class ExclusiveGroup implements IPublicModelExclusiveGroup {
readonly isExclusiveGroup = true;
readonly id = uniqueId('exclusive');
@ -72,9 +73,9 @@ export class ExclusiveGroup {
return i === this.visibleIndex;
}
readonly title: TitleContent;
readonly title: IPublicTypeTitleContent;
constructor(readonly name: string, title?: TitleContent) {
constructor(readonly name: string, title?: IPublicTypeTitleContent) {
makeObservable(this);
this.title = title || {
type: 'i18n',

View File

@ -1,6 +1,7 @@
import { EventEmitter } from 'events';
import { Node } from './node';
import { DocumentModel } from '../document-model';
import { IPublicModelModalNodesManager } from '@alilc/lowcode-types';
import { createModuleEventBus, IEventBus } from '@alilc/lowcode-editor-core';
export function getModalNodes(node: Node) {
if (!node) return [];
@ -17,7 +18,11 @@ export function getModalNodes(node: Node) {
return nodes;
}
export class ModalNodesManager {
export interface IModalNodesManager extends IPublicModelModalNodesManager {
}
export class ModalNodesManager implements IModalNodesManager {
public willDestroy: any;
private page: DocumentModel;
@ -26,11 +31,11 @@ export class ModalNodesManager {
private nodeRemoveEvents: any;
private emitter: EventEmitter;
private emitter: IEventBus;
constructor(page: DocumentModel) {
this.page = page;
this.emitter = new EventEmitter();
this.emitter = createModuleEventBus('ModalNodesManager');
this.nodeRemoveEvents = {};
this.setNodes();
this.hideModalNodes();

View File

@ -1,21 +1,24 @@
import { obx, computed, globalContext, makeObservable } from '@alilc/lowcode-editor-core';
import { Node, ParentalNode } from './node';
import { TransformStage } from './transform-stage';
import { NodeData } from '@alilc/lowcode-types';
import { obx, computed, globalContext, makeObservable, IEventBus, createModuleEventBus } from '@alilc/lowcode-editor-core';
import { Node, INode } from './node';
import { IPublicTypeNodeData, IPublicModelNodeChildren, IPublicEnumTransformStage } from '@alilc/lowcode-types';
import { shallowEqual, compatStage, isNodeSchema } from '@alilc/lowcode-utils';
import { EventEmitter } from 'events';
import { foreachReverse } from '../../utils/tree';
import { NodeRemoveOptions } from '../../types';
export interface IOnChangeOptions {
type: string;
node: Node;
}
export class NodeChildren {
@obx.shallow private children: Node[];
private emitter = new EventEmitter();
export interface INodeChildren extends IPublicModelNodeChildren {
constructor(readonly owner: ParentalNode, data: NodeData | NodeData[], options: any = {}) {
}
export class NodeChildren implements INodeChildren {
@obx.shallow private children: INode[];
private emitter: IEventBus = createModuleEventBus('NodeChildren');
constructor(readonly owner: INode, data: IPublicTypeNodeData | IPublicTypeNodeData[], options: any = {}) {
makeObservable(this);
this.children = (Array.isArray(data) ? data : [data]).map((child) => {
return this.owner.document.createNode(child, options.checkId);
@ -29,19 +32,19 @@ export class NodeChildren {
/**
* schema
*/
export(stage: TransformStage = TransformStage.Save): NodeData[] {
export(stage: IPublicEnumTransformStage = IPublicEnumTransformStage.Save): IPublicTypeNodeData[] {
stage = compatStage(stage);
return this.children.map((node) => {
const data = node.export(stage);
if (node.isLeaf() && TransformStage.Save === stage) {
if (node.isLeaf() && IPublicEnumTransformStage.Save === stage) {
// FIXME: filter empty
return data.children as NodeData;
return data.children as IPublicTypeNodeData;
}
return data;
});
}
import(data?: NodeData | NodeData[], checkId = false) {
import(data?: IPublicTypeNodeData | IPublicTypeNodeData[], checkId = false) {
data = data ? (Array.isArray(data) ? data : [data]) : [];
const originChildren = this.children.slice();
@ -73,7 +76,7 @@ export class NodeChildren {
* @deprecated
* @param nodes
*/
concat(nodes: Node[]) {
concat(nodes: INode[]) {
return this.children.concat(nodes);
}
@ -114,8 +117,8 @@ export class NodeChildren {
});
}
unlinkChild(node: Node) {
const i = this.children.indexOf(node);
unlinkChild(node: INode) {
const i = this.children.map(d => d.id).indexOf(node.id);
if (i < 0) {
return false;
}
@ -129,7 +132,7 @@ export class NodeChildren {
/**
*
*/
delete(node: Node, purge = false, useMutator = true, options: NodeRemoveOptions = {}): boolean {
delete(node: INode, purge = false, useMutator = true, options: NodeRemoveOptions = {}): boolean {
node.internalPurgeStart();
if (node.isParental()) {
foreachReverse(
@ -147,8 +150,8 @@ export class NodeChildren {
(iterable, idx) => (iterable as [])[idx],
);
}
// 需要在从 children 中删除 node 前记录下 indexinternalSetParent 中会执行删除(unlink)操作
const i = this.children.indexOf(node);
// 需要在从 children 中删除 node 前记录下 indexinternalSetParent 中会执行删除 (unlink) 操作
const i = this.children.map(d => d.id).indexOf(node.id);
if (purge) {
// should set parent null
node.internalSetParent(null, useMutator);
@ -161,7 +164,9 @@ export class NodeChildren {
const { document } = node;
/* istanbul ignore next */
if (globalContext.has('editor')) {
globalContext.get('editor').emit('node.remove', { node, index: i });
const workspace = globalContext.get('workspace');
const editor = workspace.isActive ? workspace.window.editor : globalContext.get('editor');
editor.eventBus.emit('node.remove', { node, index: i });
}
document.unlinkNode(node);
document.selection.remove(node.id);
@ -189,19 +194,21 @@ export class NodeChildren {
/**
*
*/
insert(node: Node, at?: number | null, useMutator = true): void {
insert(node: INode, at?: number | null, useMutator = true): void {
const { children } = this;
let index = at == null || at === -1 ? children.length : at;
const i = children.indexOf(node);
const i = children.map(d => d.id).indexOf(node.id);
if (node.parent) {
/* istanbul ignore next */
globalContext.has('editor') &&
globalContext.get('editor').emit('node.remove.topLevel', {
if (globalContext.has('editor')) {
const workspace = globalContext.get('workspace');
const editor = workspace.isActive ? workspace.window.editor : globalContext.get('editor');
editor.eventBus.emit('node.remove.topLevel', {
node,
index: node.index,
});
}
}
if (i < 0) {
@ -231,7 +238,9 @@ export class NodeChildren {
this.emitter.emit('insert', node);
/* istanbul ignore next */
if (globalContext.has('editor')) {
globalContext.get('editor').emit('node.add', { node });
const workspace = globalContext.get('workspace');
const editor = workspace.isActive ? workspace.window.editor : globalContext.get('editor');
editor.eventBus.emit('node.add', { node });
}
if (useMutator) {
this.reportModified(node, this.owner, { type: 'insert' });
@ -263,14 +272,14 @@ export class NodeChildren {
/**
*
*/
indexOf(node: Node): number {
return this.children.indexOf(node);
indexOf(node: INode): number {
return this.children.map(d => d.id).indexOf(node.id);
}
/**
*
*/
splice(start: number, deleteCount: number, node?: Node): Node[] {
splice(start: number, deleteCount: number, node?: INode): INode[] {
if (node) {
return this.children.splice(start, deleteCount, node);
}
@ -280,21 +289,21 @@ export class NodeChildren {
/**
*
*/
get(index: number): Node | null {
get(index: number): INode | null {
return this.children.length > index ? this.children[index] : null;
}
/**
*
*/
has(node: Node) {
has(node: INode) {
return this.indexOf(node) > -1;
}
/**
*
*/
[Symbol.iterator](): { next(): { value: Node } } {
[Symbol.iterator](): { next(): { value: INode } } {
let index = 0;
const { children } = this;
const length = children.length || 0;
@ -317,7 +326,7 @@ export class NodeChildren {
/**
*
*/
forEach(fn: (item: Node, index: number) => void): void {
forEach(fn: (item: INode, index: number) => void): void {
this.children.forEach((child, index) => {
return fn(child, index);
});
@ -326,43 +335,43 @@ export class NodeChildren {
/**
*
*/
map<T>(fn: (item: Node, index: number) => T): T[] | null {
map<T>(fn: (item: INode, index: number) => T): T[] | null {
return this.children.map((child, index) => {
return fn(child, index);
});
}
every(fn: (item: Node, index: number) => any): boolean {
every(fn: (item: INode, index: number) => any): boolean {
return this.children.every((child, index) => fn(child, index));
}
some(fn: (item: Node, index: number) => any): boolean {
some(fn: (item: INode, index: number) => any): boolean {
return this.children.some((child, index) => fn(child, index));
}
filter(fn: (item: Node, index: number) => any) {
filter(fn: (item: INode, index: number) => any) {
return this.children.filter(fn);
}
find(fn: (item: Node, index: number) => boolean) {
find(fn: (item: INode, index: number) => boolean) {
return this.children.find(fn);
}
reduce(fn: (acc: any, cur: Node) => any, initialValue: any): void {
reduce(fn: (acc: any, cur: INode) => any, initialValue: any): void {
return this.children.reduce(fn, initialValue);
}
mergeChildren(
remover: (node: Node, idx: number) => boolean,
adder: (children: Node[]) => NodeData[] | null,
sorter: (firstNode: Node, secondNode: Node) => number,
remover: (node: INode, idx: number) => boolean,
adder: (children: INode[]) => IPublicTypeNodeData[] | null,
sorter: (firstNode: INode, secondNode: INode) => number,
): any {
let changed = false;
if (remover) {
const willRemove = this.children.filter(remover);
if (willRemove.length > 0) {
willRemove.forEach((node) => {
const i = this.children.indexOf(node);
const i = this.children.map(d => d.id).indexOf(node.id);
if (i > -1) {
this.children.splice(i, 1);
node.remove(false);
@ -374,7 +383,7 @@ export class NodeChildren {
if (adder) {
const items = adder(this.children);
if (items && items.length > 0) {
items.forEach((child: NodeData) => {
items.forEach((child: IPublicTypeNodeData) => {
const node = this.owner.document.createNode(child);
this.children.push(node);
node.internalSetParent(this.owner);
@ -398,7 +407,7 @@ export class NodeChildren {
};
}
onInsert(fn: (node: Node) => void) {
onInsert(fn: (node: INode) => void) {
this.emitter.on('insert', fn);
return () => {
this.emitter.removeListener('insert', fn);
@ -410,7 +419,7 @@ export class NodeChildren {
return 'Array';
}
private reportModified(node: Node, owner: Node, options = {}) {
private reportModified(node: INode, owner: INode, options = {}) {
if (!node) {
return;
}

View File

@ -1,20 +1,21 @@
import { ReactElement } from 'react';
import { EventEmitter } from 'events';
import { obx, computed, autorun, makeObservable, runInAction, wrapWithEventSwitch, action } from '@alilc/lowcode-editor-core';
import { obx, computed, autorun, makeObservable, runInAction, wrapWithEventSwitch, action, createModuleEventBus, IEventBus } from '@alilc/lowcode-editor-core';
import {
NodeSchema,
PropsMap,
PropsList,
NodeData,
I18nData,
IPublicTypeNodeSchema,
IPublicTypePropsMap,
IPublicTypePropsList,
IPublicTypeNodeData,
IPublicTypeI18nData,
SlotSchema,
PageSchema,
ComponentSchema,
IPublicTypePageSchema,
IPublicTypeComponentSchema,
NodeStatus,
CompositeValue,
IPublicTypeCompositeValue,
GlobalEvent,
ComponentAction,
IPublicTypeComponentAction,
IPublicModelNode,
IPublicModelExclusiveGroup,
IPublicEnumTransformStage,
} from '@alilc/lowcode-types';
import { compatStage, isDOMText, isJSExpression } from '@alilc/lowcode-utils';
import { SettingTopEntry } from '@alilc/lowcode-designer';
@ -24,11 +25,15 @@ import { NodeChildren } from './node-children';
import { Prop } from './props/prop';
import { ComponentMeta } from '../../component-meta';
import { ExclusiveGroup, isExclusiveGroup } from './exclusive-group';
import { TransformStage } from './transform-stage';
import { includeSlot, removeSlot } from '../../utils/slot';
import { foreachReverse } from '../../utils/tree';
import { NodeRemoveOptions } from '../../types';
export interface INode extends IPublicModelNode {
}
/**
*
*
@ -77,8 +82,8 @@ import { NodeRemoveOptions } from '../../types';
* isLocked
* hidden
*/
export class Node<Schema extends NodeSchema = NodeSchema> {
private emitter: EventEmitter;
export class Node<Schema extends IPublicTypeNodeSchema = IPublicTypeNodeSchema> implements INode {
private emitter: IEventBus;
/**
*
@ -114,12 +119,12 @@ export class Node<Schema extends NodeSchema = NodeSchema> {
*/
private _addons: { [key: string]: { exportData: () => any; isProp: boolean } } = {};
@obx.ref private _parent: ParentalNode | null = null;
@obx.ref private _parent: INode | null = null;
/**
*
*/
get parent(): ParentalNode | null {
get parent(): INode | null {
return this._parent;
}
@ -140,7 +145,7 @@ export class Node<Schema extends NodeSchema = NodeSchema> {
return 0;
}
@computed get title(): string | I18nData | ReactElement {
@computed get title(): string | IPublicTypeI18nData | ReactElement {
let t = this.getExtraProp('title');
// TODO: 暂时走不到这个分支
// if (!t && this.componentMeta.descriptor) {
@ -172,7 +177,7 @@ export class Node<Schema extends NodeSchema = NodeSchema> {
});
} else {
this.props = new Props(this, props, extras);
this._children = new NodeChildren(this as ParentalNode, this.initialChildren(children));
this._children = new NodeChildren(this as INode, this.initialChildren(children));
this._children.internalInitParent();
this.props.merge(
this.upgradeProps(this.initProps(props || {})),
@ -184,7 +189,7 @@ export class Node<Schema extends NodeSchema = NodeSchema> {
this.initBuiltinProps();
this.isInited = true;
this.emitter = new EventEmitter();
this.emitter = createModuleEventBus('Node');
}
_settingEntry: SettingTopEntry;
@ -210,12 +215,12 @@ export class Node<Schema extends NodeSchema = NodeSchema> {
@action
private initProps(props: any): any {
return this.document.designer.transformProps(props, this, TransformStage.Init);
return this.document.designer.transformProps(props, this, IPublicEnumTransformStage.Init);
}
@action
private upgradeProps(props: any): any {
return this.document.designer.transformProps(props, this, TransformStage.Upgrade);
return this.document.designer.transformProps(props, this, IPublicEnumTransformStage.Upgrade);
}
private autoruns?: Array<() => void>;
@ -232,7 +237,7 @@ export class Node<Schema extends NodeSchema = NodeSchema> {
});
}
private initialChildren(children: any): NodeData[] {
private initialChildren(children: any): IPublicTypeNodeData[] {
// FIXME! this is dirty code
if (children == null) {
const initialChildren = this.componentMeta.getMetadata().configure.advanced?.initialChildren;
@ -283,7 +288,7 @@ export class Node<Schema extends NodeSchema = NodeSchema> {
/**
*
*/
isParental(): this is ParentalNode {
isParental(): boolean {
return !this.isLeaf();
}
@ -325,7 +330,7 @@ export class Node<Schema extends NodeSchema = NodeSchema> {
* 使
* @param useMutator
*/
internalSetParent(parent: ParentalNode | null, useMutator = false) {
internalSetParent(parent: INode | null, useMutator = false) {
if (this._parent === parent) {
return;
}
@ -387,7 +392,7 @@ export class Node<Schema extends NodeSchema = NodeSchema> {
) {
if (this.parent) {
if (!options.suppressRemoveEvent) {
this.document.designer.editor?.emit('node.remove.topLevel', {
this.document.designer.editor?.eventBus.emit('node.remove.topLevel', {
node: this,
index: this.parent?.children?.indexOf(this),
});
@ -440,11 +445,11 @@ export class Node<Schema extends NodeSchema = NodeSchema> {
return this.document.getComponentMeta(this.componentName);
}
@computed get propsData(): PropsMap | PropsList | null {
@computed get propsData(): IPublicTypePropsMap | IPublicTypePropsList | null {
if (!this.isParental() || this.componentName === 'Fragment') {
return null;
}
return this.props.export(TransformStage.Serilize).props || null;
return this.props.export(IPublicEnumTransformStage.Serilize).props || null;
}
@obx.shallow _slots: Node[] = [];
@ -458,15 +463,15 @@ export class Node<Schema extends NodeSchema = NodeSchema> {
}
/* istanbul ignore next */
@obx.ref private _conditionGroup: ExclusiveGroup | null = null;
@obx.ref private _conditionGroup: IPublicModelExclusiveGroup | null = null;
/* istanbul ignore next */
get conditionGroup(): ExclusiveGroup | null {
get conditionGroup(): IPublicModelExclusiveGroup | null {
return this._conditionGroup;
}
/* istanbul ignore next */
setConditionGroup(grp: ExclusiveGroup | string | null) {
setConditionGroup(grp: IPublicModelExclusiveGroup | string | null) {
if (!grp) {
this.getExtraProp('conditionGroup', false)?.remove();
if (this._conditionGroup) {
@ -589,7 +594,7 @@ export class Node<Schema extends NodeSchema = NodeSchema> {
return this.props.get(getConvertedExtraKey(key), createIfNone) || null;
}
setExtraProp(key: string, value: CompositeValue) {
setExtraProp(key: string, value: IPublicTypeCompositeValue) {
this.getProp(getConvertedExtraKey(key), true)?.setValue(value);
}
@ -617,14 +622,14 @@ export class Node<Schema extends NodeSchema = NodeSchema> {
/**
*
*/
mergeProps(props: PropsMap) {
mergeProps(props: IPublicTypePropsMap) {
this.props.merge(props);
}
/**
*
*/
setProps(props?: PropsMap | PropsList | Props | null) {
setProps(props?: IPublicTypePropsMap | IPublicTypePropsList | Props | null) {
if (props instanceof Props) {
this.props = props;
return;
@ -674,7 +679,7 @@ export class Node<Schema extends NodeSchema = NodeSchema> {
* - schema
*/
get schema(): Schema {
return this.export(TransformStage.Save);
return this.export(IPublicEnumTransformStage.Save);
}
set schema(data: Schema) {
@ -709,16 +714,16 @@ export class Node<Schema extends NodeSchema = NodeSchema> {
/**
* schema
*/
export(stage: TransformStage = TransformStage.Save, options: any = {}): Schema {
export(stage: IPublicEnumTransformStage = IPublicEnumTransformStage.Save, options: any = {}): Schema {
stage = compatStage(stage);
const baseSchema: any = {
componentName: this.componentName,
};
if (stage !== TransformStage.Clone) {
if (stage !== IPublicEnumTransformStage.Clone) {
baseSchema.id = this.id;
}
if (stage === TransformStage.Render) {
if (stage === IPublicEnumTransformStage.Render) {
baseSchema.docId = this.document.id;
}
@ -817,7 +822,7 @@ export class Node<Schema extends NodeSchema = NodeSchema> {
if (includeSlot(this, slotName)) {
removeSlot(this, slotName);
}
slotNode.internalSetParent(this as ParentalNode, true);
slotNode.internalSetParent(this as INode, true);
this._slots.push(slotNode);
}
@ -880,13 +885,13 @@ export class Node<Schema extends NodeSchema = NodeSchema> {
*/
canPerformAction(actionName: string): boolean {
const availableActions =
this.componentMeta?.availableActions?.filter((action: ComponentAction) => {
this.componentMeta?.availableActions?.filter((action: IPublicTypeComponentAction) => {
const { condition } = action;
return typeof condition === 'function' ?
condition(this) !== false :
condition !== false;
})
.map((action: ComponentAction) => action.name) || [];
.map((action: IPublicTypeComponentAction) => action.name) || [];
return availableActions.indexOf(actionName) >= 0;
}
@ -949,7 +954,7 @@ export class Node<Schema extends NodeSchema = NodeSchema> {
mergeChildren(
remover: () => any,
adder: (children: Node[]) => NodeData[] | null,
adder: (children: Node[]) => IPublicTypeNodeData[] | null,
sorter: () => any,
) {
this.children?.mergeChildren(remover, adder, sorter);
@ -1018,7 +1023,7 @@ export class Node<Schema extends NodeSchema = NodeSchema> {
}
/**
* @deprecated
* TODO: replace non standard metas with standard ones.
*/
getSuitablePlace(node: Node, ref: any): any {
const focusNode = this.document?.focusNode;
@ -1137,11 +1142,11 @@ export class Node<Schema extends NodeSchema = NodeSchema> {
return this.id;
}
emitPropChange(val: PropChangeOptions) {
emitPropChange(val: IPublicTypePropChangeOptions) {
this.emitter?.emit('propChange', val);
}
onPropChange(func: (info: PropChangeOptions) => void): Function {
onPropChange(func: (info: IPublicTypePropChangeOptions) => void): Function {
const wrappedFunc = wrapWithEventSwitch(func);
this.emitter.on('propChange', wrappedFunc);
return () => {
@ -1164,20 +1169,21 @@ function ensureNode(node: any, document: DocumentModel): Node {
return nodeInstance;
}
export interface ParentalNode<T extends NodeSchema = NodeSchema> extends Node<T> {
readonly children: NodeChildren;
}
export interface LeafNode extends Node {
readonly children: null;
}
export type PropChangeOptions = Omit<GlobalEvent.Node.Prop.ChangeOptions, 'node'>;
export type IPublicTypePropChangeOptions = Omit<GlobalEvent.Node.Prop.ChangeOptions, 'node'>;
export type SlotNode = ParentalNode<SlotSchema>;
export type PageNode = ParentalNode<PageSchema>;
export type ComponentNode = ParentalNode<ComponentSchema>;
export type SlotNode = Node<SlotSchema>;
export type PageNode = Node<IPublicTypePageSchema>;
export type ComponentNode = Node<IPublicTypeComponentSchema>;
export type RootNode = PageNode | ComponentNode;
/**
* @deprecated use same function from '@alilc/lowcode-utils' instead
*/
export function isNode(node: any): node is Node {
return node && node.isNode;
}
@ -1266,14 +1272,14 @@ export function comparePosition(node1: Node, node2: Node): PositionNO {
}
export function insertChild(
container: ParentalNode,
thing: Node | NodeData,
container: INode,
thing: Node | IPublicTypeNodeData,
at?: number | null,
copy?: boolean,
): Node {
let node: Node;
if (isNode(thing) && (copy || thing.isSlot())) {
thing = thing.export(TransformStage.Clone);
thing = thing.export(IPublicEnumTransformStage.Clone);
}
if (isNode(thing)) {
node = thing;
@ -1287,8 +1293,8 @@ export function insertChild(
}
export function insertChildren(
container: ParentalNode,
nodes: Node[] | NodeData[],
container: INode,
nodes: Node[] | IPublicTypeNodeData[],
at?: number | null,
copy?: boolean,
): Node[] {

View File

@ -1,10 +1,10 @@
import { untracked, computed, obx, engineConfig, action, makeObservable, mobx, runInAction } from '@alilc/lowcode-editor-core';
import { CompositeValue, GlobalEvent, JSSlot, SlotSchema } from '@alilc/lowcode-types';
import { IPublicTypeCompositeValue, GlobalEvent, IPublicTypeJSSlot, SlotSchema, IPublicEnumTransformStage } from '@alilc/lowcode-types';
import { uniqueId, isPlainObject, hasOwnProperty, compatStage, isJSExpression, isJSSlot } from '@alilc/lowcode-utils';
import { valueToSource } from './value-to-source';
import { Props } from './props';
import { SlotNode, Node } from '../node';
import { TransformStage } from '../transform-stage';
// import { TransformStage } from '../transform-stage';
const { set: mobxSet, isObservableArray } = mobx;
export const UNSET = Symbol.for('unset');
@ -41,7 +41,7 @@ export class Prop implements IPropParent {
constructor(
public parent: IPropParent,
value: CompositeValue | UNSET = UNSET,
value: IPublicTypeCompositeValue | UNSET = UNSET,
key?: string | number,
spread = false,
options = {},
@ -104,14 +104,14 @@ export class Prop implements IPropParent {
/**
*
*/
@computed get value(): CompositeValue | UNSET {
return this.export(TransformStage.Serilize);
@computed get value(): IPublicTypeCompositeValue | UNSET {
return this.export(IPublicEnumTransformStage.Serilize);
}
export(stage: TransformStage = TransformStage.Save): CompositeValue {
export(stage: IPublicEnumTransformStage = IPublicEnumTransformStage.Save): IPublicTypeCompositeValue {
stage = compatStage(stage);
const type = this._type;
if (stage === TransformStage.Render && this.key === '___condition___') {
if (stage === IPublicEnumTransformStage.Render && this.key === '___condition___') {
// 在设计器里,所有组件默认需要展示,除非开启了 enableCondition 配置
if (engineConfig?.get('enableCondition') !== true) {
return true;
@ -125,7 +125,7 @@ export class Prop implements IPropParent {
if (type === 'literal' || type === 'expression') {
// TODO 后端改造之后删除此逻辑
if (this._value === null && stage === TransformStage.Save) {
if (this._value === null && stage === IPublicEnumTransformStage.Save) {
return '';
}
return this._value;
@ -133,7 +133,7 @@ export class Prop implements IPropParent {
if (type === 'slot') {
const schema = this._slotNode?.export(stage) || {} as any;
if (stage === TransformStage.Render) {
if (stage === IPublicEnumTransformStage.Render) {
return {
type: 'JSSlot',
params: schema.params,
@ -191,7 +191,7 @@ export class Prop implements IPropParent {
}
// todo: JSFunction ...
if (this.type === 'slot') {
return JSON.stringify(this._slotNode!.export(TransformStage.Save));
return JSON.stringify(this._slotNode!.export(IPublicEnumTransformStage.Save));
}
return this._code != null ? this._code : JSON.stringify(this.value);
}
@ -237,7 +237,7 @@ export class Prop implements IPropParent {
* set value, val should be JSON Object
*/
@action
setValue(val: CompositeValue) {
setValue(val: IPublicTypeCompositeValue) {
if (val === this._value) return;
const editor = this.owner.document?.designer.editor;
const oldValue = this._value;
@ -277,7 +277,7 @@ export class Prop implements IPropParent {
newValue: this._value,
};
editor?.emit(GlobalEvent.Node.Prop.InnerChange, {
editor?.eventBus.emit(GlobalEvent.Node.Prop.InnerChange, {
node: this.owner as any,
...propsInfo,
});
@ -286,8 +286,8 @@ export class Prop implements IPropParent {
}
}
getValue(): CompositeValue {
return this.export(TransformStage.Serilize);
getValue(): IPublicTypeCompositeValue {
return this.export(IPublicEnumTransformStage.Serilize);
}
@action
@ -312,7 +312,7 @@ export class Prop implements IPropParent {
}
@action
setAsSlot(data: JSSlot) {
setAsSlot(data: IPublicTypeJSSlot) {
this._type = 'slot';
let slotSchema: SlotSchema;
// 当 data.value 的结构为 { componentName: 'Slot' } 时,复用部分 slotSchema 数据
@ -557,7 +557,7 @@ export class Prop implements IPropParent {
* @param force
*/
@action
add(value: CompositeValue, force = false): Prop | null {
add(value: IPublicTypeCompositeValue, force = false): Prop | null {
const type = this._type;
if (type !== 'list' && type !== 'unset' && !force) {
return null;
@ -577,7 +577,7 @@ export class Prop implements IPropParent {
* @param force
*/
@action
set(key: string | number, value: CompositeValue | Prop, force = false) {
set(key: string | number, value: IPublicTypeCompositeValue | Prop, force = false) {
const type = this._type;
if (type !== 'map' && type !== 'list' && type !== 'unset' && !force) {
return null;

View File

@ -1,9 +1,9 @@
import { computed, makeObservable, obx, action } from '@alilc/lowcode-editor-core';
import { PropsMap, PropsList, CompositeValue } from '@alilc/lowcode-types';
import { IPublicTypePropsMap, IPublicTypePropsList, IPublicTypeCompositeValue, IPublicEnumTransformStage } from '@alilc/lowcode-types';
import { uniqueId, compatStage } from '@alilc/lowcode-utils';
import { Prop, IPropParent, UNSET } from './prop';
import { Node } from '../node';
import { TransformStage } from '../transform-stage';
// import { TransformStage } from '../transform-stage';
interface ExtrasObject {
[key: string]: any;
@ -57,7 +57,7 @@ export class Props implements IPropParent {
@obx type: 'map' | 'list' = 'map';
constructor(owner: Node, value?: PropsMap | PropsList | null, extras?: ExtrasObject) {
constructor(owner: Node, value?: IPublicTypePropsMap | IPublicTypePropsList | null, extras?: ExtrasObject) {
makeObservable(this);
this.owner = owner;
if (Array.isArray(value)) {
@ -76,7 +76,7 @@ export class Props implements IPropParent {
}
@action
import(value?: PropsMap | PropsList | null, extras?: ExtrasObject) {
import(value?: IPublicTypePropsMap | IPublicTypePropsList | null, extras?: ExtrasObject) {
const originItems = this.items;
if (Array.isArray(value)) {
this.type = 'list';
@ -99,7 +99,7 @@ export class Props implements IPropParent {
}
@action
merge(value: PropsMap, extras?: PropsMap) {
merge(value: IPublicTypePropsMap, extras?: IPublicTypePropsMap) {
Object.keys(value).forEach((key) => {
this.query(key, true)!.setValue(value[key]);
this.query(key, true)!.setupItems();
@ -112,8 +112,8 @@ export class Props implements IPropParent {
}
}
export(stage: TransformStage = TransformStage.Save): {
props?: PropsMap | PropsList;
export(stage: IPublicEnumTransformStage = IPublicEnumTransformStage.Save): {
props?: IPublicTypePropsMap | IPublicTypePropsList;
extras?: ExtrasObject;
} {
stage = compatStage(stage);
@ -256,7 +256,7 @@ export class Props implements IPropParent {
*/
@action
add(
value: CompositeValue | null,
value: IPublicTypeCompositeValue | null,
key?: string | number,
spread = false,
options: any = {},

View File

@ -212,6 +212,8 @@ export function valueToSource(
}
case 'undefined':
return `${indentString.repeat(indentLevel)}undefined`;
default:
return `${indentString.repeat(indentLevel)}undefined`;
}
}

View File

@ -1,10 +1,14 @@
import { EventEmitter } from 'events';
import { obx, makeObservable } from '@alilc/lowcode-editor-core';
import { obx, makeObservable, IEventBus, createModuleEventBus } from '@alilc/lowcode-editor-core';
import { Node, comparePosition, PositionNO } from './node/node';
import { DocumentModel } from './document-model';
import { IPublicModelSelection } from '@alilc/lowcode-types';
export class Selection {
private emitter = new EventEmitter();
export interface ISelection extends IPublicModelSelection {
}
export class Selection implements ISelection {
private emitter: IEventBus = createModuleEventBus('Selection');
@obx.shallow private _selected: string[] = [];
@ -129,7 +133,7 @@ export class Selection {
}
/**
* ,
*
*/
getTopNodes(includeRoot = false) {
const nodes = [];

View File

@ -7,4 +7,3 @@ export * from './clone';
export * from './page';
export * from './container';
export * from './unlock';

View File

@ -1,6 +1,5 @@
/* eslint-disable no-multi-assign */
import { EngineConfig, engineConfig } from '@alilc/lowcode-editor-core';
import { ILowCodePluginManager } from '@alilc/lowcode-designer';
import { engineConfig, createModuleEventBus } from '@alilc/lowcode-editor-core';
import {
IPublicApiHotkey,
IPublicApiProject,
@ -9,43 +8,45 @@ import {
IPublicApiMaterial,
IPublicApiEvent,
IPublicApiCommon,
} from '@alilc/lowcode-types';
import { getLogger, Logger } from '@alilc/lowcode-utils';
import {
ILowCodePluginContext,
IPluginContextOptions,
ILowCodePluginPreferenceDeclaration,
PreferenceValueType,
IPublicModelPluginContext,
IPluginPreferenceMananger,
IPublicTypePreferenceValueType,
IPublicModelEngineConfig,
IPublicApiLogger,
IPublicApiPlugins,
IPublicTypePluginDeclaration,
IPublicApiCanvas,
} from '@alilc/lowcode-types';
import {
IPluginContextOptions,
ILowCodePluginContextApiAssembler,
ILowCodePluginContextPrivate,
} from './plugin-types';
import { isValidPreferenceKey } from './plugin-utils';
export default class PluginContext implements ILowCodePluginContext, ILowCodePluginContextPrivate {
export default class PluginContext implements IPublicModelPluginContext, ILowCodePluginContextPrivate {
hotkey: IPublicApiHotkey;
project: IPublicApiProject;
skeleton: IPublicApiSkeleton;
setters: IPublicApiSetters;
material: IPublicApiMaterial;
event: IPublicApiEvent;
config: EngineConfig;
config: IPublicModelEngineConfig;
common: IPublicApiCommon;
logger: Logger;
plugins: ILowCodePluginManager;
logger: IPublicApiLogger;
plugins: IPublicApiPlugins;
preference: IPluginPreferenceMananger;
pluginEvent: IPublicApiEvent;
canvas: IPublicApiCanvas;
constructor(
plugins: ILowCodePluginManager,
options: IPluginContextOptions,
contextApiAssembler: ILowCodePluginContextApiAssembler,
) {
contextApiAssembler.assembleApis(this);
this.plugins = plugins;
const { pluginName = 'anonymous' } = options;
this.logger = getLogger({ level: 'warn', bizName: `designer:plugin:${pluginName}` });
const { pluginName = 'anonymous', meta = {} } = options;
contextApiAssembler.assembleApis(this, pluginName, meta);
this.pluginEvent = createModuleEventBus(pluginName, 200);
const enhancePluginContextHook = engineConfig.get('enhancePluginContextHook');
if (enhancePluginContextHook) {
enhancePluginContextHook(this);
@ -54,12 +55,12 @@ export default class PluginContext implements ILowCodePluginContext, ILowCodePlu
setPreference(
pluginName: string,
preferenceDeclaration: ILowCodePluginPreferenceDeclaration,
preferenceDeclaration: IPublicTypePluginDeclaration,
): void {
const getPreferenceValue = (
key: string,
defaultValue?: PreferenceValueType,
): PreferenceValueType | undefined => {
defaultValue?: IPublicTypePreferenceValueType,
): IPublicTypePreferenceValueType | undefined => {
if (!isValidPreferenceKey(key, preferenceDeclaration)) {
return undefined;
}

View File

@ -1,45 +1,53 @@
import { engineConfig } from '@alilc/lowcode-editor-core';
import { getLogger } from '@alilc/lowcode-utils';
import {
ILowCodePlugin,
ILowCodePluginConfig,
ILowCodePluginRuntime,
ILowCodePluginManager,
ILowCodePluginContext,
ILowCodeRegisterOptions,
IPluginContextOptions,
PreferenceValueType,
ILowCodePluginConfigMeta,
PluginPreference,
ILowCodePluginPreferenceDeclaration,
isLowCodeRegisterOptions,
ILowCodePluginContextApiAssembler,
} from './plugin-types';
import { filterValidOptions } from './plugin-utils';
import { LowCodePlugin } from './plugin';
import { filterValidOptions, isLowCodeRegisterOptions } from './plugin-utils';
import { LowCodePluginRuntime } from './plugin';
// eslint-disable-next-line import/no-named-as-default
import LowCodePluginContext from './plugin-context';
import { invariant } from '../utils';
import sequencify from './sequencify';
import semverSatisfies from 'semver/functions/satisfies';
import {
ILowCodeRegisterOptions,
IPublicTypePreferenceValueType,
IPublicTypePlugin,
} from '@alilc/lowcode-types';
const logger = getLogger({ level: 'warn', bizName: 'designer:pluginManager' });
export class LowCodePluginManager implements ILowCodePluginManager {
private plugins: ILowCodePlugin[] = [];
// 保留的事件前缀
const RESERVED_EVENT_PREFIX = ['designer', 'editor', 'skeleton', 'renderer', 'render', 'utils', 'plugin', 'engine', 'editor-core', 'engine-core', 'plugins', 'event', 'events', 'log', 'logger', 'ctx', 'context'];
private pluginsMap: Map<string, ILowCodePlugin> = new Map();
export class LowCodePluginManager implements ILowCodePluginManager {
private plugins: ILowCodePluginRuntime[] = [];
pluginsMap: Map<string, ILowCodePluginRuntime> = new Map();
pluginContextMap: Map<string, LowCodePluginContext> = new Map();
private pluginPreference?: PluginPreference = new Map();
contextApiAssembler: ILowCodePluginContextApiAssembler;
constructor(contextApiAssembler: ILowCodePluginContextApiAssembler) {
constructor(contextApiAssembler: ILowCodePluginContextApiAssembler, readonly viewName = 'global') {
this.contextApiAssembler = contextApiAssembler;
}
private _getLowCodePluginContext(options: IPluginContextOptions) {
return new LowCodePluginContext(this, options, this.contextApiAssembler);
}
_getLowCodePluginContext = (options: IPluginContextOptions) => {
const { pluginName } = options;
let context = this.pluginContextMap.get(pluginName);
if (!context) {
context = new LowCodePluginContext(options, this.contextApiAssembler);
this.pluginContextMap.set(pluginName, context);
}
return context;
};
isEngineVersionMatched(versionExp: string): boolean {
const engineVersion = engineConfig.get('ENGINE_VERSION');
@ -55,7 +63,7 @@ export class LowCodePluginManager implements ILowCodePluginManager {
* @param registerOptions - the plugin register options
*/
async register(
pluginConfigCreator: (ctx: ILowCodePluginContext, options: any) => ILowCodePluginConfig,
pluginModel: IPublicTypePlugin,
options?: any,
registerOptions?: ILowCodeRegisterOptions,
): Promise<void> {
@ -64,11 +72,18 @@ export class LowCodePluginManager implements ILowCodePluginManager {
registerOptions = options;
options = {};
}
let { pluginName, meta = {} } = pluginConfigCreator as any;
const { preferenceDeclaration, engines } = meta as ILowCodePluginConfigMeta;
const ctx = this._getLowCodePluginContext({ pluginName });
let { pluginName, meta = {} } = pluginModel;
const { preferenceDeclaration, engines } = meta;
// filter invalid eventPrefix
const { eventPrefix } = meta;
const isReservedPrefix = RESERVED_EVENT_PREFIX.find((item) => item === eventPrefix);
if (isReservedPrefix) {
meta.eventPrefix = undefined;
logger.warn(`plugin ${pluginName} is trying to use ${eventPrefix} as event prefix, which is a reserved event prefix, please use another one`);
}
const ctx = this._getLowCodePluginContext({ pluginName, meta });
const customFilterValidOptions = engineConfig.get('customPluginFilterOptions', filterValidOptions);
const config = pluginConfigCreator(ctx, customFilterValidOptions(options, preferenceDeclaration!));
const config = pluginModel(ctx, customFilterValidOptions(options, preferenceDeclaration!));
// compat the legacy way to declare pluginName
// @ts-ignore
pluginName = pluginName || config.name;
@ -78,7 +93,7 @@ export class LowCodePluginManager implements ILowCodePluginManager {
config,
);
ctx.setPreference(pluginName, (preferenceDeclaration as ILowCodePluginPreferenceDeclaration));
ctx.setPreference(pluginName, preferenceDeclaration);
const allowOverride = registerOptions?.override === true;
@ -104,21 +119,22 @@ export class LowCodePluginManager implements ILowCodePluginManager {
throw new Error(`plugin ${pluginName} skipped, engine check failed, current engine version is ${engineConfig.get('ENGINE_VERSION')}, meta.engines.lowcodeEngine is ${engineVersionExp}`);
}
const plugin = new LowCodePlugin(pluginName, this, config, meta);
// support initialization of those plugins which registered after normal initialization by plugin-manager
const plugin = new LowCodePluginRuntime(pluginName, this, config, meta);
// support initialization of those plugins which registered
// after normal initialization by plugin-manager
if (registerOptions?.autoInit) {
await plugin.init();
}
this.plugins.push(plugin);
this.pluginsMap.set(pluginName, plugin);
logger.log(`plugin registered with pluginName: ${pluginName}, config: ${config}, meta: ${meta}`);
logger.log(`plugin registered with pluginName: ${pluginName}, config: `, config, 'meta:', meta);
}
get(pluginName: string): ILowCodePlugin | undefined {
get(pluginName: string): ILowCodePluginRuntime | undefined {
return this.pluginsMap.get(pluginName);
}
getAll(): ILowCodePlugin[] {
getAll(): ILowCodePluginRuntime[] {
return this.plugins;
}
@ -138,7 +154,7 @@ export class LowCodePluginManager implements ILowCodePluginManager {
async init(pluginPreference?: PluginPreference) {
const pluginNames: string[] = [];
const pluginObj: { [name: string]: ILowCodePlugin } = {};
const pluginObj: { [name: string]: ILowCodePluginRuntime } = {};
this.pluginPreference = pluginPreference;
this.plugins.forEach((plugin) => {
pluginNames.push(plugin.name);
@ -170,7 +186,7 @@ export class LowCodePluginManager implements ILowCodePluginManager {
return this.pluginsMap.size;
}
getPluginPreference(pluginName: string): Record<string, PreferenceValueType> | null | undefined {
getPluginPreference(pluginName: string): Record<string, IPublicTypePreferenceValueType> | null | undefined {
if (!this.pluginPreference) {
return null;
}

Some files were not shown because too many files have changed in this diff Show More