{
diff --git a/packages/rax-renderer/README.md b/packages/rax-renderer/README.md
deleted file mode 100644
index 7b430de630..0000000000
--- a/packages/rax-renderer/README.md
+++ /dev/null
@@ -1,49 +0,0 @@
-# Rax Renderer
-
-Rax 渲染模块。
-
-## 安装
-
-```
-$ npm install @alilc/lowcode-rax-renderer --save
-```
-
-## 使用
-
-```js
-import { createElement, render } from 'rax';
-import DriverUniversal from 'driver-universal';
-import RaxRenderer from '@ali/lowcode-rax-renderer';
-
-const components = {
- View,
- Text
-};
-
-const schema = {
- componentName: 'Page',
- fileName: 'home',
- children: [
- {
- componentName: 'View',
- children: [
- {
- componentName: 'Text',
- props: {
- type: 'primary'
- },
- children: ['Welcome to Your Rax App']
- }
- ]
- }
- ]
-};
-
-render(
-
,
- document.getElementById('root'), { driver: DriverUniversal }
-);
-```
diff --git a/packages/rax-renderer/build.json b/packages/rax-renderer/build.json
deleted file mode 100644
index 3edf143801..0000000000
--- a/packages/rax-renderer/build.json
+++ /dev/null
@@ -1,11 +0,0 @@
-{
- "plugins": [
- [
- "build-plugin-rax-component",
- {
- "type": "rax",
- "targets": ["web"]
- }
- ]
- ]
-}
diff --git a/packages/rax-renderer/demo/index.jsx b/packages/rax-renderer/demo/index.jsx
deleted file mode 100644
index cfcae2a201..0000000000
--- a/packages/rax-renderer/demo/index.jsx
+++ /dev/null
@@ -1,35 +0,0 @@
-import { createElement, render } from 'rax';
-import DriverUniversal from 'driver-universal';
-import View from 'rax-view';
-import Text from 'rax-text';
-import { Engine } from '../src/index';
-
-const components = {
- View,
- Text,
-};
-
-const schema = {
- componentName: 'Page',
- fileName: 'home',
- props: {},
- children: [
- {
- componentName: 'View',
- props: {},
- children: [
- {
- componentName: 'Text',
- props: {
- type: 'primary',
- },
- children: ['Welcome to Your Rax App!'],
- },
- ],
- },
- ],
-};
-
-render(
, document.getElementById('root'), {
- driver: DriverUniversal,
-});
diff --git a/packages/rax-renderer/demo/miniapp/app.js b/packages/rax-renderer/demo/miniapp/app.js
deleted file mode 100644
index 3482935519..0000000000
--- a/packages/rax-renderer/demo/miniapp/app.js
+++ /dev/null
@@ -1 +0,0 @@
-App({});
diff --git a/packages/rax-renderer/demo/miniapp/app.json b/packages/rax-renderer/demo/miniapp/app.json
deleted file mode 100644
index 94127c774c..0000000000
--- a/packages/rax-renderer/demo/miniapp/app.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "pages": ["pages/index"],
- "window": {
- "defaultTitle": "demo"
- }
-}
diff --git a/packages/rax-renderer/demo/miniapp/pages/index.acss b/packages/rax-renderer/demo/miniapp/pages/index.acss
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/packages/rax-renderer/demo/miniapp/pages/index.axml b/packages/rax-renderer/demo/miniapp/pages/index.axml
deleted file mode 100644
index 41b536b4ce..0000000000
--- a/packages/rax-renderer/demo/miniapp/pages/index.axml
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/packages/rax-renderer/demo/miniapp/pages/index.js b/packages/rax-renderer/demo/miniapp/pages/index.js
deleted file mode 100644
index 687d87e197..0000000000
--- a/packages/rax-renderer/demo/miniapp/pages/index.js
+++ /dev/null
@@ -1,4 +0,0 @@
-Page({
- onLoad() {},
- onShow() {},
-});
diff --git a/packages/rax-renderer/demo/miniapp/pages/index.json b/packages/rax-renderer/demo/miniapp/pages/index.json
deleted file mode 100644
index 89b15c54ca..0000000000
--- a/packages/rax-renderer/demo/miniapp/pages/index.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "defaultTitle": "Miniapp Rax Text demo",
- "usingComponents": {
- "my-component": "../components/Target/index"
- }
-}
diff --git a/packages/rax-renderer/demo/wechat-miniprogram/app.js b/packages/rax-renderer/demo/wechat-miniprogram/app.js
deleted file mode 100644
index 3482935519..0000000000
--- a/packages/rax-renderer/demo/wechat-miniprogram/app.js
+++ /dev/null
@@ -1 +0,0 @@
-App({});
diff --git a/packages/rax-renderer/demo/wechat-miniprogram/app.json b/packages/rax-renderer/demo/wechat-miniprogram/app.json
deleted file mode 100644
index be00ced601..0000000000
--- a/packages/rax-renderer/demo/wechat-miniprogram/app.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "pages": ["pages/index"],
- "window": {
- "title": "demo"
- }
-}
diff --git a/packages/rax-renderer/demo/wechat-miniprogram/pages/index.js b/packages/rax-renderer/demo/wechat-miniprogram/pages/index.js
deleted file mode 100644
index 687d87e197..0000000000
--- a/packages/rax-renderer/demo/wechat-miniprogram/pages/index.js
+++ /dev/null
@@ -1,4 +0,0 @@
-Page({
- onLoad() {},
- onShow() {},
-});
diff --git a/packages/rax-renderer/demo/wechat-miniprogram/pages/index.json b/packages/rax-renderer/demo/wechat-miniprogram/pages/index.json
deleted file mode 100644
index 9448c84eaf..0000000000
--- a/packages/rax-renderer/demo/wechat-miniprogram/pages/index.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "title": "Wechat MiniProgram Rax Text demo",
- "usingComponents": {
- "my-component": "../components/Target/index"
- }
-}
diff --git a/packages/rax-renderer/demo/wechat-miniprogram/pages/index.wxml b/packages/rax-renderer/demo/wechat-miniprogram/pages/index.wxml
deleted file mode 100644
index 41b536b4ce..0000000000
--- a/packages/rax-renderer/demo/wechat-miniprogram/pages/index.wxml
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/packages/rax-renderer/demo/wechat-miniprogram/pages/index.wxss b/packages/rax-renderer/demo/wechat-miniprogram/pages/index.wxss
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/packages/rax-renderer/package.json b/packages/rax-renderer/package.json
deleted file mode 100644
index 837aebc85e..0000000000
--- a/packages/rax-renderer/package.json
+++ /dev/null
@@ -1,54 +0,0 @@
-{
- "name": "@alilc/lowcode-rax-renderer",
- "version": "1.2.3",
- "description": "Rax renderer for Ali lowCode engine",
- "main": "lib/index.js",
- "module": "es/index.js",
- "miniappConfig": {
- "main": "lib/miniapp/index",
- "main:wechat": "lib/wechat-miniprogram/index"
- },
- "files": [
- "dist",
- "es",
- "lib"
- ],
- "keywords": [
- "low-code",
- "lowcode",
- "Rax"
- ],
- "engines": {
- "npm": ">=3.0.0"
- },
- "peerDependencies": {
- "prop-types": "^15.7.2",
- "rax": "^1.1.0"
- },
- "scripts": {
- "start": "build-scripts start",
- "build": "build-scripts build"
- },
- "dependencies": {
- "@alilc/lowcode-renderer-core": "1.2.3",
- "@alilc/lowcode-utils": "1.2.3",
- "rax-find-dom-node": "^1.0.1"
- },
- "devDependencies": {
- "@alib/build-scripts": "^0.1.0",
- "build-plugin-rax-component": "^0.2.11",
- "driver-universal": "^3.1.3"
- },
- "publishConfig": {
- "access": "public",
- "registry": "https://registry.npmjs.org/"
- },
- "repository": {
- "type": "http",
- "url": "https://github.com/alibaba/lowcode-engine/tree/main/packages/rax-renderer"
- },
- "license": "MIT",
- "homepage": "https://github.com/alibaba/lowcode-engine/#readme",
- "gitHead": "2669f179e6f899d395ce1942d0fe04f9c5ed48a6",
- "bugs": "https://github.com/alibaba/lowcode-engine/issues"
-}
diff --git a/packages/rax-renderer/src/hoc/compFactory.tsx b/packages/rax-renderer/src/hoc/compFactory.tsx
deleted file mode 100644
index 1c821ab6d9..0000000000
--- a/packages/rax-renderer/src/hoc/compFactory.tsx
+++ /dev/null
@@ -1,82 +0,0 @@
-// @ts-nocheck
-
-import { Component, forwardRef } from 'rax';
-import PropTypes from 'prop-types';
-import { AppHelper } from '@alilc/lowcode-utils';
-import { utils, contextFactory } from '@alilc/lowcode-renderer-core';
-import componentRendererFactory from '../renderer/component';
-import blockRendererFactory from '../renderer/block';
-
-const { forEach, isFileSchema } = utils;
-
-export default function compFactory(schema, components = {}, componentsMap = {}, config = {}) {
- // 自定义组件需要有自己独立的appHelper
- const appHelper = new AppHelper(config);
- const CompRenderer = componentRendererFactory();
- const BlockRenderer = blockRendererFactory();
- const AppContext = contextFactory();
-
- class LNCompView extends Component {
- static displayName = 'LceCompFactory';
-
- static version = config.version || '0.0.0';
-
- static contextType = AppContext;
-
- static propTypes = {
- forwardedRef: PropTypes.func,
- };
-
- render() {
- if (!schema || schema.componentName !== 'Component' || !isFileSchema(schema)) {
- console.warn('自定义组件模型结构异常!');
- return null;
- }
- const { forwardedRef, ...otherProps } = this.props;
- // 低代码组件透传应用上下文
- const ctx = ['utils', 'constants', 'history', 'location', 'match'];
- ctx.forEach(key => {
- if (!appHelper[key] && this.context?.appHelper && this.context?.appHelper[key]) {
- appHelper.set(key, this.context.appHelper[key]);
- }
- });
- // 支持通过context透传国际化配置
- const localeProps = {};
- const { locale, messages } = this.context;
- if (locale && messages && messages[schema.fileName]) {
- localeProps.locale = locale;
- localeProps.messages = messages[schema.fileName];
- }
- const props = {
- ...schema.defaultProps,
- ...localeProps,
- ...otherProps,
- __schema: schema,
- ref: forwardedRef,
- };
-
- return (
-
- {context => {
- this.context = context;
- return (
-
- );
- }}
-
- );
- }
- }
-
- const ResComp = forwardRef((props, ref) =>
);
- forEach(schema.static, (val, key) => {
- ResComp[key] = val;
- });
- ResComp.version = config.version || '0.0.0';
- return ResComp;
-}
diff --git a/packages/rax-renderer/src/index.ts b/packages/rax-renderer/src/index.ts
deleted file mode 100644
index 1947aa5aec..0000000000
--- a/packages/rax-renderer/src/index.ts
+++ /dev/null
@@ -1,52 +0,0 @@
-import { Component, PureComponent, createElement, createContext, forwardRef } from 'rax';
-import findDOMNode from 'rax-find-dom-node';
-import {
- adapter,
- addonRendererFactory,
- tempRendererFactory,
- rendererFactory,
-} from '@alilc/lowcode-renderer-core';
-import pageRendererFactory from './renderer/page';
-import componentRendererFactory from './renderer/component';
-import blockRendererFactory from './renderer/block';
-import CompFactory from './hoc/compFactory';
-
-adapter.setRuntime({
- Component,
- PureComponent,
- createContext,
- createElement,
- forwardRef,
- findDOMNode,
-});
-
-adapter.setRenderers({
- PageRenderer: pageRendererFactory(),
- ComponentRenderer: componentRendererFactory(),
- BlockRenderer: blockRendererFactory(),
- AddonRenderer: addonRendererFactory(),
- TempRenderer: tempRendererFactory(),
-});
-
-function factory() {
- const Renderer = rendererFactory();
- return class extends Renderer {
- constructor(props: any, context: any) {
- super(props, context);
- }
-
- isValidComponent(obj: any) {
- return obj?.prototype?.setState || obj?.prototype instanceof Component;
- }
- };
-}
-
-const RaxRenderer: any = factory();
-const Engine: any = RaxRenderer;
-
-export {
- Engine,
- CompFactory,
-};
-
-export default RaxRenderer;
diff --git a/packages/rax-renderer/src/renderer/block.tsx b/packages/rax-renderer/src/renderer/block.tsx
deleted file mode 100644
index 8fa4a27316..0000000000
--- a/packages/rax-renderer/src/renderer/block.tsx
+++ /dev/null
@@ -1,25 +0,0 @@
-import { blockRendererFactory, types } from '@alilc/lowcode-renderer-core';
-
-const raxBlockRendererFactory: () => any = () => {
- const OriginBlock = blockRendererFactory();
- return class BlockRenderer extends OriginBlock {
- render() {
- // @ts-ignore
- const that: types.IRenderer = this;
- const { __schema, __components } = that.props;
- if (that.__checkSchema(__schema)) {
- return '区块 schema 结构异常!';
- }
- that.__debug(`render - ${__schema.fileName}`);
-
- const children = ((context) => {
- that.context = context;
- that.__generateCtx({});
- that.__render();
- return that.__renderComp((__components as any)?.Block, { blockContext: that });
- });
- return that.__renderContextConsumer(children);
- }
- };
-};
-export default raxBlockRendererFactory;
diff --git a/packages/rax-renderer/src/renderer/component.tsx b/packages/rax-renderer/src/renderer/component.tsx
deleted file mode 100644
index 9943b3c2f1..0000000000
--- a/packages/rax-renderer/src/renderer/component.tsx
+++ /dev/null
@@ -1,37 +0,0 @@
-import { componentRendererFactory, types } from '@alilc/lowcode-renderer-core';
-
-const raxComponentRendererFactory: () => any = () => {
- const OriginComponent = componentRendererFactory();
- return class ComponentRenderer extends OriginComponent {
- render() {
- // @ts-ignore
- const that: types.IRenderer = this;
- const { __schema, __components } = that.props;
- if (that.__checkSchema(__schema)) {
- return '自定义组件 schema 结构异常!';
- }
- that.__debug(`render - ${__schema.fileName}`);
-
- const { noContainer } = that.__parseData(__schema.props);
-
- const children = ((context) => {
- that.context = context;
- that.__generateCtx({ component: that });
- that.__render();
- // 传 null,使用内置的 div 来渲染,解决在页面中渲染 vc-component 报错的问题
- return that.__renderComp(null, {
- compContext: that,
- blockContext: that,
- });
- });
- const content = that.__renderContextConsumer(children);
-
- if (noContainer) {
- return content;
- }
-
- return that.__renderContent(content);
- }
- };
-};
-export default raxComponentRendererFactory;
diff --git a/packages/rax-renderer/src/renderer/page.tsx b/packages/rax-renderer/src/renderer/page.tsx
deleted file mode 100644
index f6ebd3f7cf..0000000000
--- a/packages/rax-renderer/src/renderer/page.tsx
+++ /dev/null
@@ -1,38 +0,0 @@
-import { pageRendererFactory, types } from '@alilc/lowcode-renderer-core';
-
-const raxPageRendererFactory: () => any = () => {
- const OriginPage = pageRendererFactory();
- return class PageRenderer extends OriginPage {
- async componentDidUpdate() {
- // @ts-ignore
- super.componentDidUpdate(...arguments);
- }
-
- render() {
- // @ts-ignore
- const that: types.IRenderer = this;
- const { __schema, __components } = that.props;
- if (that.__checkSchema(__schema)) {
- return '页面 schema 结构异常!';
- }
- that.__debug(`render - ${__schema?.fileName}`);
-
- const { Page } = __components as any;
- if (Page) {
- const children = ((context) => {
- that.context = context;
- that.__render();
- return that.__renderComp(Page, { pageContext: that });
- });
- return that.__renderContextConsumer(children);
- }
-
- return that.__renderContent(that.__renderContextConsumer((context) => {
- that.context = context;
- return that.__renderContextProvider({ pageContext: that });
- }));
- }
- };
-};
-
-export default raxPageRendererFactory;
\ No newline at end of file
diff --git a/packages/rax-renderer/tsconfig.json b/packages/rax-renderer/tsconfig.json
deleted file mode 100644
index 7e264d1f05..0000000000
--- a/packages/rax-renderer/tsconfig.json
+++ /dev/null
@@ -1,26 +0,0 @@
-{
- "compilerOptions": {
- "lib": ["es2015", "dom"],
- "target": "esnext",
- "module": "esnext",
- "moduleResolution": "node",
- "strict": false,
- "strictPropertyInitialization": false,
- "allowSyntheticDefaultImports": true,
- "esModuleInterop": true,
- "jsx": "react",
- "jsxFactory": "createElement",
- "importHelpers": true,
- "experimentalDecorators": true,
- "emitDecoratorMetadata": true,
- "sourceMap": true,
- "forceConsistentCasingInFileNames": true,
- "resolveJsonModule": true,
- "skipLibCheck": true,
- "outDir": "lib"
- },
- "exclude": ["test", "lib", "es", "node_modules"],
- "include": [
- "src"
- ]
-}
diff --git a/packages/rax-simulator-renderer/.babelrc b/packages/rax-simulator-renderer/.babelrc
deleted file mode 100644
index e0e2e5f343..0000000000
--- a/packages/rax-simulator-renderer/.babelrc
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "plugins": [
- ["@babel/plugin-transform-react-jsx", {
- "pragma": "createElement", // default pragma is React.createElement
- "pragmaFrag": "createFragment", // default is React.Fragment
- "throwIfNamespace": false // defaults to true
- }]
- ]
-}
diff --git a/packages/rax-simulator-renderer/babel.config.js b/packages/rax-simulator-renderer/babel.config.js
deleted file mode 100644
index c5986f2bc0..0000000000
--- a/packages/rax-simulator-renderer/babel.config.js
+++ /dev/null
@@ -1 +0,0 @@
-module.exports = require('../../babel.config');
\ No newline at end of file
diff --git a/packages/rax-simulator-renderer/build.json b/packages/rax-simulator-renderer/build.json
deleted file mode 100644
index e7ae1dcf7a..0000000000
--- a/packages/rax-simulator-renderer/build.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "plugins": ["@alilc/build-plugin-lce", "./build.plugin.js"]
-}
diff --git a/packages/rax-simulator-renderer/build.plugin.js b/packages/rax-simulator-renderer/build.plugin.js
deleted file mode 100644
index d613f1f56a..0000000000
--- a/packages/rax-simulator-renderer/build.plugin.js
+++ /dev/null
@@ -1,5 +0,0 @@
-module.exports = ({ onGetWebpackConfig }) => {
- onGetWebpackConfig((config) => {
- config.performance.hints(false);
- });
-};
diff --git a/packages/rax-simulator-renderer/build.umd.json b/packages/rax-simulator-renderer/build.umd.json
deleted file mode 100644
index 833c92b246..0000000000
--- a/packages/rax-simulator-renderer/build.umd.json
+++ /dev/null
@@ -1,38 +0,0 @@
-{
- "entry": {
- "rax-simulator-renderer": "src/index"
- },
- "sourceMap": true,
- "library": "___RaxSimulatorRenderer___",
- "libraryTarget": "umd",
- "externals": {
- "react": "var window.React",
- "react-dom": "var window.ReactDOM",
- "prop-types": "var window.PropTypes",
- "@alifd/next": "var Next",
- "@alilc/lowcode-engine-ext": "var window.AliLowCodeEngineExt",
- "rax": "var window.Rax",
- "moment": "var moment",
- "lodash": "var _"
- },
- "polyfill": false,
- "outputDir": "dist",
- "vendor": false,
- "ignoreHtmlTemplate": true,
- "plugins": [
- "build-plugin-react-app",
- [
- "build-plugin-fusion",
- {
- "externalNext": "umd"
- }
- ],
- [
- "build-plugin-moment-locales",
- {
- "locales": ["zh-cn"]
- }
- ],
- "./build.plugin.js"
- ]
-}
diff --git a/packages/rax-simulator-renderer/package.json b/packages/rax-simulator-renderer/package.json
deleted file mode 100644
index dc9b7b1478..0000000000
--- a/packages/rax-simulator-renderer/package.json
+++ /dev/null
@@ -1,55 +0,0 @@
-{
- "name": "@alilc/lowcode-rax-simulator-renderer",
- "version": "1.2.3",
- "description": "rax simulator renderer for alibaba lowcode designer",
- "main": "lib/index.js",
- "module": "es/index.js",
- "license": "MIT",
- "files": [
- "dist"
- ],
- "scripts": {
- "build": "NODE_OPTIONS=--max_old_space_size=8192 build-scripts build",
- "build:umd": "build-scripts build --config build.umd.json"
- },
- "dependencies": {
- "@alilc/lowcode-designer": "1.2.3",
- "@alilc/lowcode-rax-renderer": "1.2.3",
- "@alilc/lowcode-types": "1.2.3",
- "@alilc/lowcode-utils": "1.2.3",
- "classnames": "^2.2.6",
- "driver-universal": "^3.1.3",
- "history": "^5.0.0",
- "lodash": "^4.17.19",
- "mobx": "^6.3.0",
- "mobx-react": "^7.2.0",
- "path-to-regexp": "3.2.0",
- "rax-find-dom-node": "^1.0.0",
- "react": "^16",
- "react-dom": "^16.7.0"
- },
- "devDependencies": {
- "@alib/build-scripts": "^0.1.18",
- "@babel/plugin-transform-react-jsx": "^7.10.4",
- "@types/classnames": "^2.2.7",
- "@types/node": "^13.7.1",
- "@types/rax": "^1.0.0",
- "@types/react": "^16",
- "@types/react-dom": "^16",
- "build-plugin-rax-component": "^0.2.11"
- },
- "peerDependencies": {
- "rax": "^1.1.0"
- },
- "publishConfig": {
- "access": "public",
- "registry": "https://registry.npmjs.org/"
- },
- "repository": {
- "type": "http",
- "url": "https://github.com/alibaba/lowcode-engine/tree/main/packages/rax-simulator-renderer"
- },
- "homepage": "https://github.com/alibaba/lowcode-engine/#readme",
- "gitHead": "2669f179e6f899d395ce1942d0fe04f9c5ed48a6",
- "bugs": "https://github.com/alibaba/lowcode-engine/issues"
-}
diff --git a/packages/rax-simulator-renderer/src/builtin-components/UnusualComponent/index.less b/packages/rax-simulator-renderer/src/builtin-components/UnusualComponent/index.less
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/packages/rax-simulator-renderer/src/builtin-components/UnusualComponent/index.tsx b/packages/rax-simulator-renderer/src/builtin-components/UnusualComponent/index.tsx
deleted file mode 100644
index 6608942c4b..0000000000
--- a/packages/rax-simulator-renderer/src/builtin-components/UnusualComponent/index.tsx
+++ /dev/null
@@ -1,34 +0,0 @@
-import { Component } from 'rax';
-import lg from '@ali/vu-logger';
-
-import './index.less';
-
-export class UnknownComponent extends Component {
- props: {
- _componentName: string;
- };
-
- render() {
- lg.log('ERROR_NO_COMPONENT_VIEW');
- lg.error('Error component information:', this.props);
- return
组件 {this.props._componentName} 无视图,请打开控制台排查
;
- }
-}
-
-export class FaultComponent extends Component {
- props: {
- _componentName: string;
- };
-
- render() {
- return
组件 {this.props._componentName} 渲染错误,请打开控制台排查
;
- }
-}
-
-export class HiddenComponent extends Component {
- render() {
- return
在本页面不显示
;
- }
-}
-
-export default { FaultComponent, HiddenComponent, UnknownComponent };
diff --git a/packages/rax-simulator-renderer/src/builtin-components/leaf.tsx b/packages/rax-simulator-renderer/src/builtin-components/leaf.tsx
deleted file mode 100644
index 5276b0018d..0000000000
--- a/packages/rax-simulator-renderer/src/builtin-components/leaf.tsx
+++ /dev/null
@@ -1,251 +0,0 @@
-import { Component } from 'rax';
-
-class Leaf extends Component {
- static displayName = 'Leaf';
-
- static componentMetadata = {
- componentName: 'Leaf',
- configure: {
- props: [{
- name: 'children',
- setter: 'StringSetter',
- }],
- // events/className/style/general/directives
- supports: false,
- },
- };
-
- render() {
- const { children } = this.props;
- return children;
- }
-}
-
-export default Leaf;
-
-// import { Component, createElement } from 'rax';
-// import findDOMNode from 'rax-find-dom-node';
-// import { each, get, omit } from 'lodash';
-// import { getView, setNativeNode, createNodeStyleSheet } from '../renderUtils';
-
-// import { FaultComponent, HiddenComponent, UnknownComponent } from '../UnusualComponent';
-
-// export interface ILeaf {
-// leaf: any;
-// }
-// export default class Leaf extends Component
{
-// static displayName = 'Leaf';
-
-// state = {
-// hasError: false,
-// };
-
-// willDetach: any[];
-
-// styleSheet: any;
-
-// context: any;
-// refs: any;
-
-// componentWillMount() {
-// const { leaf } = this.props;
-// this.willDetach = [
-// leaf.onPropsChange(() => {
-// // 强制刷新
-// this.setState(this.state);
-// }),
-// leaf.onChildrenChange(() => {
-// // 强制刷新
-// this.setState(this.state);
-// }),
-// leaf.onStatusChange((status: { dropping: boolean }, field: string) => {
-// // console.log({...status}, field)
-// if (status.dropping !== false) {
-// // 当 dropping 为 Insertion 对象时,强制渲染会出错,原因待查
-// return;
-// }
-// if (field === 'dragging' || field === 'dropping' || field === 'pseudo' || field === 'visibility') {
-// // 强制刷新
-// this.setState(this.state);
-// }
-// }),
-// ];
-
-// /**
-// * while props replaced
-// * bind the new event on it
-// */
-// leaf.onPropsReplace(() => {
-// this.willDetach[0]();
-// this.willDetach[0] = leaf.onPropsChange(() => {
-// // 强制刷新
-// this.setState(this.state);
-// });
-// });
-// }
-
-// componentDidMount() {
-// this.modifyDOM();
-// }
-
-// shouldComponentUpdate() {
-// // forceUpdate 的替代方案
-// return true;
-// // const pageCanRefresh = this.leaf.getPage().canRefresh();
-// // if (pageCanRefresh) {
-// // return pageCanRefresh;
-// // }
-// // const getExtProps = obj => {
-// // const { leaf, ...props } = obj;
-// // return props;
-// // };
-// // return !shallowEqual(getExtProps(this.props), getExtProps(nextProps));
-// }
-
-// componentDidUpdate() {
-// this.modifyDOM();
-// }
-
-// componentWillUnmount() {
-// if (this.willDetach) {
-// this.willDetach.forEach((off) => off());
-// }
-// setNativeNode(this.props.leaf, null);
-// }
-
-// componentDidCatch() {
-// this.setState({ hasError: true }, () => {
-// console.log('error');
-// });
-// }
-
-// modifyDOM() {
-// const shell = findDOMNode(this);
-// const { leaf } = this.props;
-// // 与 React 不同,rax 的 findDOMNode 找不到节点时,
-// // shell 会是 ,而不是 null,
-// // 所以这里进行是否为注释的判断
-// if (shell && shell.nodeType !== window.Node.COMMENT_NODE) {
-// setNativeNode(leaf, shell);
-// if (leaf.getStatus('dragging')) {
-// get(shell, 'classList').add('engine-dragging');
-// } else {
-// get(shell, 'classList').remove('engine-dragging');
-// }
-// each(get(shell, 'classList'), (cls) => {
-// if (cls.substring(0, 8) === '-pseudo-') {
-// get(shell, 'classList').remove(cls);
-// }
-// });
-// const pseudo = leaf.getStatus('pseudo');
-// if (pseudo) {
-// get(shell, 'classList').add(`-pseudo-${pseudo}`);
-// }
-// } else {
-// setNativeNode(leaf, null);
-// }
-// }
-
-// render() {
-// const props = omit(this.props, ['leaf']);
-// const { leaf } = this.props;
-// const componentName = leaf.getComponentName();
-
-// const View = getView(componentName);
-
-// const newProps = {
-// _componentName: componentName,
-// };
-
-// if (!View) {
-// return createElement(UnknownComponent, {
-// // _componentName: componentName,
-// ...newProps,
-// });
-// }
-
-// let staticProps = {
-// ...leaf.getStaticProps(false),
-// ...props,
-// _componentName: componentName,
-// _leaf: leaf,
-// componentId: leaf.getId(),
-// };
-
-// if (!leaf.isVisibleInPane()) {
-// return null;
-// }
-
-// if (!leaf.isVisible()) {
-// return createElement(HiddenComponent, {
-// ...staticProps,
-// });
-// }
-
-// if (this.state.hasError) {
-// return createElement(FaultComponent, {
-// // _componentName: componentName,
-// ...newProps,
-// });
-// }
-
-// if (this.styleSheet) {
-// this.styleSheet.parentNode.removeChild(this.styleSheet);
-// }
-
-// this.styleSheet = createNodeStyleSheet(staticProps);
-
-// if (leaf.ableToModifyChildren()) {
-// const children = leaf
-// .getChildren()
-// .filter((child: any) => child.getComponentName() !== 'Slot')
-// .map((child: any) =>
-// createElement(Leaf, {
-// key: child.getId(),
-// leaf: child,
-// }),
-// );
-// // const insertion = leaf.getStatus('dropping');
-// // InsertionGhost 都是React节点,用Rax渲染会报错,后面这些节点需要通过Rax组件来实现
-// // if (children.length < 1 && insertion && insertion.getIndex() !== null) {
-
-// // //children = [];
-// // children = [];
-// // } else if (insertion && insertion.isNearEdge()) {
-// // if (insertion.isNearAfter()) {
-// // children.push();
-// // } else {
-// // children.unshift();
-// // }
-// // }
-// staticProps = {
-// ...staticProps,
-// ...this.processSlots(this.props.leaf.getChildren()),
-// };
-
-// return createElement(
-// View,
-// {
-// ...staticProps,
-// },
-// children,
-// );
-// }
-
-// return createElement(View, {
-// ...staticProps,
-// });
-// }
-
-// processSlots(children: Rax.RaxNodeArray) {
-// const slots: any = {};
-// children &&
-// children.length &&
-// children.forEach((child: any) => {
-// if (child.getComponentName() === 'Slot') {
-// slots[child.getPropValue('slotName')] = ;
-// }
-// });
-// return slots;
-// }
-// }
diff --git a/packages/rax-simulator-renderer/src/builtin-components/renderUtils.ts b/packages/rax-simulator-renderer/src/builtin-components/renderUtils.ts
deleted file mode 100644
index 10f8438fb2..0000000000
--- a/packages/rax-simulator-renderer/src/builtin-components/renderUtils.ts
+++ /dev/null
@@ -1,83 +0,0 @@
-import { isObject } from 'lodash';
-import { css } from '@alilc/lowcode-utils';
-
-const { toCss } = css;
-const engine = (window as any).VisualEngine;
-const { Trunk, Viewport } = engine;
-
-export const NativeNodeCache: any = {};
-
-function ucfirst(s: string) {
- return s.charAt(0).toUpperCase() + s.substring(1);
-}
-
-export function shallowEqual(obj: { [key: string]: string }, tObj: { [key: string]: string }) {
- for (const i in obj) {
- if (Object.prototype.hasOwnProperty.call(obj, i) && obj[i] !== tObj[i]) {
- return false;
- }
- }
- return true;
-}
-
-export function createNodeStyleSheet(props: any) {
- if (props && props.fieldId) {
- let styleProp = props.__style__;
-
- if (isObject(styleProp)) {
- styleProp = toCss(styleProp);
- }
-
- if (typeof styleProp === 'string') {
- const s = document.createElement('style');
- const cssId = `_style_pesudo_${ props.fieldId}`;
- const cssClass = `_css_pesudo_${ props.fieldId}`;
-
- props.className = cssClass;
- s.setAttribute('type', 'text/css');
- s.setAttribute('id', cssId);
- document.getElementsByTagName('head')[0].appendChild(s);
-
- s.appendChild(
- document.createTextNode(
- styleProp
- .replace(/(\d+)rpx/g, (a, b) => {
- return `${b / 2}px`;
- })
- .replace(/:root/g, `.${ cssClass}`),
- ),
- );
- return s;
- }
- }
-}
-
-export function setNativeNode(leaf: any, node: Rax.RaxNode) {
- const id = leaf.getId();
- if (NativeNodeCache[id] === node) {
- return;
- }
- NativeNodeCache[id] = node;
- leaf.mountChange();
-}
-
-export function getView(componentName: string) {
- // let view = new Trunk().getPrototypeView(componentName);
- let view = Trunk.getPrototypeView(componentName);
- if (!view) {
- return null;
- }
- const viewport = Viewport.getViewport();
- if (viewport) {
- const [mode, device] = viewport.split('-', 2).map(ucfirst);
- if (view.hasOwnProperty(device)) {
- view = view[device];
- }
-
- if (view.hasOwnProperty(mode)) {
- view = view[mode];
- }
- }
-
- return view;
-}
diff --git a/packages/rax-simulator-renderer/src/builtin-components/slot.tsx b/packages/rax-simulator-renderer/src/builtin-components/slot.tsx
deleted file mode 100644
index 3a77491bc0..0000000000
--- a/packages/rax-simulator-renderer/src/builtin-components/slot.tsx
+++ /dev/null
@@ -1,54 +0,0 @@
-import { Component } from 'rax';
-
-class Slot extends Component {
- static displayName = 'Slot';
-
- static componentMetadata = {
- componentName: 'Slot',
- configure: {
- props: [{
- name: '___title',
- title: {
- type: 'i18n',
- 'en-US': 'Slot Title',
- 'zh-CN': '插槽标题',
- },
- setter: 'StringSetter',
- defaultValue: '插槽容器',
- }, {
- name: '___params',
- title: {
- type: 'i18n',
- 'en-US': 'Slot Params',
- 'zh-CN': '插槽入参',
- },
- setter: {
- componentName: 'ArraySetter',
- props: {
- itemSetter: {
- componentName: 'StringSetter',
- props: {
- placeholder: {
- type: 'i18n',
- 'zh-CN': '参数名称',
- 'en-US': 'Argument Name',
- },
- },
- },
- },
- },
- }],
- // events/className/style/general/directives
- supports: false,
- },
- };
-
- render() {
- const { children } = this.props;
- return (
- {children}
- );
- }
-}
-
-export default Slot;
diff --git a/packages/rax-simulator-renderer/src/host.ts b/packages/rax-simulator-renderer/src/host.ts
deleted file mode 100644
index c5cf2e3e1c..0000000000
--- a/packages/rax-simulator-renderer/src/host.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-// NOTE: 仅做类型标注,切勿做其它用途
-import { BuiltinSimulatorHost } from '@alilc/lowcode-designer';
-
-export const host: BuiltinSimulatorHost = (window as any).LCSimulatorHost;
diff --git a/packages/rax-simulator-renderer/src/image.d.ts b/packages/rax-simulator-renderer/src/image.d.ts
deleted file mode 100644
index 7ed4ad925c..0000000000
--- a/packages/rax-simulator-renderer/src/image.d.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-declare module 'rax-find-dom-node';
-declare module '@alilc/lowcode-rax-renderer/lib/index';
diff --git a/packages/rax-simulator-renderer/src/index.ts b/packages/rax-simulator-renderer/src/index.ts
deleted file mode 100644
index 3a88726657..0000000000
--- a/packages/rax-simulator-renderer/src/index.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-import renderer from './renderer';
-
-if (typeof window !== 'undefined') {
- (window as any).SimulatorRenderer = renderer;
-}
-
-export default renderer;
diff --git a/packages/rax-simulator-renderer/src/rax-use-router.js b/packages/rax-simulator-renderer/src/rax-use-router.js
deleted file mode 100644
index 9a399a9478..0000000000
--- a/packages/rax-simulator-renderer/src/rax-use-router.js
+++ /dev/null
@@ -1,288 +0,0 @@
-// Inspired by react-router and universal-router
-import { useState, useEffect, useLayoutEffect, createElement } from 'rax';
-import pathToRegexp from 'path-to-regexp';
-
-const cache = {};
-function decodeParam(val) {
- try {
- return decodeURIComponent(val);
- } catch (err) {
- return val;
- }
-}
-
-function matchPath(route, pathname, parentParams) {
- let { path, routes, exact: end = true, strict = false, sensitive = false } = route;
- // If not has path or has routes that should do not exact match
- if (path == null || routes) {
- end = false;
- }
-
- // Default path is empty
- path = path || '';
-
- const regexpCacheKey = `${path}|${end}|${strict}|${sensitive}`;
- const keysCacheKey = `${regexpCacheKey }|`;
-
- let regexp = cache[regexpCacheKey];
- const keys = cache[keysCacheKey] || [];
-
- if (!regexp) {
- regexp = pathToRegexp(path, keys, {
- end,
- strict,
- sensitive,
- });
- cache[regexpCacheKey] = regexp;
- cache[keysCacheKey] = keys;
- }
-
- const result = regexp.exec(pathname);
- if (!result) {
- return null;
- }
-
- const url = result[0];
- const params = { ...parentParams, history: router.history, location: router.history.location };
-
- for (let i = 1; i < result.length; i++) {
- const key = keys[i - 1];
- const prop = key.name;
- const value = result[i];
- if (value !== undefined || !Object.prototype.hasOwnProperty.call(params, prop)) {
- if (key.repeat) {
- params[prop] = value ? value.split(key.delimiter).map(decodeParam) : [];
- } else {
- params[prop] = value ? decodeParam(value) : value;
- }
- }
- }
-
- return {
- path: !end && url.charAt(url.length - 1) === '/' ? url.slice(1) : url,
- params,
- };
-}
-
-function matchRoute(route, baseUrl, pathname, parentParams) {
- let matched;
- let childMatches;
- let childIndex = 0;
-
- return {
- next() {
- if (!matched) {
- matched = matchPath(route, pathname, parentParams);
-
- if (matched) {
- return {
- done: false,
- $: {
- route,
- baseUrl,
- path: matched.path,
- params: matched.params,
- },
- };
- }
- }
-
- if (matched && route.routes) {
- while (childIndex < route.routes.length) {
- if (!childMatches) {
- const childRoute = route.routes[childIndex];
- childRoute.parent = route;
-
- childMatches = matchRoute(
- childRoute,
- baseUrl + matched.path,
- pathname.slice(matched.path.length),
- matched.params,
- );
- }
-
- const childMatch = childMatches.next();
- if (!childMatch.done) {
- return {
- done: false,
- $: childMatch.$,
- };
- }
-
- childMatches = null;
- childIndex++;
- }
- }
-
- return { done: true };
- },
- };
-}
-
-let _initialized = false;
-let _routerConfig = null;
-const router = {
- history: null,
- handles: [],
- errorHandler() { },
- addHandle(handle) {
- return router.handles.push(handle);
- },
- removeHandle(handleId) {
- router.handles[handleId - 1] = null;
- },
- triggerHandles(component) {
- router.handles.forEach((handle) => {
- handle && handle(component);
- });
- },
- match(fullpath) {
- if (fullpath == null) return;
-
- router.fullpath = fullpath;
-
- const parent = router.root;
- const matched = matchRoute(
- parent,
- parent.path,
- fullpath,
- );
-
- function next(parent) {
- const current = matched.next();
-
- if (current.done) {
- const error = new Error(`No match for ${fullpath}`);
- return router.errorHandler(error, router.history.location);
- }
-
- let { component } = current.$.route;
- if (typeof component === 'function') {
- component = component(current.$.params, router.history.location);
- }
- if (component instanceof Promise) {
- // Lazy loading component by import('./Foo')
- return component.then((component) => {
- // Check current fullpath avoid router has changed before lazy loading complete
- if (fullpath === router.fullpath) {
- router.triggerHandles(component);
- }
- });
- } else if (component != null) {
- router.triggerHandles(component);
- return component;
- } else {
- return next(parent);
- }
- }
-
- return next(parent);
- },
-};
-
-function matchLocation({ pathname }) {
- router.match(pathname);
-}
-
-
-function getInitialComponent(routerConfig) {
- let InitialComponent = [];
-
- if (_routerConfig === null) {
- if (process.env.NODE_ENV !== 'production') {
- if (!routerConfig) {
- throw new Error('Error: useRouter should have routerConfig, see: https://www.npmjs.com/package/rax-use-router.');
- }
- if (!routerConfig.history || !routerConfig.routes) {
- throw new Error('Error: routerConfig should contain history and routes, see: https://www.npmjs.com/package/rax-use-router.');
- }
- }
- _routerConfig = routerConfig;
- }
- if (_routerConfig.InitialComponent) {
- InitialComponent = _routerConfig.InitialComponent;
- }
- router.history = _routerConfig.history;
-
- return InitialComponent;
-}
-
-let unlisten = null;
-let handleId = null;
-let pathes = '';
-export function useRouter(routerConfig) {
- const [component, setComponent] = useState(getInitialComponent(routerConfig));
-
- let newPathes = '';
- if (routerConfig) {
- _routerConfig = routerConfig;
- const { routes } = _routerConfig;
- router.root = Array.isArray(routes) ? { routes } : routes;
- if (Array.isArray(routes)) {
- newPathes = routes.map(it => it.path).join(',');
- } else {
- newPathes = routes.path;
- }
- }
- if (_initialized && _routerConfig.history) {
- if (newPathes !== pathes) {
- matchLocation(_routerConfig.history.location);
- pathes = newPathes;
- }
- }
-
- useLayoutEffect(() => {
- if (unlisten) {
- unlisten();
- unlisten = null;
- }
-
- if (handleId) {
- router.removeHandle(handleId);
- handleId = null;
- }
-
- const { history } = _routerConfig;
- const { routes } = _routerConfig;
-
- router.root = Array.isArray(routes) ? { routes } : routes;
-
- handleId = router.addHandle((component) => {
- setComponent(component);
- });
-
- // Init path match
- if (_initialized || !_routerConfig.InitialComponent) {
- matchLocation(history.location);
- pathes = newPathes;
- }
-
- unlisten = history.listen(({ location }) => {
- matchLocation(location);
- pathes = newPathes;
- });
-
- _initialized = true;
-
- return () => {
- pathes = '';
- router.removeHandle(handleId);
- handleId = null;
- unlisten();
- unlisten = null;
- };
- }, []);
-
- return { component };
-}
-
-export function withRouter(Component) {
- function Wrapper(props) {
- const { history } = router;
- return createElement(Component, { ...props, history, location: history.location });
- }
-
- Wrapper.displayName = `withRouter(${ Component.displayName || Component.name })`;
- Wrapper.WrappedComponent = Component;
- return Wrapper;
-}
diff --git a/packages/rax-simulator-renderer/src/renderer-view.tsx b/packages/rax-simulator-renderer/src/renderer-view.tsx
deleted file mode 100644
index bc39cddea0..0000000000
--- a/packages/rax-simulator-renderer/src/renderer-view.tsx
+++ /dev/null
@@ -1,275 +0,0 @@
-import RaxRenderer from '@alilc/lowcode-rax-renderer';
-import { History } from 'history';
-import { Component, createElement, Fragment } from 'rax';
-import { useRouter } from './rax-use-router';
-import { DocumentInstance, SimulatorRendererContainer } from './renderer';
-import './renderer.less';
-import { uniqueId } from '@alilc/lowcode-utils';
-import { GlobalEvent } from '@alilc/lowcode-types';
-import { host } from './host';
-
-// patch cloneElement avoid lost keyProps
-const originCloneElement = (window as any).Rax.cloneElement;
-(window as any).Rax.cloneElement = (child: any, { _leaf, ...props }: any = {}, ...rest: any[]) => {
- if (child.ref && props.ref) {
- const dRef = props.ref;
- const cRef = child.ref;
- props.ref = (x: any) => {
- if (cRef) {
- if (typeof cRef === 'function') {
- cRef(x);
- } else {
- try {
- cRef.current = x;
- } catch (e) {
- console.error(e);
- }
- }
- }
- if (dRef) {
- if (typeof dRef === 'function') {
- dRef(x);
- } else {
- try {
- dRef.current = x;
- } catch (e) {
- console.error(e);
- }
- }
- }
- };
- }
- return originCloneElement(child, props, ...rest);
-};
-
-export default class SimulatorRendererView extends Component<{ rendererContainer: SimulatorRendererContainer }> {
- private unlisten: any;
-
- componentDidMount() {
- const { rendererContainer } = this.props;
- this.unlisten = rendererContainer.onLayoutChange(() => {
- this.forceUpdate();
- });
- }
-
- componentWillUnmount() {
- if (this.unlisten) {
- this.unlisten();
- }
- }
-
- render() {
- const { rendererContainer } = this.props;
- return (
-
-
-
- );
- }
-}
-
-export const Routes = (props: {
- rendererContainer: SimulatorRendererContainer;
- history: History;
-}) => {
- const { rendererContainer, history } = props;
- const { documentInstances } = rendererContainer;
-
- const routes = {
- history,
- routes: documentInstances.map(instance => {
- return {
- path: instance.path,
- component: (props: any) => ,
- };
- }),
- };
- const { component } = useRouter(routes);
- return component;
-};
-
-function ucfirst(s: string) {
- return s.charAt(0).toUpperCase() + s.substring(1);
-}
-function getDeviceView(view: any, device: string, mode: string) {
- if (!view || typeof view === 'string') {
- return view;
- }
-
- // compatible vision Mobile | Preview
- device = ucfirst(device);
- if (device === 'Mobile' && view.hasOwnProperty(device)) {
- view = view[device];
- }
- mode = ucfirst(mode);
- if (mode === 'Preview' && view.hasOwnProperty(mode)) {
- view = view[mode];
- }
- return view;
-}
-
-class Layout extends Component<{ rendererContainer: SimulatorRendererContainer }> {
- constructor(props: any) {
- super(props);
- this.props.rendererContainer.onReRender(() => {
- this.forceUpdate();
- });
- }
-
- render() {
- const { rendererContainer, children } = this.props;
- const { layout } = rendererContainer;
-
- if (layout) {
- const { Component, props, componentName } = layout;
- if (Component) {
- return {children};
- }
- if (componentName && rendererContainer.getComponent(componentName)) {
- return createElement(
- rendererContainer.getComponent(componentName),
- {
- ...props,
- rendererContainer,
- },
- [children],
- );
- }
- }
-
- return {children};
- }
-}
-
-class Renderer extends Component<{
- rendererContainer: SimulatorRendererContainer;
- documentInstance: DocumentInstance;
-}> {
- private unlisten: any;
- private key: string;
- private startTime: number | null = null;
-
- componentWillMount() {
- this.key = uniqueId('renderer');
- }
-
- componentDidMount() {
- const { documentInstance } = this.props;
- this.unlisten = documentInstance.onReRender((params) => {
- if (params && params.shouldRemount) {
- this.key = uniqueId('renderer');
- }
- this.forceUpdate();
- });
- }
-
- componentWillUnmount() {
- if (this.unlisten) {
- this.unlisten();
- }
- }
- shouldComponentUpdate() {
- return false;
- }
-
- componentDidUpdate() {
- if (this.startTime) {
- const time = Date.now() - this.startTime;
- const nodeCount = host.designer.currentDocument?.getNodeCount?.();
- host.designer.editor?.eventBus.emit(GlobalEvent.Node.Rerender, {
- componentName: 'Renderer',
- type: 'All',
- time,
- nodeCount,
- });
- }
- }
-
- schemaChangedSymbol = false;
-
- getSchemaChangedSymbol = () => {
- return this.schemaChangedSymbol;
- };
-
- setSchemaChangedSymbol = (symbol: boolean) => {
- this.schemaChangedSymbol = symbol;
- };
-
- render() {
- const { documentInstance } = this.props;
- const { container, document } = documentInstance;
- const { designMode, device } = container;
- const { rendererContainer: renderer } = this.props;
- this.startTime = Date.now();
- this.schemaChangedSymbol = false;
-
- return (
- {
- documentInstance.mountInstance(schema.id, ref);
- }}
- thisRequiredInJSE={host.thisRequiredInJSE}
- documentId={document.id}
- getNode={(id: string) => documentInstance.getNode(id) as any}
- rendererName="PageRenderer"
- customCreateElement={(Component: any, props: any, children: any) => {
- const { __id, ...viewProps } = props;
- viewProps.componentId = __id;
- const leaf = documentInstance.getNode(__id);
- viewProps._leaf = leaf;
- viewProps._componentName = leaf?.componentName;
- // 如果是容器 && 无children && 高宽为空 增加一个占位容器,方便拖动
- if (
- !viewProps.dataSource &&
- leaf?.isContainer() &&
- (children == null || (Array.isArray(children) && !children.length)) &&
- (!viewProps.style || Object.keys(viewProps.style).length === 0)
- ) {
- children = (
-
- {viewProps.placeholder || '拖拽组件或模板到这里'}
-
- );
- }
-
- // if (viewProps._componentName === 'Menu') {
- // Object.assign(viewProps, {
- // _componentName: 'Menu',
- // className: '_css_pesudo_menu_kbrzyh0f',
- // context: { VE: (window as any).VisualLowCodeRenderer },
- // direction: undefined,
- // events: { ignored: true },
- // fieldId: 'menu_kbrzyh0f',
- // footer: '',
- // header: '',
- // mode: 'inline',
- // onItemClick: { ignored: true },
- // onSelect: { ignored: true },
- // popupAlign: 'follow',
- // selectMode: false,
- // triggerType: 'click',
- // });
- // console.info('menuprops', viewProps);
- // }
-
- return createElement(
- getDeviceView(Component, device, designMode),
- viewProps,
- leaf?.isContainer() ? (children == null ? [] : Array.isArray(children) ? children : [children]) : children,
- );
- }}
- />
- );
- }
-}
diff --git a/packages/rax-simulator-renderer/src/renderer.less b/packages/rax-simulator-renderer/src/renderer.less
deleted file mode 100644
index b71dde9896..0000000000
--- a/packages/rax-simulator-renderer/src/renderer.less
+++ /dev/null
@@ -1,125 +0,0 @@
-body, html {
- display: block;
- background: white;
- padding: 0;
- margin: 0;
-}
-
-html.engine-cursor-move, html.engine-cursor-move * {
- cursor: grabbing !important;
-}
-
-html.engine-cursor-copy, html.engine-cursor-copy * {
- cursor: copy !important;
-}
-
-html.engine-cursor-ew-resize, html.engine-cursor-ew-resize * {
- cursor: ew-resize !important;
-}
-
-::-webkit-scrollbar {
- display: none;
-}
-
-.lc-container {
- &:empty {
- background: #f2f3f5;
- color: #a7b1bd;
- outline: 1px dashed rgba(31, 56, 88, 0.2);
- outline-offset: -1px !important;
- height: 66px;
- max-height: 100%;
- min-width: 140px;
- text-align: center;
- overflow: hidden;
- display: flex;
- align-items: center;
- &:before {
- content: '\62D6\62FD\7EC4\4EF6\6216\6A21\677F\5230\8FD9\91CC';
- font-size: 14px;
- z-index: 1;
- width: 100%;
- white-space: nowrap;
- height: 100%;
- display: flex;
- align-items: center;
- justify-content: center;
- }
- }
-}
-
-.engine-empty {
- background: #f2f3f5;
- color: #a7b1bd;
- outline: 1px dashed rgba(31, 56, 88, 0.2);
- outline-offset: -1px !important;
- height: 66px;
- max-height: 100%;
- min-width: 140px;
- text-align: center;
- overflow: hidden;
- display: flex;
- align-items: center;
-}
-
-.engine-empty:before {
- content: '\62D6\62FD\7EC4\4EF6\6216\6A21\677F\5230\8FD9\91CC';
- font-size: 14px;
- z-index: 1;
- width: 100%;
- white-space: nowrap;
- height: 100%;
- display: flex;
- align-items: center;
- justify-content: center;
-}
-
-.lc-container-placeholder {
- min-height: 60px;
- height: 100%;
- width: 100%;
- background-color: rgb(240, 240, 240);
- border: 1px dotted;
- color: rgb(167, 177, 189);
- display: flex;
- align-items: center;
- justify-content: center;
- font-size: 14px;
-}
-
-body.engine-document {
- &:after, &:before {
- content: "";
- display: table;
- }
- &:after {
- clear: both;
- }
-
- /*
- .next-input-group,
- .next-checkbox-group,.next-date-picker,.next-input,.next-month-picker,
- .next-number-picker,.next-radio-group,.next-range,.next-range-picker,
- .next-rating,.next-select,.next-switch,.next-time-picker,.next-upload,
- .next-year-picker,
- .next-breadcrumb-item,.next-calendar-header,.next-calendar-table {
- pointer-events: none !important;
- } */
-}
-
-.engine-live-editing {
- cursor: text;
- outline: none;
- box-shadow: 0 0 0 2px rgb(102, 188, 92);
- user-select: text;
-}
-
-/* stylelint-disable-next-line selector-max-id */
-#app {
- height: 100vh;
-}
-
-
-.luna-page {
- height: 100%;
-}
diff --git a/packages/rax-simulator-renderer/src/renderer.ts b/packages/rax-simulator-renderer/src/renderer.ts
deleted file mode 100644
index 64ec2d2c69..0000000000
--- a/packages/rax-simulator-renderer/src/renderer.ts
+++ /dev/null
@@ -1,690 +0,0 @@
-import { BuiltinSimulatorRenderer, Component, IBaseNode, IDocumentModel } from '@alilc/lowcode-designer';
-import { IPublicTypeComponentSchema, IPublicTypeNodeSchema, IPublicTypeNpmInfo, IPublicEnumTransformStage, IPublicTypeNodeInstance, IPublicTypeProjectSchema } from '@alilc/lowcode-types';
-import { Asset, compatibleLegaoSchema, cursor, isElement, isESModule, isLowcodeProjectSchema, isComponentSchema, isPlainObject, isReactComponent, setNativeSelection } from '@alilc/lowcode-utils';
-import LowCodeRenderer from '@alilc/lowcode-rax-renderer';
-import { computed, observable as obx, makeObservable, configure } from 'mobx';
-import DriverUniversal from 'driver-universal';
-import { createMemoryHistory, MemoryHistory } from 'history';
-// @ts-ignore
-import Rax, { ComponentType, createElement, render as raxRender, shared } from 'rax';
-import Leaf from './builtin-components/leaf';
-import Slot from './builtin-components/slot';
-import { host } from './host';
-import SimulatorRendererView from './renderer-view';
-import { raxFindDOMNodes } from './utils/find-dom-nodes';
-import { getClientRects } from './utils/get-client-rects';
-import loader from './utils/loader';
-import { parseQuery, withQueryParams } from './utils/url';
-import { IEventBus, createModuleEventBus } from '@alilc/lowcode-editor-core';
-
-configure({ enforceActions: 'never' });
-const { Instance } = shared;
-
-export interface LibraryMap {
- [key: string]: string;
-}
-
-const SYMBOL_VNID = Symbol('_LCNodeId');
-const SYMBOL_VDID = Symbol('_LCDocId');
-
-const INTERNAL = '_internal';
-
-function accessLibrary(library: string | object) {
- if (typeof library !== 'string') {
- return library;
- }
-
- return (window as any)[library];
-}
-
-// Slot/Leaf and Fragment|FunctionComponent polyfill(ref)
-
-const builtinComponents = {
- Slot,
- Leaf,
-};
-
-function buildComponents(
- libraryMap: LibraryMap,
- componentsMap: { [componentName: string]: IPublicTypeNpmInfo | ComponentType | IPublicTypeComponentSchema },
- createComponent: (schema: IPublicTypeProjectSchema) => Component | null,
-) {
- const components: any = {
- ...builtinComponents,
- };
- Object.keys(componentsMap).forEach((componentName) => {
- let component = componentsMap[componentName];
- if (component && (isLowcodeProjectSchema(component) || isComponentSchema(component))) {
- if (isComponentSchema(component)) {
- components[componentName] = createComponent({
- version: '',
- componentsMap: [],
- componentsTree: [component],
- });
- } else {
- components[componentName] = createComponent(component);
- }
- } else if (isReactComponent(component)) {
- components[componentName] = component;
- } else {
- component = findComponent(libraryMap, componentName, component as IPublicTypeNpmInfo);
- if (component) {
- components[componentName] = component;
- }
- }
- });
- return components;
-}
-
-let REACT_KEY = '';
-function cacheReactKey(el: Element): Element {
- if (REACT_KEY !== '') {
- return el;
- }
- // react17 采用 __reactFiber 开头
- REACT_KEY = Object.keys(el).find(
- (key) => key.startsWith('__reactInternalInstance$') || key.startsWith('__reactFiber$'),
- ) || '';
- if (!REACT_KEY && (el as HTMLElement).parentElement) {
- return cacheReactKey((el as HTMLElement).parentElement!);
- }
- return el;
-}
-
-function checkInstanceMounted(instance: any): boolean {
- if (isElement(instance)) {
- return instance.parentElement != null;
- }
- return true;
-}
-
-function isValidDesignModeRaxComponentInstance(
- raxComponentInst: any,
-): raxComponentInst is {
- props: {
- _leaf: Exclude['node'], null | undefined>;
- };
-} {
- const leaf = raxComponentInst?.props?._leaf;
- return leaf && typeof leaf === 'object' && leaf.isNode;
-}
-
-export class DocumentInstance {
- private instancesMap = new Map();
-
- private emitter: IEventBus = createModuleEventBus('DocumentInstance');
-
- get schema(): any {
- return this.document.export(IPublicEnumTransformStage.Render);
- }
-
- constructor(readonly container: SimulatorRendererContainer, readonly document: IDocumentModel) {
- makeObservable(this);
- }
-
- @computed get suspended(): any {
- return false;
- }
-
- @computed get scope(): any {
- return null;
- }
-
- get path(): string {
- return `/${ this.document.fileName}`;
- }
-
- get id() {
- return this.document.id;
- }
-
- private unmountIntance(id: string, instance: any) {
- const instances = this.instancesMap.get(id);
- if (instances) {
- const i = instances.indexOf(instance);
- if (i > -1) {
- instances.splice(i, 1);
- host.setInstance(this.document.id, id, instances);
- }
- }
- }
-
- refresh() {
- this.emitter.emit('rerender', { shouldRemount: true });
- }
-
- onReRender(fn: () => void) {
- this.emitter.on('rerender', fn);
- return () => {
- this.emitter.removeListener('renderer', fn);
- };
- }
-
- mountInstance(id: string, instance: any) {
- const docId = this.document.id;
- const { instancesMap } = this;
- if (instance == null) {
- let instances = this.instancesMap.get(id);
- if (instances) {
- instances = instances.filter(checkInstanceMounted);
- if (instances.length > 0) {
- instancesMap.set(id, instances);
- host.setInstance(this.document.id, id, instances);
- } else {
- instancesMap.delete(id);
- host.setInstance(this.document.id, id, null);
- }
- }
- return;
- }
- const unmountIntance = this.unmountIntance.bind(this);
- const origId = (instance as any)[SYMBOL_VNID];
- if (origId && origId !== id) {
- // 另外一个节点的 instance 在此被复用了,需要从原来地方卸载
- unmountIntance(origId, instance);
- }
- if (isElement(instance)) {
- cacheReactKey(instance);
- } else if (origId !== id) {
- // 涵盖 origId == null || origId !== id 的情况
- let origUnmount: any = instance.componentWillUnmount;
- if (origUnmount && origUnmount.origUnmount) {
- origUnmount = origUnmount.origUnmount;
- }
- // hack! delete instance from map
- const newUnmount = function (this: any) {
- unmountIntance(id, instance);
- origUnmount && origUnmount.call(this);
- };
- (newUnmount as any).origUnmount = origUnmount;
- instance.componentWillUnmount = newUnmount;
- }
-
- (instance as any)[SYMBOL_VNID] = id;
- (instance as any)[SYMBOL_VDID] = docId;
- let instances = this.instancesMap.get(id);
- if (instances) {
- const l = instances.length;
- instances = instances.filter(checkInstanceMounted);
- let updated = instances.length !== l;
- if (!instances.includes(instance)) {
- instances.push(instance);
- updated = true;
- }
- if (!updated) {
- return;
- }
- } else {
- instances = [instance];
- }
- instancesMap.set(id, instances);
- host.setInstance(this.document.id, id, instances);
- }
-
- mountContext(docId: string, id: string, ctx: object) {
- // this.ctxMap.set(id, ctx);
- }
-
- getComponentInstances(id: string): any[] | null {
- return this.instancesMap.get(id) || null;
- }
-
- getNode(id: string): IBaseNode | null {
- return this.document.getNode(id);
- }
-}
-
-export class SimulatorRendererContainer implements BuiltinSimulatorRenderer {
- readonly isSimulatorRenderer = true;
- private dispose?: () => void;
- readonly history: MemoryHistory;
-
- private emitter: IEventBus = createModuleEventBus('SimulatorRendererContainer');
-
- @obx.ref private _documentInstances: DocumentInstance[] = [];
- get documentInstances() {
- return this._documentInstances;
- }
-
- get currentDocumentInstance() {
- return this._documentInstances.find((item) => item.id === host.project.currentDocument?.id);
- }
-
- constructor() {
- this.dispose = host.connect(this, () => {
- // sync layout config
- this._layout = host.project.get('config').layout;
- // todo: split with others, not all should recompute
- if (this._libraryMap !== host.libraryMap || this._componentsMap !== host.designer.componentsMap) {
- this._libraryMap = host.libraryMap || {};
- this._componentsMap = host.designer.componentsMap;
- this.buildComponents();
- }
-
- // sync designMode
- this._designMode = host.designMode;
-
- this._locale = host.locale;
-
- // sync requestHandlersMap
- this._requestHandlersMap = host.requestHandlersMap;
-
- // sync device
- this._device = host.device;
-
- this.emitter.emit('layoutChange');
- });
- const documentInstanceMap = new Map();
- let initialEntry = '/';
- let firstRun = true;
- host.autorun(() => {
- this._documentInstances = host.project.documents.map((doc) => {
- let inst = documentInstanceMap.get(doc.id);
- if (!inst) {
- inst = new DocumentInstance(this, doc);
- documentInstanceMap.set(doc.id, inst);
- }
- return inst;
- });
-
- const path = host.project.currentDocument ? documentInstanceMap.get(host.project.currentDocument.id)!.path : '/';
- if (firstRun) {
- initialEntry = path;
- firstRun = false;
- } else {
- if (this.history.location.pathname !== path) {
- this.history.replace(path);
- }
- this.emitter.emit('layoutChange');
- }
- });
- const history = createMemoryHistory({
- initialEntries: [initialEntry],
- });
- this.history = history;
- history.listen(({ location }) => {
- host.project.open(location.pathname.slice(1));
- });
- host.componentsConsumer.consume(async (componentsAsset) => {
- if (componentsAsset) {
- await this.load(componentsAsset);
- this.buildComponents();
- }
- });
- this._appContext = {
- utils: {
- router: {
- push(path: string, params?: object) {
- history.push(withQueryParams(path, params));
- },
- replace(path: string, params?: object) {
- history.replace(withQueryParams(path, params));
- },
- back() {
- history.back();
- },
- },
- legaoBuiltins: {
- getUrlParams() {
- const { search } = history.location;
- return parseQuery(search);
- },
- },
- },
- constants: {},
- requestHandlersMap: this._requestHandlersMap,
- };
- host.injectionConsumer.consume((data) => {
- // sync utils, i18n, contants,... config
- });
- }
-
- @obx private _layout: any = null;
- @computed get layout(): any {
- // TODO: parse layout Component
- return this._layout;
- }
- set layout(value: any) {
- this._layout = value;
- }
-
- private _libraryMap: { [key: string]: string } = {};
- private buildComponents() {
- // TODO: remove this.createComponent
- this._components = buildComponents(this._libraryMap, this._componentsMap, this.createComponent.bind(this));
- }
- @obx.ref private _components: Record | null = {};
- @computed get components(): Record {
- // 根据 device 选择不同组件,进行响应式
- // 更好的做法是,根据 device 选择加载不同的组件资源,甚至是 simulatorUrl
- return this._components || {};
- }
- // context from: utils、constants、history、location、match
- @obx.ref private _appContext = {};
- @computed get context(): any {
- return this._appContext;
- }
- @obx.ref private _designMode: string = 'design';
- @computed get designMode(): any {
- return this._designMode;
- }
- @obx.ref private _device: string = 'default';
- @computed get device() {
- return this._device;
- }
- @obx.ref private _locale: string | undefined = undefined;
- @computed get locale() {
- return this._locale;
- }
- @obx.ref private _requestHandlersMap = null;
- @computed get requestHandlersMap(): any {
- return this._requestHandlersMap;
- }
- @obx.ref private _componentsMap = {};
- @computed get componentsMap(): any {
- return this._componentsMap;
- }
-
- /**
- * 加载资源
- */
- load(asset: Asset): Promise {
- return loader.load(asset);
- }
-
- async loadAsyncLibrary(asyncLibraryMap: Record) {
- }
-
- getComponent(componentName: string) {
- const paths = componentName.split('.');
- const subs: string[] = [];
-
- while (true) {
- const component = this._components?.[componentName];
- if (component) {
- return getSubComponent(component, subs);
- }
-
- const sub = paths.pop();
- if (!sub) {
- return null;
- }
- subs.unshift(sub);
- componentName = paths.join('.');
- }
- }
-
- getNodeInstance(dom: HTMLElement): IPublicTypeNodeInstance | null {
- const INTERNAL = '_internal';
- let instance: any = dom;
- if (!isElement(instance)) {
- return {
- docId: instance.props._leaf.document.id,
- nodeId: instance.props._leaf.getId(),
- instance,
- node: instance.props._leaf,
- };
- }
- instance = Instance.get(dom);
-
- let loopNum = 0; // 防止由于某种意外而导致死循环
- while (instance && instance[INTERNAL] && loopNum < 1000) {
- if (isValidDesignModeRaxComponentInstance(instance)) {
- // if (instance && SYMBOL_VNID in instance) {
- // const docId = (instance.props as any).schema.docId;
- return {
- docId: instance.props._leaf.document?.id || '',
- nodeId: instance.props._leaf.getId(),
- instance,
- node: instance.props._leaf,
- };
- }
-
- instance = getRaxVDomParentInstance(instance);
- loopNum += 1;
- }
-
- return null;
- }
-
- getClosestNodeInstance(from: any, nodeId?: string): IPublicTypeNodeInstance | null {
- const el: any = from;
- if (el) {
- // if (isElement(el)) {
- // el = cacheReactKey(el);
- // } else {
- // return getNodeInstance(el, specId);
- // }
- return this.getNodeInstance(el);
- }
- return null;
- }
-
- findDOMNodes(instance: any, selector?: string): Array | null {
- let el = instance;
- if (selector) {
- el = document.querySelector(selector);
- }
- try {
- return raxFindDOMNodes(el);
- } catch (e) {
- // ignore
- }
- if (el && el.type && el.props && el.props.componentId) {
- el = document.querySelector(`${el.type}[componentid=${el.props.componentId}]`);
- } else {
- console.error(instance);
- throw new Error('This instance may not a valid element');
- }
- return raxFindDOMNodes(el);
- }
-
- getClientRects(element: Element | Text) {
- return getClientRects(element);
- }
-
- setNativeSelection(enableFlag: boolean) {
- setNativeSelection(enableFlag);
- }
- setDraggingState(state: boolean) {
- cursor.setDragging(state);
- }
- setCopyState(state: boolean) {
- cursor.setCopy(state);
- }
- clearState() {
- cursor.release();
- }
-
- onLayoutChange(cb: () => void) {
- this.emitter.on('layoutChange', cb);
- return () => {
- this.emitter.removeListener('layoutChange', cb);
- };
- }
-
- onReRender(fn: () => void) {
- this.emitter.on('rerender', fn);
- return () => {
- this.emitter.removeListener('renderer', fn);
- };
- }
-
- rerender() {
- this.currentDocumentInstance?.refresh();
- }
-
- stopAutoRepaintNode() {
- }
-
- enableAutoRepaintNode() {
- }
-
- createComponent(schema: IPublicTypeProjectSchema): Component | null {
- const _schema: IPublicTypeProjectSchema = {
- ...schema,
- componentsTree: schema.componentsTree.map(compatibleLegaoSchema),
- };
-
- const componentsTreeSchema = _schema.componentsTree[0];
-
- if (componentsTreeSchema.componentName === 'Component' && componentsTreeSchema.css) {
- const doc = window.document;
- const s = doc.createElement('style');
- s.setAttribute('type', 'text/css');
- s.setAttribute('id', `Component-${componentsTreeSchema.id || ''}`);
- s.appendChild(doc.createTextNode(componentsTreeSchema.css || ''));
- doc.getElementsByTagName('head')[0].appendChild(s);
- }
-
- const renderer = this;
- const { componentsMap: components } = renderer;
-
- class LowCodeComp extends Rax.Component {
- render() {
- const extraProps = getLowCodeComponentProps(this.props);
- // @ts-ignore
- return createElement(LowCodeRenderer, {
- ...extraProps,
- schema: componentsTreeSchema,
- components,
- designMode: '',
- locale: renderer.locale,
- messages: _schema.i18n || {},
- device: renderer.device,
- appHelper: renderer.context,
- rendererName: 'LowCodeRenderer',
- thisRequiredInJSE: host.thisRequiredInJSE,
- customCreateElement: (Comp: any, props: any, children: any) => {
- const componentMeta = host.currentDocument?.getComponentMeta(Comp.displayName);
- if (componentMeta?.isModal) {
- return null;
- }
-
- const { __id, __designMode, ...viewProps } = props;
- // mock _leaf,减少性能开销
- const _leaf = {
- isEmpty: () => false,
- isMock: true,
- };
- viewProps._leaf = _leaf;
- return createElement(Comp, viewProps, children);
- },
- });
- }
- }
-
- return LowCodeComp;
- }
-
- private _running = false;
- run() {
- if (this._running) {
- return;
- }
- this._running = true;
- const containerId = 'app';
- let container = document.getElementById(containerId);
- if (!container) {
- container = document.createElement('div');
- document.body.appendChild(container);
- container.id = containerId;
- }
-
- // ==== compatiable vision
- document.documentElement.classList.add('engine-page');
- document.body.classList.add('engine-document'); // important! Stylesheet.invoke depends
-
- raxRender(createElement(SimulatorRendererView, {
- rendererContainer: this,
- }), container, {
- driver: DriverUniversal,
- });
- host.project.setRendererReady(this);
- }
-}
-
-function getSubComponent(library: any, paths: string[]) {
- const l = paths.length;
- if (l < 1 || !library) {
- return library;
- }
- let i = 0;
- let component: any;
- while (i < l) {
- const key = paths[i]!;
- let ex: any;
- try {
- component = library[key];
- } catch (e) {
- ex = e;
- component = null;
- }
- if (i === 0 && component == null && key === 'default') {
- if (ex) {
- return l === 1 ? library : null;
- }
- component = library;
- } else if (component == null) {
- return null;
- }
- library = component;
- i++;
- }
- return component;
-}
-
-function findComponent(libraryMap: LibraryMap, componentName: string, npm?: IPublicTypeNpmInfo) {
- if (!npm) {
- return accessLibrary(componentName);
- }
- // libraryName the key access to global
- // export { exportName } from xxx exportName === global.libraryName.exportName
- // export exportName from xxx exportName === global.libraryName.default || global.libraryName
- // export { exportName as componentName } from package
- // if exportName == null exportName === componentName;
- // const componentName = exportName.subName, if exportName empty subName donot use
- const exportName = npm.exportName || npm.componentName || componentName;
- const libraryName = libraryMap[npm.package] || exportName;
- const library = accessLibrary(libraryName);
- const paths = npm.exportName && npm.subName ? npm.subName.split('.') : [];
- if (npm.destructuring) {
- paths.unshift(exportName);
- } else if (isESModule(library)) {
- paths.unshift('default');
- }
- return getSubComponent(library, paths);
-}
-
-function getLowCodeComponentProps(props: any) {
- if (!props || !isPlainObject(props)) {
- return props;
- }
- const newProps: any = {};
- Object.keys(props).forEach(k => {
- if (['children', 'componentId', '__designMode', '_componentName', '_leaf'].includes(k)) {
- return;
- }
- newProps[k] = props[k];
- });
- return newProps;
-}
-
-/**
- * 获取 Rax 里面 VDOM 的上一级的实例
- * 注意:Rax 的 development 的包是带有 __parentInstance,
- * 但是 production 的包 __parentInstance 会被压缩掉,
- * 所以这里遍历下其中的所有值,尝试找到有 _internal 的那个(别的值不会带有这个属性的)
- */
-function getRaxVDomParentInstance(instance: { _internal: any }) {
- const internalInstance = instance._internal;
- return internalInstance.__parentInstance ||
- Object.values(internalInstance).find(v => (
- v !== null &&
- v !== instance &&
- typeof v === 'object' &&
- typeof (v as {_internal: unknown})._internal === 'object'
- ));
-}
-
-export default new SimulatorRendererContainer();
diff --git a/packages/rax-simulator-renderer/src/utils/create-defer.ts b/packages/rax-simulator-renderer/src/utils/create-defer.ts
deleted file mode 100644
index e7997365a0..0000000000
--- a/packages/rax-simulator-renderer/src/utils/create-defer.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-export interface Defer {
- resolve(value?: T | PromiseLike): void;
- reject(reason?: any): void;
- promise(): Promise;
-}
-
-export function createDefer(): Defer {
- const r: any = {};
- const promise = new Promise((resolve, reject) => {
- r.resolve = resolve;
- r.reject = reject;
- });
-
- r.promise = () => promise;
-
- return r;
-}
diff --git a/packages/rax-simulator-renderer/src/utils/find-dom-nodes.ts b/packages/rax-simulator-renderer/src/utils/find-dom-nodes.ts
deleted file mode 100644
index 106af2efac..0000000000
--- a/packages/rax-simulator-renderer/src/utils/find-dom-nodes.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-import { isElement } from '@alilc/lowcode-utils';
-import findDOMNode from 'rax-find-dom-node';
-// import { isDOMNode } from './is-dom-node';
-
-export function raxFindDOMNodes(instance: any): Array | null {
- if (!instance) {
- return null;
- }
- if (isElement(instance)) {
- return [instance];
- }
- // eslint-disable-next-line react/no-find-dom-node
- const result = findDOMNode(instance);
- if (Array.isArray(result)) {
- return result;
- }
- return [result];
-}
diff --git a/packages/rax-simulator-renderer/src/utils/get-client-rects.ts b/packages/rax-simulator-renderer/src/utils/get-client-rects.ts
deleted file mode 100644
index dd13aba81e..0000000000
--- a/packages/rax-simulator-renderer/src/utils/get-client-rects.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-import { isElement } from '@alilc/lowcode-utils';
-
-// a range for test TextNode clientRect
-const cycleRange = document.createRange();
-
-export function getClientRects(node: Element | Text) {
- if (isElement(node)) {
- return [node.getBoundingClientRect()];
- }
-
- cycleRange.selectNode(node);
- return Array.from(cycleRange.getClientRects());
-}
diff --git a/packages/rax-simulator-renderer/src/utils/get-closest-node-instance.ts b/packages/rax-simulator-renderer/src/utils/get-closest-node-instance.ts
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/packages/rax-simulator-renderer/src/utils/get-device-view.ts b/packages/rax-simulator-renderer/src/utils/get-device-view.ts
deleted file mode 100644
index 9005562599..0000000000
--- a/packages/rax-simulator-renderer/src/utils/get-device-view.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-function ucfirst(s: string) {
- return s.charAt(0).toUpperCase() + s.substring(1);
-}
-function getDeviceView(view: any, device: string, mode: string) {
- if (!view || typeof view === 'string') {
- return view;
- }
-
- // compatible vision Mobile | Preview
- device = ucfirst(device);
- if (device === 'Mobile' && view.hasOwnProperty(device)) {
- view = view[device];
- }
- mode = ucfirst(mode);
- if (mode === 'Preview' && view.hasOwnProperty(mode)) {
- view = view[mode];
- }
- return view;
-}
-
-export default {
- getDeviceView,
-};
diff --git a/packages/rax-simulator-renderer/src/utils/is-dom-node.ts b/packages/rax-simulator-renderer/src/utils/is-dom-node.ts
deleted file mode 100644
index bfbeb79c1f..0000000000
--- a/packages/rax-simulator-renderer/src/utils/is-dom-node.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-export function isDOMNode(node: any): node is Element | Text {
- if (!node) return false;
- return node.nodeType && (node.nodeType === Node.ELEMENT_NODE || node.nodeType === Node.TEXT_NODE);
-}
diff --git a/packages/rax-simulator-renderer/src/utils/loader.ts b/packages/rax-simulator-renderer/src/utils/loader.ts
deleted file mode 100644
index 436e51c441..0000000000
--- a/packages/rax-simulator-renderer/src/utils/loader.ts
+++ /dev/null
@@ -1,114 +0,0 @@
-import { load, evaluate } from './script';
-import StylePoint from './style';
-import {
- Asset,
- AssetLevel,
- AssetLevels,
- AssetType,
- AssetList,
- isAssetBundle,
- isAssetItem,
- assetItem,
- AssetItem,
- isCSSUrl,
-} from '@alilc/lowcode-utils';
-
-function parseAssetList(scripts: any, styles: any, assets: AssetList, level?: AssetLevel) {
- for (const asset of assets) {
- parseAsset(scripts, styles, asset, level);
- }
-}
-
-function parseAsset(scripts: any, styles: any, asset: Asset | undefined | null, level?: AssetLevel) {
- if (!asset) {
- return;
- }
- if (Array.isArray(asset)) {
- return parseAssetList(scripts, styles, asset, level);
- }
-
- if (isAssetBundle(asset)) {
- if (asset.assets) {
- if (Array.isArray(asset.assets)) {
- parseAssetList(scripts, styles, asset.assets, asset.level || level);
- } else {
- parseAsset(scripts, styles, asset.assets, asset.level || level);
- }
- return;
- }
- return;
- }
-
- if (!isAssetItem(asset)) {
- asset = assetItem(isCSSUrl(asset) ? AssetType.CSSUrl : AssetType.JSUrl, asset, level)!;
- }
-
- let lv = asset.level || level;
-
- if (!lv || AssetLevel[lv] == null) {
- lv = AssetLevel.App;
- }
-
- asset.level = lv;
- if (asset.type === AssetType.CSSUrl || asset.type == AssetType.CSSText) {
- styles[lv].push(asset);
- } else {
- scripts[lv].push(asset);
- }
-}
-
-export class AssetLoader {
- async load(asset: Asset) {
- const styles: any = {};
- const scripts: any = {};
- AssetLevels.forEach(lv => {
- styles[lv] = [];
- scripts[lv] = [];
- });
- parseAsset(scripts, styles, asset);
- const styleQueue: AssetItem[] = styles[AssetLevel.Environment].concat(
- styles[AssetLevel.Library],
- styles[AssetLevel.Theme],
- styles[AssetLevel.Runtime],
- styles[AssetLevel.App],
- );
- const scriptQueue: AssetItem[] = scripts[AssetLevel.Environment].concat(
- scripts[AssetLevel.Library],
- scripts[AssetLevel.Theme],
- scripts[AssetLevel.Runtime],
- scripts[AssetLevel.App],
- );
- await Promise.all(
- styleQueue.map(({ content, level, type, id }) => this.loadStyle(content, level!, type === AssetType.CSSUrl, id)),
- );
- await Promise.all(scriptQueue.map(({ content, type }) => this.loadScript(content, type === AssetType.JSUrl)));
- }
-
- private stylePoints = new Map();
-
- private loadStyle(content: string | undefined | null, level: AssetLevel, isUrl?: boolean, id?: string) {
- if (!content) {
- return;
- }
- let point: StylePoint | undefined;
- if (id) {
- point = this.stylePoints.get(id);
- if (!point) {
- point = new StylePoint(level, id);
- this.stylePoints.set(id, point);
- }
- } else {
- point = new StylePoint(level);
- }
- return isUrl ? point.applyUrl(content) : point.applyText(content);
- }
-
- private loadScript(content: string | undefined | null, isUrl?: boolean) {
- if (!content) {
- return;
- }
- return isUrl ? load(content) : evaluate(content);
- }
-}
-
-export default new AssetLoader();
diff --git a/packages/rax-simulator-renderer/src/utils/script.ts b/packages/rax-simulator-renderer/src/utils/script.ts
deleted file mode 100644
index 81841ff6d2..0000000000
--- a/packages/rax-simulator-renderer/src/utils/script.ts
+++ /dev/null
@@ -1,54 +0,0 @@
-import { createDefer } from './create-defer';
-
-export function evaluate(script: string) {
- const scriptEl = document.createElement('script');
- scriptEl.text = script;
- document.head.appendChild(scriptEl);
- document.head.removeChild(scriptEl);
-}
-
-export function load(url: string) {
- const node: any = document.createElement('script');
-
- // node.setAttribute('crossorigin', 'anonymous');
-
- node.onload = onload;
- node.onerror = onload;
-
- const i = createDefer();
-
- function onload(e: any) {
- node.onload = null;
- node.onerror = null;
- if (e.type === 'load') {
- i.resolve();
- } else {
- i.reject();
- }
- // document.head.removeChild(node);
- // node = null;
- }
-
- // node.async = true;
- node.src = url;
-
- document.head.appendChild(node);
-
- return i.promise();
-}
-
-export function evaluateExpression(expr: string) {
- // eslint-disable-next-line no-new-func
- const fn = new Function(expr);
- return fn();
-}
-
-export function newFunction(args: string, code: string) {
- try {
- // eslint-disable-next-line no-new-func
- return new Function(args, code);
- } catch (e) {
- console.warn('Caught error, Cant init func');
- return null;
- }
-}
diff --git a/packages/rax-simulator-renderer/src/utils/style.ts b/packages/rax-simulator-renderer/src/utils/style.ts
deleted file mode 100644
index 91dbbc6345..0000000000
--- a/packages/rax-simulator-renderer/src/utils/style.ts
+++ /dev/null
@@ -1,75 +0,0 @@
-import { createDefer } from './create-defer';
-
-export default class StylePoint {
- private lastContent: string | undefined;
-
- private lastUrl: string | undefined;
-
- private placeholder: Element | Text;
-
- constructor(readonly level: number, readonly id?: string) {
- let placeholder: any;
- if (id) {
- placeholder = document.head.querySelector(`style[data-id="${id}"]`);
- }
- if (!placeholder) {
- placeholder = document.createTextNode('');
- const meta = document.head.querySelector(`meta[level="${level}"]`);
- if (meta) {
- document.head.insertBefore(placeholder, meta);
- } else {
- document.head.appendChild(placeholder);
- }
- }
- this.placeholder = placeholder;
- }
-
- applyText(content: string) {
- if (this.lastContent === content) {
- return;
- }
- this.lastContent = content;
- this.lastUrl = undefined;
- const element = document.createElement('style');
- element.setAttribute('type', 'text/css');
- if (this.id) {
- element.setAttribute('data-id', this.id);
- }
- element.appendChild(document.createTextNode(content));
- document.head.insertBefore(element, this.placeholder.parentNode === document.head ? this.placeholder.nextSibling : null);
- document.head.removeChild(this.placeholder);
- this.placeholder = element;
- }
-
- applyUrl(url: string) {
- if (this.lastUrl === url) {
- return;
- }
- this.lastContent = undefined;
- this.lastUrl = url;
- const element = document.createElement('link');
- element.onload = onload;
- element.onerror = onload;
-
- const i = createDefer();
- function onload(e: any) {
- element.onload = null;
- element.onerror = null;
- if (e.type === 'load') {
- i.resolve();
- } else {
- i.reject();
- }
- }
-
- element.href = url;
- element.rel = 'stylesheet';
- if (this.id) {
- element.setAttribute('data-id', this.id);
- }
- document.head.insertBefore(element, this.placeholder.parentNode === document.head ? this.placeholder.nextSibling : null);
- document.head.removeChild(this.placeholder);
- this.placeholder = element;
- return i.promise();
- }
-}
diff --git a/packages/rax-simulator-renderer/src/utils/url.ts b/packages/rax-simulator-renderer/src/utils/url.ts
deleted file mode 100644
index d720323b3a..0000000000
--- a/packages/rax-simulator-renderer/src/utils/url.ts
+++ /dev/null
@@ -1,74 +0,0 @@
-/**
- * Parse queryString
- * @param {String} str '?q=query&b=test'
- * @return {Object}
- */
-export function parseQuery(str: string): object {
- const ret: any = {};
-
- if (typeof str !== 'string') {
- return ret;
- }
-
- const s = str.trim().replace(/^(\?|#|&)/, '');
-
- if (!s) {
- return ret;
- }
-
- s.split('&').forEach((param) => {
- const parts = param.replace(/\+/g, ' ').split('=');
- let key = parts.shift()!;
- let val: any = parts.length > 0 ? parts.join('=') : undefined;
-
- key = decodeURIComponent(key);
-
- val = val === undefined ? null : decodeURIComponent(val);
-
- if (ret[key] === undefined) {
- ret[key] = val;
- } else if (Array.isArray(ret[key])) {
- ret[key].push(val);
- } else {
- ret[key] = [ret[key], val];
- }
- });
-
- return ret;
-}
-
-/**
- * Stringify object to query parammeters
- * @param {Object} obj
- * @return {String}
- */
-export function stringifyQuery(obj: any): string {
- const param: string[] = [];
- Object.keys(obj).forEach((key) => {
- let value = obj[key];
- if (value && typeof value === 'object') {
- value = JSON.stringify(value);
- }
- param.push(`${encodeURIComponent(key)}=${encodeURIComponent(value)}`);
- });
- return param.join('&');
-}
-
-export function uriEncode(uri: string) {
- return encodeURIComponent(uri);
-}
-
-export function uriDecode(uri: string) {
- return decodeURIComponent(uri);
-}
-
-export function withQueryParams(url: string, params?: object) {
- const queryStr = params ? stringifyQuery(params) : '';
- if (queryStr === '') {
- return url;
- }
- const urlSplit = url.split('#');
- const hash = urlSplit[1] ? `#${urlSplit[1]}` : '';
- const urlWithoutHash = urlSplit[0];
- return `${urlWithoutHash}${~urlWithoutHash.indexOf('?') ? '&' : '?'}${queryStr}${hash}`;
-}
diff --git a/packages/react-renderer/package.json b/packages/react-renderer/package.json
index 1dceb9c2e7..625801bc25 100644
--- a/packages/react-renderer/package.json
+++ b/packages/react-renderer/package.json
@@ -1,6 +1,6 @@
{
"name": "@alilc/lowcode-react-renderer",
- "version": "1.2.3",
+ "version": "1.3.2",
"description": "react renderer for ali lowcode engine",
"main": "lib/index.js",
"module": "es/index.js",
@@ -22,7 +22,7 @@
],
"dependencies": {
"@alifd/next": "^1.21.16",
- "@alilc/lowcode-renderer-core": "1.2.3"
+ "@alilc/lowcode-renderer-core": "1.3.2"
},
"devDependencies": {
"@alib/build-scripts": "^0.1.18",
diff --git a/packages/react-simulator-renderer/package.json b/packages/react-simulator-renderer/package.json
index 8935c92eef..3c3950a124 100644
--- a/packages/react-simulator-renderer/package.json
+++ b/packages/react-simulator-renderer/package.json
@@ -1,6 +1,6 @@
{
"name": "@alilc/lowcode-react-simulator-renderer",
- "version": "1.2.3",
+ "version": "1.3.2",
"description": "react simulator renderer for alibaba lowcode designer",
"main": "lib/index.js",
"module": "es/index.js",
@@ -17,10 +17,10 @@
"test:cov": "build-scripts test --config build.test.json --jest-coverage"
},
"dependencies": {
- "@alilc/lowcode-designer": "1.2.3",
- "@alilc/lowcode-react-renderer": "1.2.3",
- "@alilc/lowcode-types": "1.2.3",
- "@alilc/lowcode-utils": "1.2.3",
+ "@alilc/lowcode-designer": "1.3.2",
+ "@alilc/lowcode-react-renderer": "1.3.2",
+ "@alilc/lowcode-types": "1.3.2",
+ "@alilc/lowcode-utils": "1.3.2",
"classnames": "^2.2.6",
"mobx": "^6.3.0",
"mobx-react": "^7.2.0",
diff --git a/packages/react-simulator-renderer/src/renderer.ts b/packages/react-simulator-renderer/src/renderer.ts
index efebeda040..20f6e18c0b 100644
--- a/packages/react-simulator-renderer/src/renderer.ts
+++ b/packages/react-simulator-renderer/src/renderer.ts
@@ -614,7 +614,7 @@ function getNodeInstance(fiberNode: any, specId?: string): IPublicTypeNodeInstan
function checkInstanceMounted(instance: any): boolean {
if (isElement(instance)) {
- return instance.parentElement != null;
+ return instance.parentElement != null && window.document.contains(instance);
}
return true;
}
diff --git a/packages/renderer-core/package.json b/packages/renderer-core/package.json
index 4774ae3689..199eac1cac 100644
--- a/packages/renderer-core/package.json
+++ b/packages/renderer-core/package.json
@@ -1,6 +1,6 @@
{
"name": "@alilc/lowcode-renderer-core",
- "version": "1.2.3",
+ "version": "1.3.2",
"description": "renderer core",
"license": "MIT",
"main": "lib/index.js",
@@ -16,8 +16,8 @@
},
"dependencies": {
"@alilc/lowcode-datasource-engine": "^1.0.0",
- "@alilc/lowcode-types": "1.2.3",
- "@alilc/lowcode-utils": "1.2.3",
+ "@alilc/lowcode-types": "1.3.2",
+ "@alilc/lowcode-utils": "1.3.2",
"classnames": "^2.2.6",
"debug": "^4.1.1",
"fetch-jsonp": "^1.1.3",
@@ -32,7 +32,7 @@
"devDependencies": {
"@alib/build-scripts": "^0.1.18",
"@alifd/next": "^1.26.0",
- "@alilc/lowcode-designer": "1.2.3",
+ "@alilc/lowcode-designer": "1.3.2",
"@babel/plugin-transform-typescript": "^7.16.8",
"@testing-library/react": "^11.2.2",
"@types/classnames": "^2.2.11",
diff --git a/packages/renderer-core/src/adapter/index.ts b/packages/renderer-core/src/adapter/index.ts
index 12896b1372..7a56bc039e 100644
--- a/packages/renderer-core/src/adapter/index.ts
+++ b/packages/renderer-core/src/adapter/index.ts
@@ -2,7 +2,6 @@ import { IRuntime, IRendererModules, IGeneralConstructor } from '../types';
export enum Env {
React = 'react',
- Rax = 'rax',
}
class Adapter {
@@ -22,22 +21,22 @@ class Adapter {
initRuntime() {
const Component: IGeneralConstructor = class {
- setState() {}
- forceUpdate() {}
- render() {}
state: Readonly;
props: Readonly & Readonly<{ children?: any | undefined }>;
refs: Record;
context: Record;
- };
- const PureComponent = class {
setState() {}
forceUpdate() {}
render() {}
+ };
+ const PureComponent = class {
state: Readonly;
props: Readonly & Readonly<{ children?: any | undefined }>;
refs: Record;
context: Record;
+ setState() {}
+ forceUpdate() {}
+ render() {}
};
const createElement = () => {};
const createContext = () => {};
@@ -85,10 +84,6 @@ class Adapter {
return this.env === Env.React;
}
- isRax() {
- return this.env === Env.Rax;
- }
-
setRenderers(renderers: IRendererModules) {
this.renderers = renderers;
}
diff --git a/packages/renderer-core/src/renderer/base.tsx b/packages/renderer-core/src/renderer/base.tsx
index 9d9e88ab59..d240095604 100644
--- a/packages/renderer-core/src/renderer/base.tsx
+++ b/packages/renderer-core/src/renderer/base.tsx
@@ -4,7 +4,7 @@
import classnames from 'classnames';
import { create as createDataSourceEngine } from '@alilc/lowcode-datasource-engine/interpret';
import { IPublicTypeNodeSchema, IPublicTypeNodeData, IPublicTypeJSONValue, IPublicTypeCompositeValue } from '@alilc/lowcode-types';
-import { isI18nData, isJSExpression, isJSFunction } from '@alilc/lowcode-utils';
+import { checkPropTypes, isI18nData, isJSExpression, isJSFunction } from '@alilc/lowcode-utils';
import adapter from '../adapter';
import divFactory from '../components/Div';
import visualDomFactory from '../components/VisualDom';
@@ -21,7 +21,6 @@ import {
isFileSchema,
transformArrayToMap,
transformStringToFunction,
- checkPropTypes,
getI18n,
getFileCssName,
capitalizeFirstLetter,
diff --git a/packages/renderer-core/src/utils/common.ts b/packages/renderer-core/src/utils/common.ts
index 495744acd9..0462d358a7 100644
--- a/packages/renderer-core/src/utils/common.ts
+++ b/packages/renderer-core/src/utils/common.ts
@@ -6,16 +6,11 @@ import { isI18nData, isJSExpression } from '@alilc/lowcode-utils';
import { isEmpty } from 'lodash';
import IntlMessageFormat from 'intl-messageformat';
import pkg from '../../package.json';
-import * as ReactIs from 'react-is';
-import { default as ReactPropTypesSecret } from 'prop-types/lib/ReactPropTypesSecret';
-import { default as factoryWithTypeCheckers } from 'prop-types/factoryWithTypeCheckers';
(window as any).sdkVersion = pkg.version;
export { pick, isEqualWith as deepEqual, cloneDeep as clone, isEmpty, throttle, debounce } from 'lodash';
-const PropTypes2 = factoryWithTypeCheckers(ReactIs.isElement, true);
-
const EXPRESSION_TYPE = {
JSEXPRESSION: 'JSExpression',
JSFUNCTION: 'JSFunction',
@@ -183,31 +178,6 @@ export function transformArrayToMap(arr: any[], key: string, overwrite = true) {
return res;
}
-export function checkPropTypes(value: any, name: string, rule: any, componentName: string): boolean {
- let ruleFunction = rule;
- if (typeof rule === 'string') {
- ruleFunction = new Function(`"use strict"; const PropTypes = arguments[0]; return ${rule}`)(PropTypes2);
- }
- if (!ruleFunction || typeof ruleFunction !== 'function') {
- logger.warn('checkPropTypes should have a function type rule argument');
- return true;
- }
- const err = ruleFunction(
- {
- [name]: value,
- },
- name,
- componentName,
- 'prop',
- null,
- ReactPropTypesSecret,
- );
- if (err) {
- logger.warn(err);
- }
- return !err;
-}
-
/**
* transform string to a function
* @param str function in string form
diff --git a/packages/renderer-core/tests/adapter/adapter.test.ts b/packages/renderer-core/tests/adapter/adapter.test.ts
index a838602fbe..57d92d1d42 100644
--- a/packages/renderer-core/tests/adapter/adapter.test.ts
+++ b/packages/renderer-core/tests/adapter/adapter.test.ts
@@ -79,15 +79,10 @@ describe('test src/adapter ', () => {
});
- it('setEnv/.env/isReact/isRax works', () => {
+ it('setEnv/.env/isReact works', () => {
adapter.setEnv(Env.React);
expect(adapter.env).toBe(Env.React);
expect(adapter.isReact()).toBeTruthy();
- expect(adapter.isRax()).toBeFalsy();
- adapter.setEnv(Env.Rax);
- expect(adapter.env).toBe(Env.Rax);
- expect(adapter.isRax()).toBeTruthy();
- expect(adapter.isReact()).toBeFalsy();
});
it('setRenderers/getRenderers works', () => {
diff --git a/packages/renderer-core/tests/utils/common.test.ts b/packages/renderer-core/tests/utils/common.test.ts
index 2ee3ed4dcc..13b6908d50 100644
--- a/packages/renderer-core/tests/utils/common.test.ts
+++ b/packages/renderer-core/tests/utils/common.test.ts
@@ -1,4 +1,3 @@
-import factoryWithTypeCheckers from 'prop-types/factoryWithTypeCheckers';
import {
isSchema,
isFileSchema,
@@ -18,14 +17,9 @@ import {
parseThisRequiredExpression,
parseI18n,
parseData,
- checkPropTypes,
} from '../../src/utils/common';
import logger from '../../src/utils/logger';
-var ReactIs = require('react-is');
-
-const PropTypes = factoryWithTypeCheckers(ReactIs.isElement, true);
-
describe('test isSchema', () => {
it('should be false when empty value is passed', () => {
expect(isSchema(null)).toBeFalsy();
@@ -283,7 +277,7 @@ describe('test capitalizeFirstLetter ', () => {
describe('test forEach ', () => {
it('should work', () => {
const mockFn = jest.fn();
-
+
forEach(null, mockFn);
expect(mockFn).toBeCalledTimes(0);
@@ -298,7 +292,7 @@ describe('test forEach ', () => {
forEach({ a: 1, b: 2, c: 3 }, mockFn);
expect(mockFn).toBeCalledTimes(3);
-
+
const mockFn2 = jest.fn();
forEach({ a: 1 }, mockFn2, { b: 'bbb' });
expect(mockFn2).toHaveBeenCalledWith(1, 'a');
@@ -467,36 +461,3 @@ describe('test parseData ', () => {
});
});
-
-describe('checkPropTypes', () => {
- it('should validate correctly with valid prop type', () => {
- expect(checkPropTypes(123, 'age', PropTypes.number, 'TestComponent')).toBe(true);
- expect(checkPropTypes('123', 'age', PropTypes.string, 'TestComponent')).toBe(true);
- });
-
- it('should log a warning and return false with invalid prop type', () => {
- expect(checkPropTypes(123, 'age', PropTypes.string, 'TestComponent')).toBe(false);
- expect(checkPropTypes('123', 'age', PropTypes.number, 'TestComponent')).toBe(false);
- });
-
- it('should handle custom rule functions correctly', () => {
- const customRule = (props, propName) => {
- if (props[propName] !== 123) {
- return new Error('Invalid value');
- }
- };
- const result = checkPropTypes(123, 'customProp', customRule, 'TestComponent');
- expect(result).toBe(true);
- });
-
-
- it('should interpret and validate a rule given as a string', () => {
- const result = checkPropTypes(123, 'age', 'PropTypes.number', 'TestComponent');
- expect(result).toBe(true);
- });
-
- it('should log a warning for invalid rule type', () => {
- const result = checkPropTypes(123, 'age', 123, 'TestComponent');
- expect(result).toBe(true);
- });
-});
\ No newline at end of file
diff --git a/packages/shell/package.json b/packages/shell/package.json
index 2ebf14bbc2..c2b62e2270 100644
--- a/packages/shell/package.json
+++ b/packages/shell/package.json
@@ -1,6 +1,6 @@
{
"name": "@alilc/lowcode-shell",
- "version": "1.2.3",
+ "version": "1.3.2",
"description": "Shell Layer for AliLowCodeEngine",
"main": "lib/index.js",
"module": "es/index.js",
@@ -13,12 +13,12 @@
},
"license": "MIT",
"dependencies": {
- "@alilc/lowcode-designer": "1.2.3",
- "@alilc/lowcode-editor-core": "1.2.3",
- "@alilc/lowcode-editor-skeleton": "1.2.3",
- "@alilc/lowcode-types": "1.2.3",
- "@alilc/lowcode-utils": "1.2.3",
- "@alilc/lowcode-workspace": "1.2.3",
+ "@alilc/lowcode-designer": "1.3.2",
+ "@alilc/lowcode-editor-core": "1.3.2",
+ "@alilc/lowcode-editor-skeleton": "1.3.2",
+ "@alilc/lowcode-types": "1.3.2",
+ "@alilc/lowcode-utils": "1.3.2",
+ "@alilc/lowcode-workspace": "1.3.2",
"classnames": "^2.2.6",
"enzyme": "^3.11.0",
"enzyme-adapter-react-16": "^1.15.5",
diff --git a/packages/shell/src/api/command.ts b/packages/shell/src/api/command.ts
new file mode 100644
index 0000000000..ebab4a9ff5
--- /dev/null
+++ b/packages/shell/src/api/command.ts
@@ -0,0 +1,46 @@
+import { IPublicApiCommand, IPublicModelPluginContext, IPublicTypeCommand, IPublicTypeCommandHandlerArgs, IPublicTypeListCommand } from '@alilc/lowcode-types';
+import { commandSymbol, pluginContextSymbol } from '../symbols';
+import { ICommand, ICommandOptions } from '@alilc/lowcode-editor-core';
+
+const optionsSymbol = Symbol('options');
+const commandScopeSet = new Set();
+
+export class Command implements IPublicApiCommand {
+ [commandSymbol]: ICommand;
+ [optionsSymbol]?: ICommandOptions;
+ [pluginContextSymbol]?: IPublicModelPluginContext;
+
+ constructor(innerCommand: ICommand, pluginContext?: IPublicModelPluginContext, options?: ICommandOptions) {
+ this[commandSymbol] = innerCommand;
+ this[optionsSymbol] = options;
+ this[pluginContextSymbol] = pluginContext;
+ const commandScope = options?.commandScope;
+ if (commandScope && commandScopeSet.has(commandScope)) {
+ throw new Error(`Command scope "${commandScope}" has been registered.`);
+ }
+ }
+
+ registerCommand(command: IPublicTypeCommand): void {
+ this[commandSymbol].registerCommand(command, this[optionsSymbol]);
+ }
+
+ batchExecuteCommand(commands: { name: string; args: IPublicTypeCommandHandlerArgs }[]): void {
+ this[commandSymbol].batchExecuteCommand(commands, this[pluginContextSymbol]);
+ }
+
+ executeCommand(name: string, args: IPublicTypeCommandHandlerArgs): void {
+ this[commandSymbol].executeCommand(name, args);
+ }
+
+ listCommands(): IPublicTypeListCommand[] {
+ return this[commandSymbol].listCommands();
+ }
+
+ unregisterCommand(name: string): void {
+ this[commandSymbol].unregisterCommand(name);
+ }
+
+ onCommandError(callback: (name: string, error: Error) => void): void {
+ this[commandSymbol].onCommandError(callback);
+ }
+}
diff --git a/packages/shell/src/api/common.tsx b/packages/shell/src/api/common.tsx
index a2e4d8c4fa..8ce07153ad 100644
--- a/packages/shell/src/api/common.tsx
+++ b/packages/shell/src/api/common.tsx
@@ -26,6 +26,7 @@ import {
IPublicApiCommonEditorCabin,
IPublicModelDragon,
IPublicModelSettingField,
+ IPublicTypeI18nData,
} from '@alilc/lowcode-types';
import {
SettingField as InnerSettingField,
@@ -261,6 +262,10 @@ class Utils implements IPublicApiCommonUtils {
} {
return innerCreateIntl(instance);
}
+
+ intl(data: IPublicTypeI18nData | string, params?: object): any {
+ return innerIntl(data, params);
+ }
}
class EditorCabin implements IPublicApiCommonEditorCabin {
diff --git a/packages/shell/src/api/commonUI.tsx b/packages/shell/src/api/commonUI.tsx
new file mode 100644
index 0000000000..69dd104b2a
--- /dev/null
+++ b/packages/shell/src/api/commonUI.tsx
@@ -0,0 +1,78 @@
+import { IPublicApiCommonUI, IPublicModelPluginContext, IPublicTypeContextMenuAction } from '@alilc/lowcode-types';
+import {
+ HelpTip,
+ IEditor,
+ Tip as InnerTip,
+ Title as InnerTitle,
+ } from '@alilc/lowcode-editor-core';
+import { Balloon, Breadcrumb, Button, Card, Checkbox, DatePicker, Dialog, Dropdown, Form, Icon, Input, Loading, Message, Overlay, Pagination, Radio, Search, Select, SplitButton, Step, Switch, Tab, Table, Tree, TreeSelect, Upload, Divider } from '@alifd/next';
+import { ContextMenu } from '../components/context-menu';
+import { editorSymbol } from '../symbols';
+import { ReactElement } from 'react';
+
+export class CommonUI implements IPublicApiCommonUI {
+ [editorSymbol]: IEditor;
+
+ Balloon = Balloon;
+ Breadcrumb = Breadcrumb;
+ Button = Button;
+ Card = Card;
+ Checkbox = Checkbox;
+ DatePicker = DatePicker;
+ Dialog = Dialog;
+ Dropdown = Dropdown;
+ Form = Form;
+ Icon = Icon;
+ Input = Input;
+ Loading = Loading as any;
+ Message = Message;
+ Overlay = Overlay;
+ Pagination = Pagination;
+ Radio = Radio;
+ Search = Search;
+ Select = Select;
+ SplitButton = SplitButton;
+ Step = Step;
+ Switch = Switch;
+ Tab = Tab;
+ Table = Table;
+ Tree = Tree;
+ TreeSelect = TreeSelect;
+ Upload = Upload;
+ Divider = Divider;
+
+ ContextMenu: ((props: {
+ menus: IPublicTypeContextMenuAction[];
+ children: React.ReactElement[] | React.ReactElement;
+ }) => ReactElement) & {
+ create(menus: IPublicTypeContextMenuAction[], event: MouseEvent | React.MouseEvent): void;
+ };
+
+ constructor(editor: IEditor) {
+ this[editorSymbol] = editor;
+
+ const innerContextMenu = (props: any) => {
+ const pluginContext: IPublicModelPluginContext = editor.get('pluginContext') as IPublicModelPluginContext;
+ return ;
+ };
+
+ innerContextMenu.create = (menus: IPublicTypeContextMenuAction[], event: MouseEvent) => {
+ const pluginContext: IPublicModelPluginContext = editor.get('pluginContext') as IPublicModelPluginContext;
+ return ContextMenu.create(pluginContext, menus, event);
+ };
+
+ this.ContextMenu = innerContextMenu;
+ }
+
+ get Tip() {
+ return InnerTip;
+ }
+
+ get HelpTip() {
+ return HelpTip;
+ }
+
+ get Title() {
+ return InnerTitle;
+ }
+}
diff --git a/packages/shell/src/api/index.ts b/packages/shell/src/api/index.ts
index 4114926e19..79340f6777 100644
--- a/packages/shell/src/api/index.ts
+++ b/packages/shell/src/api/index.ts
@@ -10,4 +10,6 @@ export * from './simulator-host';
export * from './skeleton';
export * from './canvas';
export * from './workspace';
-export * from './config';
\ No newline at end of file
+export * from './config';
+export * from './commonUI';
+export * from './command';
\ No newline at end of file
diff --git a/packages/shell/src/api/material.ts b/packages/shell/src/api/material.ts
index 39b21848e0..284b88fbbf 100644
--- a/packages/shell/src/api/material.ts
+++ b/packages/shell/src/api/material.ts
@@ -3,7 +3,7 @@ import {
IDesigner,
isComponentMeta,
} from '@alilc/lowcode-designer';
-import { IPublicTypeAssetsJson } from '@alilc/lowcode-utils';
+import { IPublicTypeAssetsJson, getLogger } from '@alilc/lowcode-utils';
import {
IPublicTypeComponentAction,
IPublicTypeComponentMetadata,
@@ -13,12 +13,16 @@ import {
IPublicTypeNpmInfo,
IPublicModelEditor,
IPublicTypeDisposable,
+ IPublicTypeContextMenuAction,
+ IPublicTypeContextMenuItem,
} from '@alilc/lowcode-types';
import { Workspace as InnerWorkspace } from '@alilc/lowcode-workspace';
import { editorSymbol, designerSymbol } from '../symbols';
import { ComponentMeta as ShellComponentMeta } from '../model';
import { ComponentType } from 'react';
+const logger = getLogger({ level: 'warn', bizName: 'shell-material' });
+
const innerEditorSymbol = Symbol('editor');
export class Material implements IPublicApiMaterial {
private readonly [innerEditorSymbol]: IPublicModelEditor;
@@ -29,6 +33,10 @@ export class Material implements IPublicApiMaterial {
}
const workspace: InnerWorkspace = globalContext.get('workspace');
if (workspace.isActive) {
+ if (!workspace.window.editor) {
+ logger.error('Material api 调用时机出现问题,请检查');
+ return this[innerEditorSymbol];
+ }
return workspace.window.editor;
}
@@ -190,4 +198,16 @@ export class Material implements IPublicApiMaterial {
dispose.forEach(d => d && d());
};
}
+
+ addContextMenuOption(option: IPublicTypeContextMenuAction) {
+ this[designerSymbol].contextMenuActions.addMenuAction(option);
+ }
+
+ removeContextMenuOption(name: string) {
+ this[designerSymbol].contextMenuActions.removeMenuAction(name);
+ }
+
+ adjustContextMenuLayout(fn: (actions: IPublicTypeContextMenuItem[]) => IPublicTypeContextMenuItem[]) {
+ this[designerSymbol].contextMenuActions.adjustMenuLayout(fn);
+ }
}
diff --git a/packages/shell/src/api/skeleton.ts b/packages/shell/src/api/skeleton.ts
index 07a1727dd3..c61edf95d0 100644
--- a/packages/shell/src/api/skeleton.ts
+++ b/packages/shell/src/api/skeleton.ts
@@ -154,16 +154,30 @@ export class Skeleton implements IPublicApiSkeleton {
* @param listener
* @returns
*/
- onShowPanel(listener: (...args: any[]) => void): IPublicTypeDisposable {
+ onShowPanel(listener: (paneName: string, panel: IPublicModelSkeletonItem) => void): IPublicTypeDisposable {
const { editor } = this[skeletonSymbol];
editor.eventBus.on(SkeletonEvents.PANEL_SHOW, (name: any, panel: any) => {
- // 不泄漏 skeleton
- const { skeleton, ...restPanel } = panel;
- listener(name, restPanel);
+ listener(name, new SkeletonItem(panel));
});
return () => editor.eventBus.off(SkeletonEvents.PANEL_SHOW, listener);
}
+ onDisableWidget(listener: (...args: any[]) => void): IPublicTypeDisposable {
+ const { editor } = this[skeletonSymbol];
+ editor.eventBus.on(SkeletonEvents.WIDGET_DISABLE, (name: any, panel: any) => {
+ listener(name, new SkeletonItem(panel));
+ });
+ return () => editor.eventBus.off(SkeletonEvents.WIDGET_DISABLE, listener);
+ }
+
+ onEnableWidget(listener: (...args: any[]) => void): IPublicTypeDisposable {
+ const { editor } = this[skeletonSymbol];
+ editor.eventBus.on(SkeletonEvents.WIDGET_ENABLE, (name: any, panel: any) => {
+ listener(name, new SkeletonItem(panel));
+ });
+ return () => editor.eventBus.off(SkeletonEvents.WIDGET_ENABLE, listener);
+ }
+
/**
* 监听 panel 隐藏事件
* @param listener
@@ -172,9 +186,7 @@ export class Skeleton implements IPublicApiSkeleton {
onHidePanel(listener: (...args: any[]) => void): IPublicTypeDisposable {
const { editor } = this[skeletonSymbol];
editor.eventBus.on(SkeletonEvents.PANEL_HIDE, (name: any, panel: any) => {
- // 不泄漏 skeleton
- const { skeleton, ...restPanel } = panel;
- listener(name, restPanel);
+ listener(name, new SkeletonItem(panel));
});
return () => editor.eventBus.off(SkeletonEvents.PANEL_HIDE, listener);
}
@@ -187,9 +199,7 @@ export class Skeleton implements IPublicApiSkeleton {
onShowWidget(listener: (...args: any[]) => void): IPublicTypeDisposable {
const { editor } = this[skeletonSymbol];
editor.eventBus.on(SkeletonEvents.WIDGET_SHOW, (name: any, panel: any) => {
- // 不泄漏 skeleton
- const { skeleton, ...rest } = panel;
- listener(name, rest);
+ listener(name, new SkeletonItem(panel));
});
return () => editor.eventBus.off(SkeletonEvents.WIDGET_SHOW, listener);
}
@@ -202,9 +212,7 @@ export class Skeleton implements IPublicApiSkeleton {
onHideWidget(listener: (...args: any[]) => void): IPublicTypeDisposable {
const { editor } = this[skeletonSymbol];
editor.eventBus.on(SkeletonEvents.WIDGET_HIDE, (name: any, panel: any) => {
- // 不泄漏 skeleton
- const { skeleton, ...rest } = panel;
- listener(name, rest);
+ listener(name, new SkeletonItem(panel));
});
return () => editor.eventBus.off(SkeletonEvents.WIDGET_HIDE, listener);
}
diff --git a/packages/shell/src/api/workspace.ts b/packages/shell/src/api/workspace.ts
index fd3e82fa90..f5bc79009f 100644
--- a/packages/shell/src/api/workspace.ts
+++ b/packages/shell/src/api/workspace.ts
@@ -90,7 +90,7 @@ export class Workspace implements IPublicApiWorkspace {
}
get plugins() {
- return new Plugins(this[workspaceSymbol].plugins, true);
+ return new Plugins(this[workspaceSymbol].plugins, true).toProxy();
}
get skeleton() {
diff --git a/packages/shell/src/components/context-menu.tsx b/packages/shell/src/components/context-menu.tsx
new file mode 100644
index 0000000000..8c7ab446ba
--- /dev/null
+++ b/packages/shell/src/components/context-menu.tsx
@@ -0,0 +1,72 @@
+import { createContextMenu, parseContextMenuAsReactNode, parseContextMenuProperties } from '@alilc/lowcode-utils';
+import { engineConfig } from '@alilc/lowcode-editor-core';
+import { IPublicModelPluginContext, IPublicTypeContextMenuAction } from '@alilc/lowcode-types';
+import React, { useCallback } from 'react';
+
+export function ContextMenu({ children, menus, pluginContext }: {
+ menus: IPublicTypeContextMenuAction[];
+ children: React.ReactElement[] | React.ReactElement;
+ pluginContext: IPublicModelPluginContext;
+}): React.ReactElement> {
+ const handleContextMenu = useCallback((event: React.MouseEvent) => {
+ event.preventDefault();
+ event.stopPropagation();
+
+ let destroyFn: Function | undefined;
+ const destroy = () => {
+ destroyFn?.();
+ };
+ const children: React.ReactNode[] = parseContextMenuAsReactNode(parseContextMenuProperties(menus, {
+ destroy,
+ pluginContext,
+ }), { pluginContext });
+
+ if (!children?.length) {
+ return;
+ }
+
+ destroyFn = createContextMenu(children, { event });
+ }, [menus]);
+
+ if (!engineConfig.get('enableContextMenu')) {
+ return (
+ <>{ children }>
+ );
+ }
+
+ if (!menus) {
+ return (
+ <>{ children }>
+ );
+ }
+
+ // 克隆 children 并添加 onContextMenu 事件处理器
+ const childrenWithContextMenu = React.Children.map(children, (child) =>
+ React.cloneElement(
+ child,
+ { onContextMenu: handleContextMenu },
+ ));
+
+ return (
+ <>{childrenWithContextMenu}>
+ );
+}
+
+ContextMenu.create = (pluginContext: IPublicModelPluginContext, menus: IPublicTypeContextMenuAction[], event: MouseEvent) => {
+ event.preventDefault();
+ event.stopPropagation();
+
+ const children: React.ReactNode[] = parseContextMenuAsReactNode(parseContextMenuProperties(menus, {
+ pluginContext,
+ }), {
+ pluginContext,
+ });
+
+ if (!children?.length) {
+ return;
+ }
+
+ return createContextMenu(children, {
+ event,
+ });
+};
\ No newline at end of file
diff --git a/packages/shell/src/index.ts b/packages/shell/src/index.ts
index 6f79c78bcd..fb1e7228f3 100644
--- a/packages/shell/src/index.ts
+++ b/packages/shell/src/index.ts
@@ -28,6 +28,8 @@ import {
Workspace,
SimulatorHost,
Config,
+ CommonUI,
+ Command,
} from './api';
export * from './symbols';
@@ -68,4 +70,6 @@ export {
Config,
SettingField,
SkeletonItem,
+ CommonUI,
+ Command,
};
diff --git a/packages/shell/src/model/window.ts b/packages/shell/src/model/window.ts
index 2b5e0dd8c3..1bc84e661c 100644
--- a/packages/shell/src/model/window.ts
+++ b/packages/shell/src/model/window.ts
@@ -48,8 +48,8 @@ export class Window implements IPublicModelWindow {
}
get currentEditorView() {
- if (this[windowSymbol].editorView) {
- return new EditorView(this[windowSymbol].editorView).toProxy() as any;
+ if (this[windowSymbol]._editorView) {
+ return new EditorView(this[windowSymbol]._editorView).toProxy() as any;
}
return null;
}
diff --git a/packages/shell/src/symbols.ts b/packages/shell/src/symbols.ts
index 8e2962a24f..e0f846ad36 100644
--- a/packages/shell/src/symbols.ts
+++ b/packages/shell/src/symbols.ts
@@ -39,4 +39,5 @@ export const configSymbol = Symbol('configSymbol');
export const conditionGroupSymbol = Symbol('conditionGroup');
export const editorViewSymbol = Symbol('editorView');
export const pluginContextSymbol = Symbol('pluginContext');
-export const skeletonItemSymbol = Symbol('skeletonItem');
\ No newline at end of file
+export const skeletonItemSymbol = Symbol('skeletonItem');
+export const commandSymbol = Symbol('command');
\ No newline at end of file
diff --git a/packages/types/package.json b/packages/types/package.json
index c0b8e203fe..5651d427d4 100644
--- a/packages/types/package.json
+++ b/packages/types/package.json
@@ -1,6 +1,6 @@
{
"name": "@alilc/lowcode-types",
- "version": "1.2.3",
+ "version": "1.3.2",
"description": "Types for Ali lowCode engine",
"files": [
"es",
diff --git a/packages/types/src/shell/api/command.ts b/packages/types/src/shell/api/command.ts
new file mode 100644
index 0000000000..1f8425dcef
--- /dev/null
+++ b/packages/types/src/shell/api/command.ts
@@ -0,0 +1,34 @@
+import { IPublicTypeCommand, IPublicTypeCommandHandlerArgs, IPublicTypeListCommand } from '../type';
+
+export interface IPublicApiCommand {
+
+ /**
+ * 注册一个新命令及其处理函数
+ */
+ registerCommand(command: IPublicTypeCommand): void;
+
+ /**
+ * 注销一个已存在的命令
+ */
+ unregisterCommand(name: string): void;
+
+ /**
+ * 通过名称和给定参数执行一个命令,会校验参数是否符合命令定义
+ */
+ executeCommand(name: string, args?: IPublicTypeCommandHandlerArgs): void;
+
+ /**
+ * 批量执行命令,执行完所有命令后再进行一次重绘,历史记录中只会记录一次
+ */
+ batchExecuteCommand(commands: { name: string; args?: IPublicTypeCommandHandlerArgs }[]): void;
+
+ /**
+ * 列出所有已注册的命令
+ */
+ listCommands(): IPublicTypeListCommand[];
+
+ /**
+ * 注册错误处理回调函数
+ */
+ onCommandError(callback: (name: string, error: Error) => void): void;
+}
\ No newline at end of file
diff --git a/packages/types/src/shell/api/common.ts b/packages/types/src/shell/api/common.ts
index 60fac1606d..05ef0da17f 100644
--- a/packages/types/src/shell/api/common.ts
+++ b/packages/types/src/shell/api/common.ts
@@ -1,6 +1,6 @@
import { Component, ReactNode } from 'react';
-import { IPublicTypeNodeSchema, IPublicTypeTitleContent } from '../type';
+import { IPublicTypeI18nData, IPublicTypeNodeSchema, IPublicTypeTitleContent } from '../type';
import { IPublicEnumTransitionType } from '../enum';
export interface IPublicApiCommonUtils {
@@ -69,6 +69,11 @@ export interface IPublicApiCommonUtils {
getLocale(): string;
setLocale(locale: string): void;
};
+
+ /**
+ * i18n 转换方法
+ */
+ intl(data: IPublicTypeI18nData | string, params?: object): string;
}
export interface IPublicApiCommonSkeletonCabin {
diff --git a/packages/types/src/shell/api/commonUI.ts b/packages/types/src/shell/api/commonUI.ts
new file mode 100644
index 0000000000..5ac025fcde
--- /dev/null
+++ b/packages/types/src/shell/api/commonUI.ts
@@ -0,0 +1,74 @@
+import React, { ReactElement } from 'react';
+import { IPublicTypeContextMenuAction, IPublicTypeHelpTipConfig, IPublicTypeTipConfig, IPublicTypeTitleContent } from '../type';
+import { Balloon, Breadcrumb, Button, Card, Checkbox, DatePicker, Dialog, Dropdown, Form, Icon, Input, Loading, Message, Overlay, Pagination, Radio, Search, Select, SplitButton, Step, Switch, Tab, Table, Tree, TreeSelect, Upload, Divider } from '@alifd/next';
+import { IconProps } from '@alifd/next/types/icon';
+
+export interface IPublicApiCommonUI {
+ Balloon: typeof Balloon;
+ Breadcrumb: typeof Breadcrumb;
+ Button: typeof Button;
+ Card: typeof Card;
+ Checkbox: typeof Checkbox;
+ DatePicker: typeof DatePicker;
+ Dialog: typeof Dialog;
+ Dropdown: typeof Dropdown;
+ Form: typeof Form;
+ Icon: typeof Icon;
+ Input: typeof Input;
+ Loading: typeof Loading;
+ Message: typeof Message;
+ Overlay: typeof Overlay;
+ Pagination: typeof Pagination;
+ Radio: typeof Radio;
+ Search: typeof Search;
+ Select: typeof Select;
+ SplitButton: typeof SplitButton;
+ Step: typeof Step;
+ Switch: typeof Switch;
+ Tab: typeof Tab;
+ Table: typeof Table;
+ Tree: typeof Tree;
+ TreeSelect: typeof TreeSelect;
+ Upload: typeof Upload;
+ Divider: typeof Divider;
+
+ /**
+ * Title 组件
+ */
+ get Tip(): React.ComponentClass;
+
+ /**
+ * HelpTip 组件
+ */
+ get HelpTip(): React.VFC<{
+ help: IPublicTypeHelpTipConfig;
+
+ /**
+ * 方向
+ * @default 'top'
+ */
+ direction: IPublicTypeTipConfig['direction'];
+
+ /**
+ * 大小
+ * @default 'small'
+ */
+ size: IconProps['size'];
+ }>;
+
+ /**
+ * Tip 组件
+ */
+ get Title(): React.ComponentClass<{
+ title: IPublicTypeTitleContent | undefined;
+ match?: boolean;
+ keywords?: string | null;
+ }>;
+
+ get ContextMenu(): ((props: {
+ menus: IPublicTypeContextMenuAction[];
+ children: React.ReactElement[] | React.ReactElement;
+ }) => ReactElement) & {
+ create(menus: IPublicTypeContextMenuAction[], event: MouseEvent | React.MouseEvent): void;
+ };
+}
diff --git a/packages/types/src/shell/api/index.ts b/packages/types/src/shell/api/index.ts
index 3d7d765598..8f14d8dadd 100644
--- a/packages/types/src/shell/api/index.ts
+++ b/packages/types/src/shell/api/index.ts
@@ -10,3 +10,5 @@ export * from './plugins';
export * from './logger';
export * from './canvas';
export * from './workspace';
+export * from './commonUI';
+export * from './command';
\ No newline at end of file
diff --git a/packages/types/src/shell/api/material.ts b/packages/types/src/shell/api/material.ts
index d64455eddd..89b2b39ad1 100644
--- a/packages/types/src/shell/api/material.ts
+++ b/packages/types/src/shell/api/material.ts
@@ -1,4 +1,4 @@
-import { IPublicTypeAssetsJson, IPublicTypeMetadataTransducer, IPublicTypeComponentAction, IPublicTypeNpmInfo, IPublicTypeDisposable } from '../type';
+import { IPublicTypeAssetsJson, IPublicTypeMetadataTransducer, IPublicTypeComponentAction, IPublicTypeNpmInfo, IPublicTypeDisposable, IPublicTypeContextMenuAction, IPublicTypeContextMenuItem } from '../type';
import { IPublicModelComponentMeta } from '../model';
import { ComponentType } from 'react';
@@ -15,7 +15,7 @@ export interface IPublicApiMaterial {
* set data for Assets
* @returns void
*/
- setAssets(assets: IPublicTypeAssetsJson): void;
+ setAssets(assets: IPublicTypeAssetsJson): Promise;
/**
* 获取「资产包」结构
@@ -128,4 +128,22 @@ export interface IPublicApiMaterial {
* @since v1.1.7
*/
refreshComponentMetasMap(): void;
+
+ /**
+ * 添加右键菜单项
+ * @param action
+ */
+ addContextMenuOption(action: IPublicTypeContextMenuAction): void;
+
+ /**
+ * 删除特定右键菜单项
+ * @param name
+ */
+ removeContextMenuOption(name: string): void;
+
+ /**
+ * 调整右键菜单项布局
+ * @param actions
+ */
+ adjustContextMenuLayout(fn: (actions: IPublicTypeContextMenuItem[]) => IPublicTypeContextMenuItem[]): void;
}
diff --git a/packages/types/src/shell/api/skeleton.ts b/packages/types/src/shell/api/skeleton.ts
index 9a36aa4690..1bf788e121 100644
--- a/packages/types/src/shell/api/skeleton.ts
+++ b/packages/types/src/shell/api/skeleton.ts
@@ -1,5 +1,5 @@
import { IPublicModelSkeletonItem } from '../model';
-import { IPublicTypeConfigTransducer, IPublicTypeDisposable, IPublicTypeSkeletonConfig } from '../type';
+import { IPublicTypeConfigTransducer, IPublicTypeDisposable, IPublicTypeSkeletonConfig, IPublicTypeWidgetConfigArea } from '../type';
export interface IPublicApiSkeleton {
@@ -20,6 +20,12 @@ export interface IPublicApiSkeleton {
*/
remove(config: IPublicTypeSkeletonConfig): number | undefined;
+ /**
+ * 获取某个区域下的所有面板实例
+ * @param areaName IPublicTypeWidgetConfigArea
+ */
+ getAreaItems(areaName: IPublicTypeWidgetConfigArea): IPublicModelSkeletonItem[] | undefined;
+
/**
* 获取面板实例
* @param name 面板名称
@@ -89,7 +95,7 @@ export interface IPublicApiSkeleton {
* @param listener
* @returns
*/
- onShowPanel(listener: (...args: any[]) => void): IPublicTypeDisposable;
+ onShowPanel(listener: (paneName?: string, panel?: IPublicModelSkeletonItem) => void): IPublicTypeDisposable;
/**
* 监听 Panel 实例隐藏事件
@@ -97,7 +103,19 @@ export interface IPublicApiSkeleton {
* @param listener
* @returns
*/
- onHidePanel(listener: (...args: any[]) => void): IPublicTypeDisposable;
+ onHidePanel(listener: (paneName?: string, panel?: IPublicModelSkeletonItem) => void): IPublicTypeDisposable;
+
+ /**
+ * 监听 Widget 实例 Disable 事件
+ * @param listener
+ */
+ onDisableWidget(listener: (paneName?: string, panel?: IPublicModelSkeletonItem) => void): IPublicTypeDisposable;
+
+ /**
+ * 监听 Widget 实例 Enable 事件
+ * @param listener
+ */
+ onEnableWidget(listener: (paneName?: string, panel?: IPublicModelSkeletonItem) => void): IPublicTypeDisposable;
/**
* 监听 Widget 显示事件
@@ -105,7 +123,7 @@ export interface IPublicApiSkeleton {
* @param listener
* @returns
*/
- onShowWidget(listener: (...args: any[]) => void): IPublicTypeDisposable;
+ onShowWidget(listener: (paneName?: string, panel?: IPublicModelSkeletonItem) => void): IPublicTypeDisposable;
/**
* 监听 Widget 隐藏事件
@@ -113,7 +131,7 @@ export interface IPublicApiSkeleton {
* @param listener
* @returns
*/
- onHideWidget(listener: (...args: any[]) => void): IPublicTypeDisposable;
+ onHideWidget(listener: (paneName?: string, panel?: IPublicModelSkeletonItem) => void): IPublicTypeDisposable;
/**
* 注册一个面板的配置转换器(transducer)。
diff --git a/packages/types/src/shell/api/workspace.ts b/packages/types/src/shell/api/workspace.ts
index 9e1080b31e..b6e7d84cb7 100644
--- a/packages/types/src/shell/api/workspace.ts
+++ b/packages/types/src/shell/api/workspace.ts
@@ -1,8 +1,9 @@
import { IPublicModelWindow } from '../model';
-import { IPublicApiPlugins, IPublicModelResource, IPublicResourceList, IPublicTypeDisposable, IPublicTypeResourceType } from '@alilc/lowcode-types';
+import { IPublicApiPlugins, IPublicApiSkeleton, IPublicModelResource, IPublicResourceList, IPublicTypeDisposable, IPublicTypeResourceType } from '@alilc/lowcode-types';
export interface IPublicApiWorkspace<
Plugins = IPublicApiPlugins,
+ Skeleton = IPublicApiSkeleton,
ModelWindow = IPublicModelWindow,
Resource = IPublicModelResource,
> {
@@ -15,6 +16,8 @@ export interface IPublicApiWorkspace<
plugins: Plugins;
+ skeleton: Skeleton;
+
/** 当前设计器的编辑窗口 */
windows: ModelWindow[];
diff --git a/packages/types/src/shell/enum/context-menu.ts b/packages/types/src/shell/enum/context-menu.ts
new file mode 100644
index 0000000000..fd209b1974
--- /dev/null
+++ b/packages/types/src/shell/enum/context-menu.ts
@@ -0,0 +1,7 @@
+export enum IPublicEnumContextMenuType {
+ SEPARATOR = 'separator',
+ // 'menuItem'
+ MENU_ITEM = 'menuItem',
+ // 'nodeTree'
+ NODE_TREE = 'nodeTree',
+}
\ No newline at end of file
diff --git a/packages/types/src/shell/enum/index.ts b/packages/types/src/shell/enum/index.ts
index f3d5580119..13282d0f2b 100644
--- a/packages/types/src/shell/enum/index.ts
+++ b/packages/types/src/shell/enum/index.ts
@@ -3,4 +3,5 @@ export * from './transition-type';
export * from './transform-stage';
export * from './drag-object-type';
export * from './prop-value-changed-type';
-export * from './plugin-register-level';
\ No newline at end of file
+export * from './plugin-register-level';
+export * from './context-menu';
\ No newline at end of file
diff --git a/packages/types/src/shell/model/plugin-context.ts b/packages/types/src/shell/model/plugin-context.ts
index 35e7e38af3..d4d715e96b 100644
--- a/packages/types/src/shell/model/plugin-context.ts
+++ b/packages/types/src/shell/model/plugin-context.ts
@@ -11,6 +11,8 @@ import {
IPluginPreferenceMananger,
IPublicApiPlugins,
IPublicApiWorkspace,
+ IPublicApiCommonUI,
+ IPublicApiCommand,
} from '../api';
import { IPublicEnumPluginRegisterLevel } from '../enum';
import { IPublicModelEngineConfig, IPublicModelWindow } from './';
@@ -102,6 +104,14 @@ export interface IPublicModelPluginContext {
*/
get workspace(): IPublicApiWorkspace;
+ /**
+ * commonUI API
+ * @tutorial https://lowcode-engine.cn/site/docs/api/commonUI
+ */
+ get commonUI(): IPublicApiCommonUI;
+
+ get command(): IPublicApiCommand;
+
/**
* 插件注册层级
* @since v1.1.7
diff --git a/packages/types/src/shell/type/command.ts b/packages/types/src/shell/type/command.ts
new file mode 100644
index 0000000000..0f301bd658
--- /dev/null
+++ b/packages/types/src/shell/type/command.ts
@@ -0,0 +1,59 @@
+import { IPublicTypePropType } from './prop-types';
+
+// 定义命令处理函数的参数类型
+export interface IPublicTypeCommandHandlerArgs {
+ [key: string]: any;
+}
+
+// 定义命令参数的接口
+export interface IPublicTypeCommandParameter {
+
+ /**
+ * 参数名称
+ */
+ name: string;
+
+ /**
+ * 参数类型或详细类型描述
+ */
+ propType: string | IPublicTypePropType;
+
+ /**
+ * 参数描述
+ */
+ description: string;
+
+ /**
+ * 参数默认值(可选)
+ */
+ defaultValue?: any;
+}
+
+// 定义单个命令的接口
+export interface IPublicTypeCommand {
+
+ /**
+ * 命令名称
+ * 命名规则:commandName
+ * 使用规则:commandScope:commandName (commandScope 在插件 meta 中定义,用于区分不同插件的命令)
+ */
+ name: string;
+
+ /**
+ * 命令参数
+ */
+ parameters?: IPublicTypeCommandParameter[];
+
+ /**
+ * 命令描述
+ */
+ description?: string;
+
+ /**
+ * 命令处理函数
+ */
+ handler: (args: any) => void;
+}
+
+export interface IPublicTypeListCommand extends Pick {
+}
\ No newline at end of file
diff --git a/packages/types/src/shell/type/context-menu.ts b/packages/types/src/shell/type/context-menu.ts
new file mode 100644
index 0000000000..dd6d583c25
--- /dev/null
+++ b/packages/types/src/shell/type/context-menu.ts
@@ -0,0 +1,63 @@
+import { IPublicEnumContextMenuType } from '../enum';
+import { IPublicModelNode } from '../model';
+import { IPublicTypeI18nData } from './i8n-data';
+import { IPublicTypeHelpTipConfig } from './widget-base-config';
+
+export interface IPublicTypeContextMenuItem extends Omit {
+ disabled?: boolean;
+
+ items?: Omit[];
+}
+
+export interface IPublicTypeContextMenuAction {
+
+ /**
+ * 动作的唯一标识符
+ * Unique identifier for the action
+ */
+ name: string;
+
+ /**
+ * 显示的标题,可以是字符串或国际化数据
+ * Display title, can be a string or internationalized data
+ */
+ title?: string | IPublicTypeI18nData;
+
+ /**
+ * 菜单项类型
+ * Menu item type
+ * @see IPublicEnumContextMenuType
+ * @default IPublicEnumContextMenuType.MENU_ITEM
+ */
+ type?: IPublicEnumContextMenuType;
+
+ /**
+ * 点击时执行的动作,可选
+ * Action to execute on click, optional
+ */
+ action?: (nodes?: IPublicModelNode[], event?: MouseEvent) => void;
+
+ /**
+ * 子菜单项或生成子节点的函数,可选,仅支持两级
+ * Sub-menu items or function to generate child node, optional
+ */
+ items?: Omit[] | ((nodes?: IPublicModelNode[]) => Omit[]);
+
+ /**
+ * 显示条件函数
+ * Function to determine display condition
+ */
+ condition?: (nodes?: IPublicModelNode[]) => boolean;
+
+ /**
+ * 禁用条件函数,可选
+ * Function to determine disabled condition, optional
+ */
+ disabled?: (nodes?: IPublicModelNode[]) => boolean;
+
+ /**
+ * 帮助提示,可选
+ */
+ help?: IPublicTypeHelpTipConfig;
+}
+
diff --git a/packages/types/src/shell/type/engine-options.ts b/packages/types/src/shell/type/engine-options.ts
index f177166530..8221c4089c 100644
--- a/packages/types/src/shell/type/engine-options.ts
+++ b/packages/types/src/shell/type/engine-options.ts
@@ -41,7 +41,7 @@ export interface IPublicTypeEngineOptions {
/**
* 渲染器类型,默认值:'react'
*/
- renderEnv?: 'react' | 'rax' | string;
+ renderEnv?: 'react' | string;
/**
* 设备类型映射器,处理设计器与渲染器中 device 的映射
@@ -177,6 +177,18 @@ export interface IPublicTypeEngineOptions {
* 应用级设计模式下,自动打开第一个窗口
*/
enableAutoOpenFirstWindow?: boolean;
+
+ /**
+ * @default false
+ * 开启右键菜单能力
+ */
+ enableContextMenu?: boolean;
+
+ /**
+ * @default false
+ * 隐藏设计器辅助层
+ */
+ hideComponentAction?: boolean;
}
/**
diff --git a/packages/types/src/shell/type/field-extra-props.ts b/packages/types/src/shell/type/field-extra-props.ts
index 3e2df280b7..7aae7e0fe8 100644
--- a/packages/types/src/shell/type/field-extra-props.ts
+++ b/packages/types/src/shell/type/field-extra-props.ts
@@ -77,5 +77,5 @@ export interface IPublicTypeFieldExtraProps {
/**
* onChange 事件
*/
- onChange?: (value: any, field: any) => void;
+ onChange?: (value: any, field: IPublicModelSettingField) => void;
}
diff --git a/packages/types/src/shell/type/index.ts b/packages/types/src/shell/type/index.ts
index b2fd3313f9..76dd389255 100644
--- a/packages/types/src/shell/type/index.ts
+++ b/packages/types/src/shell/type/index.ts
@@ -91,4 +91,6 @@ export * from './hotkey-callback-config';
export * from './hotkey-callbacks';
export * from './scrollable';
export * from './simulator-renderer';
-export * from './config-transducer';
\ No newline at end of file
+export * from './config-transducer';
+export * from './context-menu';
+export * from './command';
\ No newline at end of file
diff --git a/packages/types/src/shell/type/plugin-meta.ts b/packages/types/src/shell/type/plugin-meta.ts
index 421e59ad0a..bf7f6212e8 100644
--- a/packages/types/src/shell/type/plugin-meta.ts
+++ b/packages/types/src/shell/type/plugin-meta.ts
@@ -1,14 +1,17 @@
import { IPublicTypePluginDeclaration } from './';
export interface IPublicTypePluginMeta {
+
/**
* define dependencies which the plugin depends on
*/
dependencies?: string[];
+
/**
* specify which engine version is compatible with the plugin
*/
engines?: {
+
/** e.g. '^1.0.0' */
lowcodeEngine?: string;
};
@@ -26,4 +29,9 @@ export interface IPublicTypePluginMeta {
* event.emit('someEventName') is actually sending event with name 'myEvent:someEventName'
*/
eventPrefix?: string;
+
+ /**
+ * 如果要使用 command 注册命令,需要在插件 meta 中定义 commandScope
+ */
+ commandScope?: string;
}
diff --git a/packages/types/src/shell/type/registered-setter.ts b/packages/types/src/shell/type/registered-setter.ts
index 85cad5a803..55a90465a8 100644
--- a/packages/types/src/shell/type/registered-setter.ts
+++ b/packages/types/src/shell/type/registered-setter.ts
@@ -1,17 +1,20 @@
+import { IPublicModelSettingField } from '../model';
import { IPublicTypeCustomView, IPublicTypeTitleContent } from './';
export interface IPublicTypeRegisteredSetter {
component: IPublicTypeCustomView;
defaultProps?: object;
title?: IPublicTypeTitleContent;
+
/**
* for MixedSetter to check this setter if available
*/
- condition?: (field: any) => boolean;
+ condition?: (field: IPublicModelSettingField) => boolean;
+
/**
* for MixedSetter to manual change to this setter
*/
- initialValue?: any | ((field: any) => any);
+ initialValue?: any | ((field: IPublicModelSettingField) => any);
recommend?: boolean;
// 标识是否为动态 setter,默认为 true
isDynamic?: boolean;
diff --git a/packages/types/src/shell/type/tip-config.ts b/packages/types/src/shell/type/tip-config.ts
index fa82ab96f1..f8b271c909 100644
--- a/packages/types/src/shell/type/tip-config.ts
+++ b/packages/types/src/shell/type/tip-config.ts
@@ -2,8 +2,20 @@ import { IPublicTypeI18nData } from '..';
import { ReactNode } from 'react';
export interface IPublicTypeTipConfig {
+
+ /**
+ * className
+ */
className?: string;
+
+ /**
+ * tip 的内容
+ */
children?: IPublicTypeI18nData | ReactNode;
theme?: string;
+
+ /**
+ * tip 的方向
+ */
direction?: 'top' | 'bottom' | 'left' | 'right';
}
diff --git a/packages/types/src/shell/type/title-config.ts b/packages/types/src/shell/type/title-config.ts
index 571502bc5e..f8de287599 100644
--- a/packages/types/src/shell/type/title-config.ts
+++ b/packages/types/src/shell/type/title-config.ts
@@ -1,26 +1,51 @@
import { ReactNode } from 'react';
-import { IPublicTypeI18nData, IPublicTypeIconType, TipContent } from './';
+import { IPublicTypeI18nData, IPublicTypeIconType, IPublicTypeTitleContent, TipContent } from './';
+
+export interface IPublicTypeTitleProps {
+
+ /**
+ * 标题内容
+ */
+ title: IPublicTypeTitleContent;
+
+ /**
+ * className
+ */
+ className?: string;
+
+ /**
+ * 点击事件
+ */
+ onClick?: () => void;
+ match?: boolean;
+ keywords?: string;
+}
/**
* 描述 props 的 setter title
*/
export interface IPublicTypeTitleConfig {
+
/**
* 文字描述
*/
label?: IPublicTypeI18nData | ReactNode;
+
/**
* hover 后的展现内容
*/
tip?: TipContent;
+
/**
* 文档链接,暂未实现
*/
docUrl?: string;
+
/**
* 图标
*/
icon?: IPublicTypeIconType;
+
/**
* CSS 类
*/
diff --git a/packages/types/src/shell/type/widget-base-config.ts b/packages/types/src/shell/type/widget-base-config.ts
index b23ba0f137..2764ce1927 100644
--- a/packages/types/src/shell/type/widget-base-config.ts
+++ b/packages/types/src/shell/type/widget-base-config.ts
@@ -1,4 +1,27 @@
-import { IPublicTypeIconType, IPublicTypeTitleContent, IPublicTypeWidgetConfigArea, TipContent } from './';
+import { ReactElement, ComponentType } from 'react';
+import { IPublicTypeI18nData, IPublicTypeIconType, IPublicTypeTitleContent, IPublicTypeWidgetConfigArea, TipContent } from './';
+
+export type IPublicTypeHelpTipConfig = string | { url?: string; content?: string | ReactElement };
+
+export interface IPublicTypePanelConfigProps extends IPublicTypePanelDockPanelProps {
+ title?: IPublicTypeTitleContent;
+ icon?: any; // 冗余字段
+ description?: string | IPublicTypeI18nData;
+ help?: IPublicTypeHelpTipConfig; // 显示问号帮助
+ hiddenWhenInit?: boolean; // when this is true, by default will be hidden
+ condition?: (widget: any) => any;
+ onInit?: (widget: any) => any;
+ onDestroy?: () => any;
+ shortcut?: string; // 只有在特定位置,可触发 toggle show
+ enableDrag?: boolean; // 是否开启通过 drag 调整 宽度
+ keepVisibleWhileDragging?: boolean; // 是否在该 panel 范围内拖拽时保持 visible 状态
+}
+
+export interface IPublicTypePanelConfig extends IPublicTypeWidgetBaseConfig {
+ type: 'Panel';
+ content?: string | ReactElement | ComponentType | IPublicTypePanelConfig[]; // as children
+ props?: IPublicTypePanelConfigProps;
+}
export interface IPublicTypeWidgetBaseConfig {
[extra: string]: any;
@@ -13,7 +36,7 @@ export interface IPublicTypeWidgetBaseConfig {
*/
area?: IPublicTypeWidgetConfigArea;
props?: Record;
- content?: any;
+ content?: string | ReactElement | ComponentType | IPublicTypePanelConfig[];
contentProps?: Record;
/**
diff --git a/packages/utils/package.json b/packages/utils/package.json
index 00efffb8d3..60605d81e7 100644
--- a/packages/utils/package.json
+++ b/packages/utils/package.json
@@ -1,6 +1,6 @@
{
"name": "@alilc/lowcode-utils",
- "version": "1.2.3",
+ "version": "1.3.2",
"description": "Utils for Ali lowCode engine",
"files": [
"lib",
@@ -14,9 +14,10 @@
},
"dependencies": {
"@alifd/next": "^1.19.16",
- "@alilc/lowcode-types": "1.2.3",
+ "@alilc/lowcode-types": "1.3.2",
"lodash": "^4.17.21",
"mobx": "^6.3.0",
+ "prop-types": "^15.8.1",
"react": "^16"
},
"devDependencies": {
diff --git a/packages/utils/src/check-prop-types.ts b/packages/utils/src/check-prop-types.ts
new file mode 100644
index 0000000000..dc9ce31ed5
--- /dev/null
+++ b/packages/utils/src/check-prop-types.ts
@@ -0,0 +1,72 @@
+import * as ReactIs from 'react-is';
+import { default as ReactPropTypesSecret } from 'prop-types/lib/ReactPropTypesSecret';
+import { default as factoryWithTypeCheckers } from 'prop-types/factoryWithTypeCheckers';
+import { IPublicTypePropType } from '@alilc/lowcode-types';
+import { isRequiredPropType } from './check-types/is-required-prop-type';
+import { Logger } from './logger';
+
+const PropTypes2 = factoryWithTypeCheckers(ReactIs.isElement, true);
+const logger = new Logger({ level: 'warn', bizName: 'utils' });
+
+export function transformPropTypesRuleToString(rule: IPublicTypePropType | string): string {
+ if (!rule) {
+ return 'PropTypes.any';
+ }
+
+ if (typeof rule === 'string') {
+ return rule.startsWith('PropTypes.') ? rule : `PropTypes.${rule}`;
+ }
+
+ if (isRequiredPropType(rule)) {
+ const { type, isRequired } = rule;
+ return `PropTypes.${type}${isRequired ? '.isRequired' : ''}`;
+ }
+
+ const { type, value } = rule;
+ switch (type) {
+ case 'oneOf':
+ return `PropTypes.oneOf([${value.map((item: any) => `"${item}"`).join(',')}])`;
+ case 'oneOfType':
+ return `PropTypes.oneOfType([${value.map((item: any) => transformPropTypesRuleToString(item)).join(', ')}])`;
+ case 'arrayOf':
+ case 'objectOf':
+ return `PropTypes.${type}(${transformPropTypesRuleToString(value)})`;
+ case 'shape':
+ case 'exact':
+ return `PropTypes.${type}({${value.map((item: any) => `${item.name}: ${transformPropTypesRuleToString(item.propType)}`).join(',')}})`;
+ default:
+ logger.error(`Unknown prop type: ${type}`);
+ }
+
+ return 'PropTypes.any';
+}
+
+export function checkPropTypes(value: any, name: string, rule: any, componentName: string): boolean {
+ let ruleFunction = rule;
+ if (typeof rule === 'object') {
+ // eslint-disable-next-line no-new-func
+ ruleFunction = new Function(`"use strict"; const PropTypes = arguments[0]; return ${transformPropTypesRuleToString(rule)}`)(PropTypes2);
+ }
+ if (typeof rule === 'string') {
+ // eslint-disable-next-line no-new-func
+ ruleFunction = new Function(`"use strict"; const PropTypes = arguments[0]; return ${transformPropTypesRuleToString(rule)}`)(PropTypes2);
+ }
+ if (!ruleFunction || typeof ruleFunction !== 'function') {
+ logger.warn('checkPropTypes should have a function type rule argument');
+ return true;
+ }
+ const err = ruleFunction(
+ {
+ [name]: value,
+ },
+ name,
+ componentName,
+ 'prop',
+ null,
+ ReactPropTypesSecret,
+ );
+ if (err) {
+ logger.warn(err);
+ }
+ return !err;
+}
diff --git a/packages/utils/src/check-types/index.ts b/packages/utils/src/check-types/index.ts
index 3155926ef2..507259b2c5 100644
--- a/packages/utils/src/check-types/index.ts
+++ b/packages/utils/src/check-types/index.ts
@@ -23,4 +23,6 @@ export * from './is-location-data';
export * from './is-setting-field';
export * from './is-lowcode-component-type';
export * from './is-lowcode-project-schema';
-export * from './is-component-schema';
\ No newline at end of file
+export * from './is-component-schema';
+export * from './is-basic-prop-type';
+export * from './is-required-prop-type';
\ No newline at end of file
diff --git a/packages/utils/src/check-types/is-basic-prop-type.ts b/packages/utils/src/check-types/is-basic-prop-type.ts
new file mode 100644
index 0000000000..fd3b1b1dcb
--- /dev/null
+++ b/packages/utils/src/check-types/is-basic-prop-type.ts
@@ -0,0 +1,8 @@
+import { IPublicTypeBasicType, IPublicTypePropType } from '@alilc/lowcode-types';
+
+export function isBasicPropType(propType: IPublicTypePropType): propType is IPublicTypeBasicType {
+ if (!propType) {
+ return false;
+ }
+ return typeof propType === 'string';
+}
\ No newline at end of file
diff --git a/packages/utils/src/check-types/is-required-prop-type.ts b/packages/utils/src/check-types/is-required-prop-type.ts
new file mode 100644
index 0000000000..106da78a00
--- /dev/null
+++ b/packages/utils/src/check-types/is-required-prop-type.ts
@@ -0,0 +1,8 @@
+import { IPublicTypePropType, IPublicTypeRequiredType } from '@alilc/lowcode-types';
+
+export function isRequiredPropType(propType: IPublicTypePropType): propType is IPublicTypeRequiredType {
+ if (!propType) {
+ return false;
+ }
+ return typeof propType === 'object' && propType.type && ['array', 'bool', 'func', 'number', 'object', 'string', 'node', 'element', 'any'].includes(propType.type);
+}
\ No newline at end of file
diff --git a/packages/utils/src/context-menu.scss b/packages/utils/src/context-menu.scss
new file mode 100644
index 0000000000..0b75ca3ec1
--- /dev/null
+++ b/packages/utils/src/context-menu.scss
@@ -0,0 +1,50 @@
+.engine-context-menu-tree-wrap {
+ position: relative;
+ padding: 4px 10px 4px 32px;
+}
+
+.engine-context-menu-tree-children {
+ margin-left: 8px;
+ line-height: 24px;
+}
+
+.engine-context-menu-item {
+ .engine-context-menu-text {
+ color: var(--color-context-menu-text, var(--color-text));
+ display: flex;
+ align-items: center;
+
+ .lc-help-tip {
+ margin-left: 4px;
+ opacity: 0.8;
+ }
+ }
+
+ &.disabled {
+ &:hover .engine-context-menu-text, .engine-context-menu-text {
+ color: var(--color-context-menu-text-disabled, var(--color-text-disabled));
+ }
+ }
+
+ &:hover {
+ .engine-context-menu-text {
+ color: var(--color-context-menu-text-hover, var(--color-title));
+ }
+ }
+}
+
+.engine-context-menu-title {
+ color: var(--color-context-menu-text, var(--color-text));
+ cursor: pointer;
+
+ &:hover {
+ background-color: var(--color-block-background-light);
+ color: var(--color-title);
+ }
+}
+
+.engine-context-menu-tree-selecte-icon {
+ position: absolute;
+ left: 10px;
+ color: var(--color-icon-active);
+}
\ No newline at end of file
diff --git a/packages/utils/src/context-menu.tsx b/packages/utils/src/context-menu.tsx
new file mode 100644
index 0000000000..185abbb343
--- /dev/null
+++ b/packages/utils/src/context-menu.tsx
@@ -0,0 +1,230 @@
+import { Menu, Icon } from '@alifd/next';
+import { IPublicEnumContextMenuType, IPublicModelNode, IPublicModelPluginContext, IPublicTypeContextMenuAction, IPublicTypeContextMenuItem } from '@alilc/lowcode-types';
+import { Logger } from '@alilc/lowcode-utils';
+import classNames from 'classnames';
+import React from 'react';
+import './context-menu.scss';
+
+const logger = new Logger({ level: 'warn', bizName: 'utils' });
+const { Item, Divider, PopupItem } = Menu;
+
+const MAX_LEVEL = 2;
+
+interface IOptions {
+ nodes?: IPublicModelNode[] | null;
+ destroy?: Function;
+ pluginContext: IPublicModelPluginContext;
+}
+
+const Tree = (props: {
+ node?: IPublicModelNode | null;
+ children?: React.ReactNode;
+ options: IOptions;
+}) => {
+ const { node } = props;
+
+ if (!node) {
+ return (
+ { props.children }
+ );
+ }
+
+ const { common } = props.options.pluginContext || {};
+ const { intl } = common?.utils || {};
+ const indent = node.zLevel * 8 + 32;
+ const style = {
+ paddingLeft: indent,
+ marginLeft: -indent,
+ marginRight: -10,
+ paddingRight: 10,
+ };
+
+ return (
+
+ {
+ props.options.destroy?.();
+ node.select();
+ }}
+ style={style}
+ >
+ {props.options.nodes?.[0].id === node.id ? () : null}
+ {intl(node.title)}
+
+
+ { props.children }
+
+
+ );
+};
+
+let destroyFn: Function | undefined;
+
+export function parseContextMenuAsReactNode(menus: IPublicTypeContextMenuItem[], options: IOptions): React.ReactNode[] {
+ const { common, commonUI } = options.pluginContext || {};
+ const { intl = (title: any) => title } = common?.utils || {};
+ const { HelpTip } = commonUI || {};
+
+ const children: React.ReactNode[] = [];
+ menus.forEach((menu, index) => {
+ if (menu.type === IPublicEnumContextMenuType.SEPARATOR) {
+ children.push();
+ return;
+ }
+
+ if (menu.type === IPublicEnumContextMenuType.MENU_ITEM) {
+ if (menu.items && menu.items.length) {
+ children.push((
+ {intl(menu.title)} }
+ >
+