mirror of
https://github.com/alibaba/lowcode-engine.git
synced 2025-12-12 11:09:21 +00:00
chore: merge from develop
This commit is contained in:
commit
1261a6f169
@ -12,8 +12,6 @@ sidebar_position: 3
|
||||
| @alilc/lowcode-engine | [https://github.com/alibaba/lowcode-engine](https://github.com/alibaba/lowcode-engine) | packages/engine |
|
||||
| @alilc/lowcode-plugin-designer | [https://github.com/alibaba/lowcode-engine](https://github.com/alibaba/lowcode-engine) | packages/plugin-designer |
|
||||
| @alilc/lowcode-plugin-outline-pane | [https://github.com/alibaba/lowcode-engine](https://github.com/alibaba/lowcode-engine) | packages/plugin-outline-pane |
|
||||
| @alilc/lowcode-rax-renderer | [https://github.com/alibaba/lowcode-engine](https://github.com/alibaba/lowcode-engine) | packages/rax-renderer |
|
||||
| @alilc/lowcode-rax-simulator-renderer | [https://github.com/alibaba/lowcode-engine](https://github.com/alibaba/lowcode-engine) | packages/rax-simulator-renderer |
|
||||
| @alilc/lowcode-react-renderer | [https://github.com/alibaba/lowcode-engine](https://github.com/alibaba/lowcode-engine) | packages/react-renderer |
|
||||
| @alilc/lowcode-react-simulator-renderer | [https://github.com/alibaba/lowcode-engine](https://github.com/alibaba/lowcode-engine) | packages/react-simulator-renderer |
|
||||
| @alilc/lowcode-renderer-core | [https://github.com/alibaba/lowcode-engine](https://github.com/alibaba/lowcode-engine) | packages/renderer-core |
|
||||
|
||||
@ -15,15 +15,13 @@ sidebar_position: 2
|
||||
5. ignitor
|
||||
6. plugin-designer
|
||||
7. plugin-outline-pane
|
||||
8. rax-renderer
|
||||
9. rax-simulator-renderer
|
||||
10. react-renderer
|
||||
11. react-simulator-renderer
|
||||
12. renderer-core
|
||||
13. types
|
||||
14. utils
|
||||
15. material-parser
|
||||
16. code-generator
|
||||
8. react-renderer
|
||||
9. react-simulator-renderer
|
||||
10. renderer-core
|
||||
11. types
|
||||
12. utils
|
||||
13. material-parser
|
||||
14. code-generator
|
||||
|
||||
## 2. 引擎官方扩展包
|
||||
包含了常用的设置器(setter)、跟 setter 绑定的插件等
|
||||
|
||||
@ -11,7 +11,6 @@ sidebar_position: 4
|
||||
## npm 包与仓库信息
|
||||
|
||||
- React 框架渲染 npm 包:@alilc/lowcode-react-renderer
|
||||
- Rax 框架渲染 npm 包:@alilc/lowcode-rax-renderer
|
||||
- 仓库:[https://github.com/alibaba/lowcode-engine](https://github.com/alibaba/lowcode-engine) 下的
|
||||
- packages/renderer-core
|
||||
- packages/react-renderer
|
||||
|
||||
@ -40,7 +40,6 @@ ReactDOM.render((
|
||||
), document.getElementById('root'));
|
||||
```
|
||||
|
||||
- rax-renderer:npm 包替换为 @alilc/lowcode-rax-renderer
|
||||
####
|
||||
### 项目使用示例
|
||||
> [设计器 demo](https://lowcode-engine.cn/demo/demo-general/index.html)
|
||||
|
||||
@ -47,15 +47,7 @@ npm install && npm run setup
|
||||
[
|
||||
"https?://uipaas-assets.com/prod/npm/@alilc/lowcode-engine/(.*)/dist/css/react-simulator-renderer.css",
|
||||
"http://localhost:5555/css/ReactSimulatorRenderer.css"
|
||||
],
|
||||
[
|
||||
"https?://uipaas-assets.com/prod/npm/@alilc/lowcode-engine/(.*)/dist/js/rax-simulator-renderer.js",
|
||||
"http://localhost:5555/js/RaxSimulatorRenderer.js"
|
||||
],
|
||||
[
|
||||
"https?://uipaas-assets.com/prod/npm/@alilc/lowcode-engine/(.*)/dist/css/rax-simulator-renderer.css",
|
||||
"http://localhost:5555/css/RaxSimulatorRenderer.css"
|
||||
],
|
||||
]
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
@ -499,7 +499,6 @@ try {
|
||||
- 说明:组件即将从 DOM 中移除
|
||||
- componentDidCatch(error, info)
|
||||
- 说明:组件捕获到异常
|
||||
- Rax:目前没有使用生命周期,使用 hooks 替代生命周期;
|
||||
|
||||
该对象由一系列 key-value 组成,key 为生命周期方法名,value 为 JSFunction 的描述,详见下方示例:
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@alilc/lowcode-engine-docs",
|
||||
"version": "1.2.18",
|
||||
"version": "1.2.19",
|
||||
"description": "低代码引擎版本化文档",
|
||||
"license": "MIT",
|
||||
"files": [
|
||||
|
||||
@ -52,7 +52,7 @@
|
||||
"yarn": "^1.22.17",
|
||||
"rimraf": "^3.0.2",
|
||||
"@types/react-router": "5.1.18",
|
||||
"@alilc/build-plugin-lce": "^0.0.3",
|
||||
"@alilc/build-plugin-lce": "^0.0.4",
|
||||
"babel-jest": "^26.5.2",
|
||||
"@alilc/lowcode-test-mate": "^1.0.1"
|
||||
},
|
||||
|
||||
@ -39,6 +39,7 @@ import {
|
||||
isDragAnyObject,
|
||||
isDragNodeObject,
|
||||
isLocationData,
|
||||
Logger,
|
||||
} from '@alilc/lowcode-utils';
|
||||
import {
|
||||
isShaken,
|
||||
@ -72,6 +73,8 @@ import { IScroller } from '../designer/scroller';
|
||||
import { isElementNode, isDOMNodeVisible } from '../utils/misc';
|
||||
import { debounce } from 'lodash';
|
||||
|
||||
const logger = new Logger({ level: 'warn', bizName: 'designer' });
|
||||
|
||||
export type LibraryItem = IPublicTypePackage & {
|
||||
package: string;
|
||||
library: string;
|
||||
@ -122,21 +125,6 @@ const defaultSimulatorUrl = (() => {
|
||||
return urls;
|
||||
})();
|
||||
|
||||
const defaultRaxSimulatorUrl = (() => {
|
||||
const publicPath = getPublicPath();
|
||||
let urls;
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const [_, prefix = '', dev] = /^(.+?)(\/js)?\/?$/.exec(publicPath) || [];
|
||||
if (dev) {
|
||||
urls = [`${prefix}/css/rax-simulator-renderer.css`, `${prefix}/js/rax-simulator-renderer.js`];
|
||||
} else if (process.env.NODE_ENV === 'production') {
|
||||
urls = [`${prefix}/rax-simulator-renderer.css`, `${prefix}/rax-simulator-renderer.js`];
|
||||
} else {
|
||||
urls = [`${prefix}/rax-simulator-renderer.css`, `${prefix}/rax-simulator-renderer.js`];
|
||||
}
|
||||
return urls;
|
||||
})();
|
||||
|
||||
const defaultEnvironment = [
|
||||
// https://g.alicdn.com/mylib/??react/16.11.0/umd/react.production.min.js,react-dom/16.8.6/umd/react-dom.production.min.js,prop-types/15.7.2/prop-types.min.js
|
||||
assetItem(
|
||||
@ -151,17 +139,6 @@ const defaultEnvironment = [
|
||||
),
|
||||
];
|
||||
|
||||
const defaultRaxEnvironment = [
|
||||
assetItem(
|
||||
AssetType.JSText,
|
||||
'window.Rax=parent.Rax;window.React=parent.React;window.ReactDOM=parent.ReactDOM;window.VisualEngineUtils=parent.VisualEngineUtils;window.VisualEngine=parent.VisualEngine',
|
||||
),
|
||||
assetItem(
|
||||
AssetType.JSText,
|
||||
'window.PropTypes=parent.PropTypes;React.PropTypes=parent.PropTypes; window.__REACT_DEVTOOLS_GLOBAL_HOOK__ = window.parent.__REACT_DEVTOOLS_GLOBAL_HOOK__;',
|
||||
),
|
||||
];
|
||||
|
||||
export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProps> {
|
||||
readonly isSimulator = true;
|
||||
|
||||
@ -467,11 +444,15 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
|
||||
|
||||
const libraryAsset: AssetList = this.buildLibrary();
|
||||
|
||||
if (this.renderEnv === 'rax') {
|
||||
logger.error('After LowcodeEngine v1.3.0, Rax is no longer supported.');
|
||||
}
|
||||
|
||||
const vendors = [
|
||||
// required & use once
|
||||
assetBundle(
|
||||
this.get('environment') ||
|
||||
(this.renderEnv === 'rax' ? defaultRaxEnvironment : defaultEnvironment),
|
||||
defaultEnvironment,
|
||||
AssetLevel.Environment,
|
||||
),
|
||||
// required & use once
|
||||
@ -484,7 +465,7 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
|
||||
// required & use once
|
||||
assetBundle(
|
||||
this.get('simulatorUrl') ||
|
||||
(this.renderEnv === 'rax' ? defaultRaxSimulatorUrl : defaultSimulatorUrl),
|
||||
defaultSimulatorUrl,
|
||||
AssetLevel.Runtime,
|
||||
),
|
||||
];
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { IPublicTypeCustomView, IPublicModelEditor, IPublicModelSettingTopEntry } from '@alilc/lowcode-types';
|
||||
import { IPublicTypeCustomView, IPublicModelEditor, IPublicModelSettingTopEntry, IPublicApiSetters } from '@alilc/lowcode-types';
|
||||
import { isCustomView } from '@alilc/lowcode-utils';
|
||||
import { computed, IEventBus, createModuleEventBus } from '@alilc/lowcode-editor-core';
|
||||
import { ISettingEntry } from './setting-entry-type';
|
||||
@ -6,7 +6,6 @@ import { ISettingField, SettingField } from './setting-field';
|
||||
import { INode } from '../../document';
|
||||
import type { IComponentMeta } from '../../component-meta';
|
||||
import { IDesigner } from '../designer';
|
||||
import { Setters } from '@alilc/lowcode-shell';
|
||||
|
||||
function generateSessionId(nodes: INode[]) {
|
||||
return nodes
|
||||
@ -19,18 +18,18 @@ export interface ISettingTopEntry extends ISettingEntry, IPublicModelSettingTopE
|
||||
INode,
|
||||
ISettingField
|
||||
> {
|
||||
purge(): void;
|
||||
|
||||
items: Array<ISettingField | IPublicTypeCustomView>;
|
||||
|
||||
readonly top: ISettingTopEntry;
|
||||
|
||||
readonly parent: ISettingTopEntry;
|
||||
|
||||
readonly path: never[];
|
||||
|
||||
items: Array<ISettingField | IPublicTypeCustomView>;
|
||||
|
||||
componentMeta: IComponentMeta | null;
|
||||
|
||||
purge(): void;
|
||||
|
||||
getExtraPropValue(propName: string): void;
|
||||
|
||||
setExtraPropValue(propName: string, value: any): void;
|
||||
@ -92,7 +91,7 @@ export class SettingTopEntry implements ISettingTopEntry {
|
||||
|
||||
readonly designer: IDesigner | undefined;
|
||||
|
||||
readonly setters: Setters;
|
||||
readonly setters: IPublicApiSetters;
|
||||
|
||||
disposeFunctions: any[] = [];
|
||||
|
||||
@ -103,7 +102,7 @@ export class SettingTopEntry implements ISettingTopEntry {
|
||||
this.id = generateSessionId(nodes);
|
||||
this.first = nodes[0];
|
||||
this.designer = this.first.document?.designer;
|
||||
this.setters = editor.get('setters') as Setters;
|
||||
this.setters = editor.get('setters') as IPublicApiSetters;
|
||||
|
||||
// setups
|
||||
this.setupComponentMeta();
|
||||
|
||||
@ -44,7 +44,7 @@ const VALID_ENGINE_OPTIONS = {
|
||||
},
|
||||
renderEnv: {
|
||||
type: 'string',
|
||||
enum: ['react', 'rax', 'any string value'],
|
||||
enum: ['react', 'any string value'],
|
||||
default: 'react',
|
||||
description: '渲染器类型',
|
||||
},
|
||||
|
||||
@ -126,7 +126,7 @@ https://cdn.jsdelivr.net/npm/@alilc/lowcode-react-simulator-renderer@1.0.18/dist
|
||||
```
|
||||
|
||||
#### 方式 5:使用自有 cdn
|
||||
将源码中 packages/engine/dist 和 packages/(react|rax)-simulator-renderer/dist 下的文件传至你的 cdn 提供商
|
||||
将源码中 packages/engine/dist 和 packages/react-simulator-renderer/dist 下的文件传至你的 cdn 提供商
|
||||
|
||||
## 🔗 相关链接
|
||||
|
||||
|
||||
@ -126,7 +126,7 @@ https://cdn.jsdelivr.net/npm/@alilc/lowcode-react-simulator-renderer@1.0.18/dist
|
||||
```
|
||||
|
||||
#### Method 5: Use your own cdn
|
||||
Pass the files under packages/engine/dist and packages/(react|rax)-simulator-renderer/dist in the source code to your cdn provider
|
||||
Pass the files under packages/engine/dist and packages/react-simulator-renderer/dist in the source code to your cdn provider
|
||||
|
||||
## 🔗 Related Links
|
||||
|
||||
|
||||
@ -1,8 +1,7 @@
|
||||
{
|
||||
"entry": {
|
||||
"AliLowCodeEngine": "../engine/src/index.ts",
|
||||
"ReactSimulatorRenderer": "../react-simulator-renderer/src/index.ts",
|
||||
"RaxSimulatorRenderer": "../rax-simulator-renderer/src/index.ts"
|
||||
"ReactSimulatorRenderer": "../react-simulator-renderer/src/index.ts"
|
||||
},
|
||||
"vendor": false,
|
||||
"devServer": {
|
||||
|
||||
@ -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(
|
||||
<RaxRenderer
|
||||
schema={schema}
|
||||
components={components}
|
||||
/>,
|
||||
document.getElementById('root'), { driver: DriverUniversal }
|
||||
);
|
||||
```
|
||||
@ -1,11 +0,0 @@
|
||||
{
|
||||
"plugins": [
|
||||
[
|
||||
"build-plugin-rax-component",
|
||||
{
|
||||
"type": "rax",
|
||||
"targets": ["web"]
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
@ -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(<Engine schema={schema} components={components} />, document.getElementById('root'), {
|
||||
driver: DriverUniversal,
|
||||
});
|
||||
@ -1 +0,0 @@
|
||||
App({});
|
||||
@ -1,6 +0,0 @@
|
||||
{
|
||||
"pages": ["pages/index"],
|
||||
"window": {
|
||||
"defaultTitle": "demo"
|
||||
}
|
||||
}
|
||||
@ -1 +0,0 @@
|
||||
<my-component></my-component>
|
||||
@ -1,4 +0,0 @@
|
||||
Page({
|
||||
onLoad() {},
|
||||
onShow() {},
|
||||
});
|
||||
@ -1,6 +0,0 @@
|
||||
{
|
||||
"defaultTitle": "Miniapp Rax Text demo",
|
||||
"usingComponents": {
|
||||
"my-component": "../components/Target/index"
|
||||
}
|
||||
}
|
||||
@ -1 +0,0 @@
|
||||
App({});
|
||||
@ -1,6 +0,0 @@
|
||||
{
|
||||
"pages": ["pages/index"],
|
||||
"window": {
|
||||
"title": "demo"
|
||||
}
|
||||
}
|
||||
@ -1,4 +0,0 @@
|
||||
Page({
|
||||
onLoad() {},
|
||||
onShow() {},
|
||||
});
|
||||
@ -1,6 +0,0 @@
|
||||
{
|
||||
"title": "Wechat MiniProgram Rax Text demo",
|
||||
"usingComponents": {
|
||||
"my-component": "../components/Target/index"
|
||||
}
|
||||
}
|
||||
@ -1 +0,0 @@
|
||||
<my-component></my-component>
|
||||
@ -1,54 +0,0 @@
|
||||
{
|
||||
"name": "@alilc/lowcode-rax-renderer",
|
||||
"version": "1.3.0-beta.0",
|
||||
"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.3.0-beta.0",
|
||||
"@alilc/lowcode-utils": "1.3.0-beta.0",
|
||||
"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": "b279a68054fba2f891579287da5f03c26df9e5b8",
|
||||
"bugs": "https://github.com/alibaba/lowcode-engine/issues"
|
||||
}
|
||||
@ -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 (
|
||||
<AppContext.Consumer>
|
||||
{context => {
|
||||
this.context = context;
|
||||
return (
|
||||
<CompRenderer
|
||||
{...props}
|
||||
__appHelper={appHelper}
|
||||
__components={{ ...components, Component: CompRenderer, Block: BlockRenderer }}
|
||||
__componentsMap={componentsMap}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
</AppContext.Consumer>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const ResComp = forwardRef((props, ref) => <LNCompView {...props} forwardedRef={ref} />);
|
||||
forEach(schema.static, (val, key) => {
|
||||
ResComp[key] = val;
|
||||
});
|
||||
ResComp.version = config.version || '0.0.0';
|
||||
return ResComp;
|
||||
}
|
||||
@ -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;
|
||||
@ -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;
|
||||
@ -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;
|
||||
@ -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;
|
||||
@ -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"
|
||||
]
|
||||
}
|
||||
@ -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
|
||||
}]
|
||||
]
|
||||
}
|
||||
@ -1 +0,0 @@
|
||||
module.exports = require('../../babel.config');
|
||||
@ -1,3 +0,0 @@
|
||||
{
|
||||
"plugins": ["@alilc/build-plugin-lce", "./build.plugin.js"]
|
||||
}
|
||||
@ -1,5 +0,0 @@
|
||||
module.exports = ({ onGetWebpackConfig }) => {
|
||||
onGetWebpackConfig((config) => {
|
||||
config.performance.hints(false);
|
||||
});
|
||||
};
|
||||
@ -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"
|
||||
]
|
||||
}
|
||||
@ -1,55 +0,0 @@
|
||||
{
|
||||
"name": "@alilc/lowcode-rax-simulator-renderer",
|
||||
"version": "1.3.0-beta.0",
|
||||
"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.3.0-beta.0",
|
||||
"@alilc/lowcode-rax-renderer": "1.3.0-beta.0",
|
||||
"@alilc/lowcode-types": "1.3.0-beta.0",
|
||||
"@alilc/lowcode-utils": "1.3.0-beta.0",
|
||||
"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": "b279a68054fba2f891579287da5f03c26df9e5b8",
|
||||
"bugs": "https://github.com/alibaba/lowcode-engine/issues"
|
||||
}
|
||||
@ -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 <div className="engine-unknow-component">组件 {this.props._componentName} 无视图,请打开控制台排查</div>;
|
||||
}
|
||||
}
|
||||
|
||||
export class FaultComponent extends Component {
|
||||
props: {
|
||||
_componentName: string;
|
||||
};
|
||||
|
||||
render() {
|
||||
return <div className="engine-fault-component">组件 {this.props._componentName} 渲染错误,请打开控制台排查</div>;
|
||||
}
|
||||
}
|
||||
|
||||
export class HiddenComponent extends Component {
|
||||
render() {
|
||||
return <div className="engine-hidden-component">在本页面不显示</div>;
|
||||
}
|
||||
}
|
||||
|
||||
export default { FaultComponent, HiddenComponent, UnknownComponent };
|
||||
@ -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<ILeaf, {}> {
|
||||
// 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 会是 <!-- empty -->,而不是 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 = [<InsertionGhost key="insertion" />];
|
||||
// // } else if (insertion && insertion.isNearEdge()) {
|
||||
// // if (insertion.isNearAfter()) {
|
||||
// // children.push(<InsertionGhost key="insertion" />);
|
||||
// // } else {
|
||||
// // children.unshift(<InsertionGhost key="insertion" />);
|
||||
// // }
|
||||
// // }
|
||||
// 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')] = <Leaf key={child.getId()} leaf={child} />;
|
||||
// }
|
||||
// });
|
||||
// return slots;
|
||||
// }
|
||||
// }
|
||||
@ -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;
|
||||
}
|
||||
@ -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 (
|
||||
<div className="lc-container">{children}</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Slot;
|
||||
@ -1,4 +0,0 @@
|
||||
// NOTE: 仅做类型标注,切勿做其它用途
|
||||
import { BuiltinSimulatorHost } from '@alilc/lowcode-designer';
|
||||
|
||||
export const host: BuiltinSimulatorHost = (window as any).LCSimulatorHost;
|
||||
@ -1,2 +0,0 @@
|
||||
declare module 'rax-find-dom-node';
|
||||
declare module '@alilc/lowcode-rax-renderer/lib/index';
|
||||
@ -1,7 +0,0 @@
|
||||
import renderer from './renderer';
|
||||
|
||||
if (typeof window !== 'undefined') {
|
||||
(window as any).SimulatorRenderer = renderer;
|
||||
}
|
||||
|
||||
export default renderer;
|
||||
@ -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;
|
||||
}
|
||||
@ -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 (
|
||||
<Layout rendererContainer={rendererContainer}>
|
||||
<Routes rendererContainer={rendererContainer} history={rendererContainer.history} />
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
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) => <Renderer key={instance.id} rendererContainer={rendererContainer} documentInstance={instance} {...props} />,
|
||||
};
|
||||
}),
|
||||
};
|
||||
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 <Component props={props}>{children}</Component>;
|
||||
}
|
||||
if (componentName && rendererContainer.getComponent(componentName)) {
|
||||
return createElement(
|
||||
rendererContainer.getComponent(componentName),
|
||||
{
|
||||
...props,
|
||||
rendererContainer,
|
||||
},
|
||||
[children],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return <Fragment>{children}</Fragment>;
|
||||
}
|
||||
}
|
||||
|
||||
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 (
|
||||
<RaxRenderer
|
||||
schema={documentInstance.schema}
|
||||
components={renderer.components}
|
||||
appHelper={renderer.context}
|
||||
context={renderer.context}
|
||||
device={device}
|
||||
designMode={renderer.designMode}
|
||||
key={this.key}
|
||||
__host={host}
|
||||
__container={container}
|
||||
suspended={documentInstance.suspended}
|
||||
self={documentInstance.scope}
|
||||
onCompGetRef={(schema: any, ref: any) => {
|
||||
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 = (
|
||||
<div className="lc-container-placeholder" style={viewProps.placeholderStyle}>
|
||||
{viewProps.placeholder || '拖拽组件或模板到这里'}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// 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,
|
||||
);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -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%;
|
||||
}
|
||||
@ -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<any> | IPublicTypeComponentSchema },
|
||||
createComponent: (schema: IPublicTypeProjectSchema<IPublicTypeComponentSchema>) => 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<IPublicTypeNodeInstance<any>['node'], null | undefined>;
|
||||
};
|
||||
} {
|
||||
const leaf = raxComponentInst?.props?._leaf;
|
||||
return leaf && typeof leaf === 'object' && leaf.isNode;
|
||||
}
|
||||
|
||||
export class DocumentInstance {
|
||||
private instancesMap = new Map<string, any[]>();
|
||||
|
||||
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<IPublicTypeNodeSchema> | 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<string, DocumentInstance>();
|
||||
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<string, React.FC | React.ComponentClass> | null = {};
|
||||
@computed get components(): Record<string, React.FC | React.ComponentClass> {
|
||||
// 根据 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<any> {
|
||||
return loader.load(asset);
|
||||
}
|
||||
|
||||
async loadAsyncLibrary(asyncLibraryMap: Record<string, any>) {
|
||||
}
|
||||
|
||||
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<any> | 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<any> | 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<Element | Text> | 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<IPublicTypeComponentSchema>): Component | null {
|
||||
const _schema: IPublicTypeProjectSchema<IPublicTypeComponentSchema> = {
|
||||
...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();
|
||||
@ -1,17 +0,0 @@
|
||||
export interface Defer<T = any> {
|
||||
resolve(value?: T | PromiseLike<T>): void;
|
||||
reject(reason?: any): void;
|
||||
promise(): Promise<T>;
|
||||
}
|
||||
|
||||
export function createDefer<T = any>(): Defer<T> {
|
||||
const r: any = {};
|
||||
const promise = new Promise<T>((resolve, reject) => {
|
||||
r.resolve = resolve;
|
||||
r.reject = reject;
|
||||
});
|
||||
|
||||
r.promise = () => promise;
|
||||
|
||||
return r;
|
||||
}
|
||||
@ -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<Element | Text> | 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];
|
||||
}
|
||||
@ -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());
|
||||
}
|
||||
@ -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,
|
||||
};
|
||||
@ -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);
|
||||
}
|
||||
@ -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<string, StylePoint>();
|
||||
|
||||
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();
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
@ -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}`;
|
||||
}
|
||||
@ -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 <T = any, S = any> {
|
||||
setState() {}
|
||||
forceUpdate() {}
|
||||
render() {}
|
||||
state: Readonly<S>;
|
||||
props: Readonly<T> & Readonly<{ children?: any | undefined }>;
|
||||
refs: Record<string, unknown>;
|
||||
context: Record<string, unknown>;
|
||||
setState() {}
|
||||
forceUpdate() {}
|
||||
render() {}
|
||||
};
|
||||
const PureComponent = class <T = any, S = any> {
|
||||
setState() {}
|
||||
forceUpdate() {}
|
||||
render() {}
|
||||
state: Readonly<S>;
|
||||
props: Readonly<T> & Readonly<{ children?: any | undefined }>;
|
||||
refs: Record<string, unknown>;
|
||||
context: Record<string, unknown>;
|
||||
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;
|
||||
}
|
||||
|
||||
@ -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', () => {
|
||||
|
||||
@ -41,7 +41,7 @@ export interface IPublicTypeEngineOptions {
|
||||
/**
|
||||
* 渲染器类型,默认值:'react'
|
||||
*/
|
||||
renderEnv?: 'react' | 'rax' | string;
|
||||
renderEnv?: 'react' | string;
|
||||
|
||||
/**
|
||||
* 设备类型映射器,处理设计器与渲染器中 device 的映射
|
||||
|
||||
@ -9,8 +9,6 @@ lerna run build \
|
||||
--scope @alilc/lowcode-designer \
|
||||
--scope @alilc/lowcode-plugin-designer \
|
||||
--scope @alilc/lowcode-plugin-outline-pane \
|
||||
--scope @alilc/lowcode-rax-renderer \
|
||||
--scope @alilc/lowcode-rax-simulator-renderer \
|
||||
--scope @alilc/lowcode-react-renderer \
|
||||
--scope @alilc/lowcode-react-simulator-renderer \
|
||||
--scope @alilc/lowcode-renderer-core \
|
||||
@ -20,13 +18,9 @@ lerna run build \
|
||||
|
||||
lerna run build:umd \
|
||||
--scope @alilc/lowcode-engine \
|
||||
--scope @alilc/lowcode-rax-simulator-renderer \
|
||||
--scope @alilc/lowcode-react-simulator-renderer \
|
||||
--scope @alilc/lowcode-react-renderer \
|
||||
--stream
|
||||
|
||||
cp ./packages/react-simulator-renderer/dist/js/* ./packages/engine/dist/js/
|
||||
cp ./packages/react-simulator-renderer/dist/css/* ./packages/engine/dist/css/
|
||||
|
||||
cp ./packages/rax-simulator-renderer/dist/js/* ./packages/engine/dist/js/
|
||||
cp ./packages/rax-simulator-renderer/dist/css/* ./packages/engine/dist/css/
|
||||
@ -10,8 +10,6 @@ tnpm sync @alilc/lowcode-designer
|
||||
tnpm sync @alilc/lowcode-plugin-designer
|
||||
tnpm sync @alilc/lowcode-plugin-outline-pane
|
||||
tnpm sync @alilc/lowcode-renderer-core
|
||||
tnpm sync @alilc/lowcode-rax-renderer
|
||||
tnpm sync @alilc/lowcode-rax-simulator-renderer
|
||||
tnpm sync @alilc/lowcode-react-renderer
|
||||
tnpm sync @alilc/lowcode-react-simulator-renderer
|
||||
tnpm sync @alilc/lowcode-engine
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user