From 9c62a490c9f8fee7564dec310031755e7478691e Mon Sep 17 00:00:00 2001 From: "jianfang.rjf" Date: Thu, 7 May 2020 21:25:32 +0800 Subject: [PATCH 1/5] feat: add expression-setter AutoComplete tips --- .../setters/src/expression-setter/index.scss | 141 ++------------ .../setters/src/expression-setter/index.tsx | 179 +++++++----------- 2 files changed, 85 insertions(+), 235 deletions(-) diff --git a/packages/setters/src/expression-setter/index.scss b/packages/setters/src/expression-setter/index.scss index 19287099a..0d12a5571 100644 --- a/packages/setters/src/expression-setter/index.scss +++ b/packages/setters/src/expression-setter/index.scss @@ -1,130 +1,17 @@ -// mixin -.lowcode-setter-mixin > * { - vertical-align: middle; -} -.lowcode-setter-mixin { - width: 86%; -} -.lowcode-setter-mixin .next-input { - width: 100%; -} -.lowcode-setter-mixin .next-select-trigger { - width: 100%; -} -// json-setter -// :global { - .nrs-monaco-form { - .next-form-item:last-child { - margin: 0 !important; - } +.expression-setter-item-inner { + .next-menu-item-text { + vertical-align: middle; + display: flex; + justify-content: space-between; } - .monaco-editor-wrap { - .luna-monaco-button .next-icon-first { - height: 26px; - } - .monaco_fullscreen_icon { - position: absolute; - line-height: 1; - z-index: 7; - color: #ddd; - &:hover { - color: #fff; - } - } - .btns-eare { - text-align: left; - line-height: initial; - margin-top: 5px; - // button{ - // margin-right: 10px; - // } - } - &.monaco-nofullscreen { - position: relative !important; - .monaco_fullscreen_icon { - position: absolute; - top: 5px; - right: 5px; - line-height: 1; - z-index: 7; - i:before { - font-size: 16px; - } - } - } - &.monaco-fullscreen { - position: fixed !important; - height: 100% !important; - width: 100% !important; - border: 0; - margin: 0; - top: 0; - bottom: 0; - left: 0; - right: 0; - z-index: 1001; - overflow: hidden; - .monaco_fullscreen_icon { - top: 10px; - right: 10px; - i:before { - font-size: 24px; - } - } - } - } - .luna-monaco-button button { - width: 100%; - } - .luna-monaco-button-dialog { - .next-dialog-body { - padding: 0; - .next-form-item { - height: 100%; - margin-bottom: 0; - .next-form-item-control, - .next-form-item-control > div { - height: 100% !important; - } - .next-form-item-help { - position: absolute; - } - } - } - } -// } -// color-setter -.lowcode-color-box { - margin-right: -5px; - padding: 3px 0 3px 3px; - width: 26px; - height: 26px; - display: inline-block; - div { - width: 20px; - height: 20px; - border: 1px solid #ddd; + .next-menu-item { + padding: 0 10px; } } -.next-balloon-normal.lowcode-color-content { - padding: 0; - background: #ffffff; - border-radius: 0; - border: 1px solid #e5e5e5; - box-shadow: 0 2px 8px 0 rgba(0, 0, 0, 0.1); - &:after { - display: none; - } - .sketch-picker { - border-radius: 0 !important; - border: none !important; - box-shadow: none !important; - .flexbox-fix { - input { - width: 100% !important; - min-width: 30px; - text-align: center; - } - } - } -} \ No newline at end of file +.code-input-value { + float: left; +} +.code-input-help { + float: right; + color: #6897f2; +} diff --git a/packages/setters/src/expression-setter/index.tsx b/packages/setters/src/expression-setter/index.tsx index 7e4ecd14b..2ab0e7a3c 100644 --- a/packages/setters/src/expression-setter/index.tsx +++ b/packages/setters/src/expression-setter/index.tsx @@ -1,24 +1,26 @@ import React, { PureComponent } from 'react'; import PropTypes from 'prop-types'; -import { Select, Balloon } from '@alife/next'; +import { Select, Balloon, Icon } from '@alife/next'; import * as acorn from 'acorn'; import { isJSExpression, generateI18n } from './locale/utils'; import zhCN from './locale/zh-CN'; +import './index.scss'; + const { Option, AutoComplete } = Select; const { Tooltip } = Balloon; const helpMap = { this: '容器上下文对象', - 'this.state': '容器的state', - 'this.props': '容器的props', - 'this.context': '容器的context', - 'this.page': '页面上下文对象', - 'this.component': '组件上下文对象', - 'this.constants': '应用常量对象', - 'this.utils': '应用工具对象', - 'this.dataSourceMap': '容器数据源Map', - 'this.field': '表单Field对象' + 'state': '容器的state', + 'props': '容器的props', + 'context': '容器的context', + 'schema': '页面上下文对象', + 'component': '组件上下文对象', + 'constants': '应用常量对象', + 'utils': '应用工具对象', + 'dataSourceMap': '容器数据源Map', + 'field': '表单Field对象' } export default class ExpressionView extends PureComponent { @@ -45,7 +47,7 @@ export default class ExpressionView extends PureComponent { i18n: any; t: void; $input: any; - listenerFun: (event: any) => void; + listenerFun: ((event: any) => void) | undefined; static getInitValue(val: { value: any; match: (arg0: RegExp) => any; }) { if (isJSExpression(val)) { @@ -81,51 +83,12 @@ export default class ExpressionView extends PureComponent { onChange(value: string, actionType: string) { let realInputValue = value; let realDataSource = null; - const cursorIndex = this.getInputCursorPosition(); let nextCursorIndex: number; //更新值 if (actionType === 'itemClick' || actionType === 'enter') { let curValue = this.state.value; if (curValue) { - //如果是非.结束,则替换当前这个变量; - let preStr = curValue.substr(0, cursorIndex); - let nextStr = curValue.substr(cursorIndex); - let preArr = preStr.split('.'); - let preArrLen = preArr.length; - let tarPreStr = ''; - if (!preArr[preArrLen - 1]) { - //如果是.结束,则增加到.后面 - if (preArr[preArrLen - 2] === 'this') { - preArr = preArr.slice(0, preArrLen - 2); - preArr.push(value); - tarPreStr = preArr.join('.'); - } else { - tarPreStr = preStr + value; - } - } else { - if (preArr[preArrLen - 2] === 'this') { - preArr = preArr.slice(0, preArrLen - 2); - } else { - preArr = preArr.slice(0, preArrLen - 1); - } - preArr.push(value); - tarPreStr = preArr.join('.'); - } - realInputValue = tarPreStr + nextStr; - realDataSource = this.getDataSource(tarPreStr + '.') || []; - nextCursorIndex = tarPreStr.length; - } - } else { - let tarPreStr = value.substr(0, cursorIndex); - if (tarPreStr) { - let lastChar = tarPreStr.charAt(tarPreStr.length - 1); - if (lastChar === '.') { - realDataSource = this.getDataSource(tarPreStr) || []; - } else { - realDataSource = this.getDataSource(tarPreStr + '.'); - } - } else { - realDataSource = this.getDataSource('this.'); + realInputValue = curValue + realInputValue; } } //更新数据源 @@ -154,21 +117,16 @@ export default class ExpressionView extends PureComponent { * @return {Array} */ getDataSource(tempStr: string): Array { - if (tempStr === '' || /[^\w\.]$/.test(tempStr)) { - return this.getDataSource('this.') || []; + if (/[^\w\.]$/.test(tempStr)) { + return []; + } else if (tempStr === null || tempStr === '') { + return this.getContextKeys([]); } else if (/\w\.$/.test(tempStr)) { let currentField = this.getCurrentFiled(tempStr); if (!currentField) return null; let tempKeys = this.getObjectKeys(currentField.str); tempKeys = this.getContextKeys(tempKeys); if (!tempKeys) return null; - //给默认情况增加this - if (tempStr === 'this.') { - tempKeys = tempKeys.map((item: string) => { - return 'this.' + item; - }); - tempKeys.unshift('this'); - } return tempKeys; } else if (/\.$/.test(tempStr)) { return []; @@ -202,34 +160,31 @@ export default class ExpressionView extends PureComponent { * @param {Array} * @return {Array} */ - getContextKeys(keys: any) { - // let context = {}; - // const { appHelper } = this.context; - // const activeKey = appHelper && appHelper.activeKey; - // if (!activeKey) return; - // const activeCtx = appHelper.schemaHelper.compCtxMap && appHelper.schemaHelper.compCtxMap[activeKey]; - // if (!activeCtx) return null; - // let __self = activeCtx; - // if (keys && keys.length > 1) { - // keys.shift(0); - // let path = '/' + keys.join('/'); - // path = path.replace(/[\[\]]/g, '/'); - // context = jsonuri.get(__self, path); - // if (context && typeof context === 'object') { - // return this.filterKey(context); - // } - // } else if (keys && keys[0] === 'this') { - // return this.filterKey(__self); - // } - // return null; - return [ - "page", - "component" - ] + getContextKeys(keys: []) { + const editor = this.props.field.editor; + console.log(editor); + const limitKeys = ['schema', 'utils', 'constants']; + if (keys.length === 0) return limitKeys; + if (!limitKeys.includes(keys[0])) return []; + let result = []; + let keyValue = editor; + let assert = false; + keys.forEach(item => { + if (!keyValue[item] || typeof keyValue[item] !== 'object') { + assert = true; + } + if (keyValue[item]) { + keyValue = keyValue[item]; + } + }) + if (assert) return []; + result = Object.keys(keyValue); + return result; + // return utilsKeys.concat(constantsKeys).concat(schemaKeys); } /*过滤key */ - filterKey(obj: any) { + filterKey(obj: any, name: string) { let filterKeys = [ 'reloadDataSource', 'REACT_HOT_LOADER_RENDERED_GENERATION', @@ -244,7 +199,7 @@ export default class ExpressionView extends PureComponent { let result = []; for (let key in obj) { if (key.indexOf('_') !== 0 && filterKeys.indexOf(key) === -1) { - result.push(key); + result.push(`${name}.${key}`); } } return result; @@ -259,12 +214,16 @@ export default class ExpressionView extends PureComponent { filterOption(inputValue: string, item: { value: string | any[]; }) { const cursorIndex = this.getInputCursorPosition(); let preStr = inputValue.substr(0, cursorIndex); - let lastKey = preStr.split('.').slice(-1); + let lastKey: string[] = preStr.split('.').slice(-1); if (!lastKey) return true; if (item.value.indexOf(lastKey) > -1) return true; return false; } + // handleClick = () => { + // this.props.field.editor.emit('variableBindDialog.open'); + // } + render() { const { value, dataSource } = this.state; const { placeholder } = this.props; @@ -293,26 +252,30 @@ export default class ExpressionView extends PureComponent { isValObject ? ( value ) : ( - {'{{'}} - innerAfter={{'}}'}} - itemRender={({ value }) => { - return ( - - ); - }} - onChange={this.onChange.bind(this)} - filter={this.filterOption.bind(this)} - /> +
+ {'{{'}} + innerAfter={{'}}'}} + popupClassName="expression-setter-item-inner" + itemRender={({ value }) => { + console.log(value); + return ( + + ); + }} + onChange={this.onChange.bind(this)} + filter={this.filterOption.bind(this)} + /> +
) } > @@ -363,7 +326,7 @@ export default class ExpressionView extends PureComponent { * 字符串取得对象keys */ getObjectKeys(str: string) { - let keys = []; + let keys: string | any[] = []; if (str) keys = str.split('.'); return keys.slice(0, keys.length - 1); } From 0e50a2056a9c00bfddfe25af82462a781e231910 Mon Sep 17 00:00:00 2001 From: "wuyue.xht" Date: Mon, 18 May 2020 16:03:00 +0800 Subject: [PATCH 2/5] feat: ReactProvider --- packages/react-provider/CHANGELOG.md | 4 + packages/react-provider/README.md | 1 + packages/react-provider/build.json | 5 + packages/react-provider/package.json | 43 ++++++++ packages/react-provider/src/index.ts | 5 + .../react-provider/src/lazy-component.tsx | 43 ++++++++ packages/react-provider/src/provider.tsx | 104 ++++++++++++++++++ packages/react-provider/tsconfig.json | 9 ++ 8 files changed, 214 insertions(+) create mode 100644 packages/react-provider/CHANGELOG.md create mode 100644 packages/react-provider/README.md create mode 100644 packages/react-provider/build.json create mode 100644 packages/react-provider/package.json create mode 100644 packages/react-provider/src/index.ts create mode 100644 packages/react-provider/src/lazy-component.tsx create mode 100644 packages/react-provider/src/provider.tsx create mode 100644 packages/react-provider/tsconfig.json diff --git a/packages/react-provider/CHANGELOG.md b/packages/react-provider/CHANGELOG.md new file mode 100644 index 000000000..e4d87c4d4 --- /dev/null +++ b/packages/react-provider/CHANGELOG.md @@ -0,0 +1,4 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. diff --git a/packages/react-provider/README.md b/packages/react-provider/README.md new file mode 100644 index 000000000..f37b9b0f6 --- /dev/null +++ b/packages/react-provider/README.md @@ -0,0 +1 @@ +# 低代码引擎运行时框架 diff --git a/packages/react-provider/build.json b/packages/react-provider/build.json new file mode 100644 index 000000000..bd5cf18dd --- /dev/null +++ b/packages/react-provider/build.json @@ -0,0 +1,5 @@ +{ + "plugins": [ + "build-plugin-component" + ] +} diff --git a/packages/react-provider/package.json b/packages/react-provider/package.json new file mode 100644 index 000000000..29cca8ab3 --- /dev/null +++ b/packages/react-provider/package.json @@ -0,0 +1,43 @@ +{ + "name": "@ali/lowcode-react-provider", + "version": "0.8.14", + "description": "React Provider for Runtime", + "files": [ + "es", + "lib" + ], + "main": "lib/index.js", + "module": "es/index.js", + "scripts": { + "build": "build-scripts build --skip-demo", + "test": "ava", + "test:snapshot": "ava --update-snapshots" + }, + "ava": { + "compileEnhancements": false, + "snapshotDir": "test/fixtures/__snapshots__", + "extensions": [ + "ts" + ], + "require": [ + "ts-node/register" + ] + }, + "license": "MIT", + "dependencies": { + "@ali/lowcode-runtime": "^0.8.13", + "react": "^16", + "react-dom": "^16", + "@recore/router": "^1.0.11" + }, + "devDependencies": { + "@alib/build-scripts": "^0.1.18", + "build-plugin-component": "^0.2.16", + "@types/node": "^13.7.1", + "@types/react": "^16", + "@types/react-dom": "^16" + }, + "publishConfig": { + "registry": "https://registry.npm.alibaba-inc.com" + } +} diff --git a/packages/react-provider/src/index.ts b/packages/react-provider/src/index.ts new file mode 100644 index 000000000..515d26e3e --- /dev/null +++ b/packages/react-provider/src/index.ts @@ -0,0 +1,5 @@ +import ReactProvider from './provider'; +import { Router } from '@recore/router'; + +export { Router }; +export default ReactProvider; diff --git a/packages/react-provider/src/lazy-component.tsx b/packages/react-provider/src/lazy-component.tsx new file mode 100644 index 000000000..b20fd2bf8 --- /dev/null +++ b/packages/react-provider/src/lazy-component.tsx @@ -0,0 +1,43 @@ +import { Component, createElement } from 'react'; +import { app } from '@ali/lowcode-runtime'; + +interface IProps { + getPageData: () => any; + [key: string]: any; +} + +interface IState { + schema: object | null; +} + +export default class LazyComponent extends Component { + constructor(props: IProps) { + super(props); + this.state = { + schema: null, + }; + } + + async componentDidMount() { + const { getPageData } = this.props; + if (getPageData && !this.state.schema) { + const schema = await getPageData(); + this.setState({ schema }); + } + } + + render() { + const { getPageData, ...restProps } = this.props; + const { schema } = this.state; + const Renderer = app.getRenderer(); + const Loading = app.getLoading(); + if (!Renderer || !schema) { + if (!Loading) { + return null; + } + // loading扩展点 + return createElement(Loading); + } + return createElement(Renderer as any, { schema, loading: Loading ? createElement(Loading) : null, ...restProps }); + } +} diff --git a/packages/react-provider/src/provider.tsx b/packages/react-provider/src/provider.tsx new file mode 100644 index 000000000..de4d97352 --- /dev/null +++ b/packages/react-provider/src/provider.tsx @@ -0,0 +1,104 @@ +import { createElement, ReactType, ReactElement } from 'react'; +import ReactDOM from 'react-dom'; +import { Router } from '@recore/router'; +import { app, Provider } from '@ali/lowcode-runtime'; +import LazyComponent from './lazy-component'; + +export default class ReactProvider extends Provider { + // 定制构造根组件的逻辑,如切换路由机制 + createApp() { + const RouterView = this.getRouterView(); + let App; + const layoutConfig = this.getLayoutConfig(); + if (!layoutConfig || !layoutConfig.componentName) { + App = (props: any) => (RouterView ? createElement(RouterView, { ...props }) : null); + return App; + } + const { componentName: layoutName, props: layoutProps } = layoutConfig; + const { content: Layout, props: extraLayoutProps } = app.getLayout(layoutName) || {}; + const sectionalRender = this.isSectionalRender(); + if (!sectionalRender && Layout) { + App = (props: any) => + createElement( + Layout, + { ...layoutProps, ...extraLayoutProps }, + RouterView ? createElement(RouterView, props) : null, + ); + } else { + App = (props: any) => (RouterView ? createElement(RouterView, props) : null); + } + return App; + } + + runApp(App: any, config: any) { + ReactDOM.render(, document.getElementById(config?.containerId || 'App')); + } + + // 内置实现 for 动态化渲染 + getRouterView(): ReactType | null { + const routerConfig = this.getRouterConfig(); + if (!routerConfig) { + return null; + } + const routes: Array<{ + path: string; + children: any; + exact: boolean; + defined: { keepAlive: boolean; [key: string]: any }; + }> = []; + let homePageId = this.getHomePage(); + Object.keys(routerConfig).forEach((pageId: string, idx: number) => { + if (!pageId) { + return; + } + const path = routerConfig[pageId]; + routes.push({ + path, + children: (props: any) => this.getLazyComponent(pageId, props), + exact: true, + defined: { keepAlive: true }, + }); + if (homePageId) { + return; + } + if (idx === 0 || path === '/') { + homePageId = pageId; + } + }); + if (homePageId) { + routes.push({ + path: '**', + children: (props: any) => this.getLazyComponent(homePageId, { ...props }), + exact: true, + defined: { keepAlive: true }, + }); + } + const RouterView = (props: any) => { + return createElement(Router as any, { + routes, + components: this.getComponents(), + utils: this.getUtils(), + componentsMap: this.getComponentsMapObj(), + ...props, + }); + }; + return RouterView; + } + + getLazyComponent(pageId: string, props: any): ReactElement | null { + if (!pageId) { + return null; + } + if (this.getlazyElement(pageId)) { + return this.getlazyElement(pageId); + } else { + const lazyElement = createElement(LazyComponent as any, { + getPageData: async () => await this.getPageData(pageId), + key: pageId, + ...props, + }); + this.setlazyElement(pageId, lazyElement); + return lazyElement; + } + } +} diff --git a/packages/react-provider/tsconfig.json b/packages/react-provider/tsconfig.json new file mode 100644 index 000000000..c37b76ecc --- /dev/null +++ b/packages/react-provider/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "lib" + }, + "include": [ + "./src/" + ] +} From 8e361967e04eb353dda286dc7d90bbd7538dcb3e Mon Sep 17 00:00:00 2001 From: "wuyue.xht" Date: Mon, 18 May 2020 16:03:44 +0800 Subject: [PATCH 3/5] =?UTF-8?q?chore:=20=E6=8A=BD=E7=A6=BBReact?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/runtime/build.json | 6 +- packages/runtime/package.json | 10 +- packages/runtime/src/core/container.ts | 17 ++- packages/runtime/src/core/index.ts | 15 ++- .../core/{provider/index.ts => provider.ts} | 7 +- .../runtime/src/core/provider/react/index.ts | 100 ------------------ .../core/provider/react/lazy-component.tsx | 43 -------- .../runtime/src/core/{run.ts => runApp.ts} | 54 +++++----- packages/runtime/src/index.ts | 4 +- 9 files changed, 53 insertions(+), 203 deletions(-) rename packages/runtime/src/core/{provider/index.ts => provider.ts} (96%) delete mode 100644 packages/runtime/src/core/provider/react/index.ts delete mode 100644 packages/runtime/src/core/provider/react/lazy-component.tsx rename packages/runtime/src/core/{run.ts => runApp.ts} (50%) diff --git a/packages/runtime/build.json b/packages/runtime/build.json index e791d5b6b..bd5cf18dd 100644 --- a/packages/runtime/build.json +++ b/packages/runtime/build.json @@ -1,9 +1,5 @@ { "plugins": [ - "build-plugin-component", - "build-plugin-fusion", - ["build-plugin-moment-locales", { - "locales": ["zh-cn"] - }] + "build-plugin-component" ] } diff --git a/packages/runtime/package.json b/packages/runtime/package.json index 2c667549f..276e1cc32 100644 --- a/packages/runtime/package.json +++ b/packages/runtime/package.json @@ -1,6 +1,6 @@ { "name": "@ali/lowcode-runtime", - "version": "0.8.12", + "version": "0.8.14", "description": "Runtime for Ali lowCode engine", "files": [ "es", @@ -25,15 +25,13 @@ }, "license": "MIT", "dependencies": { - "@ali/recore": "^1.6.9", - "@ali/offline-events": "^1.0.0" + "@ali/offline-events": "^1.0.0", + "history": "^4.10.1" }, "devDependencies": { "@alib/build-scripts": "^0.1.18", "@types/node": "^13.7.1", - "@types/react": "^16", - "@types/react-dom": "^16", - "build-plugin-component": "^0.2.11" + "build-plugin-component": "^0.2.16" }, "publishConfig": { "registry": "https://registry.npm.alibaba-inc.com" diff --git a/packages/runtime/src/core/container.ts b/packages/runtime/src/core/container.ts index e8deee816..809394bf6 100644 --- a/packages/runtime/src/core/container.ts +++ b/packages/runtime/src/core/container.ts @@ -1,4 +1,3 @@ -import { ReactType } from 'react'; import Provider from './provider'; export interface ILayoutOptions { @@ -7,16 +6,16 @@ export interface ILayoutOptions { } export default class Container { - private renderer: ReactType | null = null; - private layouts: { [key: string]: { content: ReactType; props: any } } = {}; - private loading: ReactType | null = null; + private renderer: any = null; + private layouts: { [key: string]: { content: any; props: any } } = {}; + private loading: any = null; private provider: any; - registerRenderer(renderer: ReactType): any { + registerRenderer(renderer: any): any { this.renderer = renderer; } - registerLayout(Layout: ReactType, options: ILayoutOptions): any { + registerLayout(Layout: any, options: ILayoutOptions): any { if (!options) { return; } @@ -27,7 +26,7 @@ export default class Container { this.layouts[componentName] = { content: Layout, props }; } - registerLoading(component: ReactType) { + registerLoading(component: any) { if (!component) { return; } @@ -50,11 +49,11 @@ export default class Container { return this.layouts[componentName]; } - getRenderer(): ReactType | null { + getRenderer(): any { return this.renderer; } - getLoading(): ReactType | null { + getLoading(): any { return this.loading; } diff --git a/packages/runtime/src/core/index.ts b/packages/runtime/src/core/index.ts index 6160a6a20..de70f9229 100644 --- a/packages/runtime/src/core/index.ts +++ b/packages/runtime/src/core/index.ts @@ -1,7 +1,6 @@ -import { ReactType } from 'react'; import Container, { ILayoutOptions } from './container'; import { IProvider } from './provider'; -import run from './run'; +import runApp from './runApp'; class App { private container: Container; @@ -11,18 +10,18 @@ class App { } run() { - run(); + runApp(); } - registerRenderer(renderer: ReactType): any { + registerRenderer(renderer: any): any { this.container.registerRenderer(renderer); } - registerLayout(Layout: ReactType, options: ILayoutOptions): any { + registerLayout(Layout: any, options: ILayoutOptions): any { this.container.registerLayout(Layout, options); } - registerLoading(component: ReactType) { + registerLoading(component: any) { this.container.registerLoading(component); } @@ -34,11 +33,11 @@ class App { return this.container.getLayout(componentName); } - getRenderer(): ReactType | null { + getRenderer(): any | null { return this.container.getRenderer(); } - getLoading(): ReactType | null { + getLoading(): any | null { return this.container.getLoading(); } diff --git a/packages/runtime/src/core/provider/index.ts b/packages/runtime/src/core/provider.ts similarity index 96% rename from packages/runtime/src/core/provider/index.ts rename to packages/runtime/src/core/provider.ts index 79016f2ab..907dd4903 100644 --- a/packages/runtime/src/core/provider/index.ts +++ b/packages/runtime/src/core/provider.ts @@ -1,4 +1,4 @@ -import { IAppConfig, IUtils, IComponents, HistoryMode } from '../run'; +import { IAppConfig, IUtils, IComponents, HistoryMode } from './runApp'; import EventEmitter from '@ali/offline-events'; interface IConstants { @@ -110,6 +110,7 @@ export interface IProvider { getPageData(pageId: string): Promise; getLazyComponent(pageId: string, props: any): any; createApp(): void; + runApp(App: any, config: IAppConfig): void; } export default class Provider implements IProvider { @@ -197,6 +198,10 @@ export default class Provider implements IProvider { throw new Error('Method called "createApp" not implemented.'); } + runApp(App: any, config: IAppConfig) { + throw new Error('Method called "runApp" not implemented.'); + } + registerComponents(components: IComponents | undefined) { if (!components) { return; diff --git a/packages/runtime/src/core/provider/react/index.ts b/packages/runtime/src/core/provider/react/index.ts deleted file mode 100644 index 1c0a3fb07..000000000 --- a/packages/runtime/src/core/provider/react/index.ts +++ /dev/null @@ -1,100 +0,0 @@ -import { createElement, ReactType, ReactElement } from 'react'; -import { Router } from '@ali/recore'; -import app from '../../index'; -import Provider from '..'; -import LazyComponent from './lazy-component'; - -export default class ReactProvider extends Provider { - // 定制构造根组件的逻辑,如切换路由机制 - createApp() { - const RouterView = this.getRouterView(); - let App; - const layoutConfig = this.getLayoutConfig(); - if (!layoutConfig || !layoutConfig.componentName) { - App = (props: any) => (RouterView ? createElement(RouterView, { ...props }) : null); - return App; - } - const { componentName: layoutName, props: layoutProps } = layoutConfig; - const { content: Layout, props: extraLayoutProps } = app.getLayout(layoutName) || {}; - const sectionalRender = this.isSectionalRender(); - if (!sectionalRender && Layout) { - App = (props: any) => - createElement( - Layout, - { ...layoutProps, ...extraLayoutProps }, - RouterView ? createElement(RouterView, props) : null, - ); - } else { - App = (props: any) => (RouterView ? createElement(RouterView, props) : null); - } - return App; - } - - // 内置实现 for 动态化渲染 - getRouterView(): ReactType | null { - const routerConfig = this.getRouterConfig(); - if (!routerConfig) { - return null; - } - const routes: Array<{ - path: string; - children: any; - exact: boolean; - defined: { keepAlive: boolean; [key: string]: any }; - }> = []; - let homePageId = this.getHomePage(); - Object.keys(routerConfig).forEach((pageId: string, idx: number) => { - if (!pageId) { - return; - } - const path = routerConfig[pageId]; - routes.push({ - path, - children: (props: any) => this.getLazyComponent(pageId, props), - exact: true, - defined: { keepAlive: true }, - }); - if (homePageId) { - return; - } - if (idx === 0 || path === '/') { - homePageId = pageId; - } - }); - if (homePageId) { - routes.push({ - path: '**', - children: (props: any) => this.getLazyComponent(homePageId, { ...props }), - exact: true, - defined: { keepAlive: true }, - }); - } - const RouterView = (props: any) => { - return createElement(Router as any, { - routes, - components: this.getComponents(), - utils: this.getUtils(), - componentsMap: this.getComponentsMapObj(), - ...props, - }); - }; - return RouterView; - } - - getLazyComponent(pageId: string, props: any): ReactElement | null { - if (!pageId) { - return null; - } - if (this.getlazyElement(pageId)) { - return this.getlazyElement(pageId); - } else { - const lazyElement = createElement(LazyComponent as any, { - getPageData: async () => await this.getPageData(pageId), - key: pageId, - ...props, - }); - this.setlazyElement(pageId, lazyElement); - return lazyElement; - } - } -} diff --git a/packages/runtime/src/core/provider/react/lazy-component.tsx b/packages/runtime/src/core/provider/react/lazy-component.tsx deleted file mode 100644 index 37c111097..000000000 --- a/packages/runtime/src/core/provider/react/lazy-component.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import { Component, createElement } from 'react'; -import app from '../../index'; - -interface IProps { - getPageData: () => any; - [key: string]: any; -} - -interface IState { - schema: object | null; -} - -export default class LazyComponent extends Component { - constructor(props: IProps) { - super(props); - this.state = { - schema: null, - }; - } - - async componentDidMount() { - const { getPageData } = this.props; - if (getPageData && !this.state.schema) { - const schema = await getPageData(); - this.setState({ schema }); - } - } - - render() { - const { getPageData, ...restProps } = this.props; - const { schema } = this.state; - const Renderer = app.getRenderer(); - const Loading = app.getLoading(); - if (!Renderer || !schema) { - if (!Loading) { - return null; - } - // loading扩展点 - return createElement(Loading); - } - return createElement(Renderer as any, { schema, loading: Loading ? createElement(Loading) : null, ...restProps }); - } -} diff --git a/packages/runtime/src/core/run.ts b/packages/runtime/src/core/runApp.ts similarity index 50% rename from packages/runtime/src/core/run.ts rename to packages/runtime/src/core/runApp.ts index 7cc7ab98f..6fb287f99 100644 --- a/packages/runtime/src/core/run.ts +++ b/packages/runtime/src/core/runApp.ts @@ -1,6 +1,4 @@ -import { ReactType } from 'react'; -import { runApp } from '@ali/recore'; -import { HashHistoryBuildOptions, BrowserHistoryBuildOptions, MemoryHistoryBuildOptions } from '@recore/history'; +import { HashHistoryBuildOptions, BrowserHistoryBuildOptions, MemoryHistoryBuildOptions } from 'history'; import app from './index'; export type HistoryOptions = { @@ -8,7 +6,7 @@ export type HistoryOptions = { } & (HashHistoryBuildOptions | BrowserHistoryBuildOptions | MemoryHistoryBuildOptions); export interface IComponents { - [key: string]: ReactType; + [key: string]: any; } export interface IUtils { @@ -22,34 +20,35 @@ export interface IAppConfig { components?: IComponents; utils?: IUtils; containerId?: string; + [key: string]: any; } -export interface IRecoreAppConfig { - history?: HistoryMode; - globalComponents?: IComponents; - globalUtils?: IUtils; - containerId?: string; -} +// export interface IRecoreAppConfig { +// history?: HistoryMode; +// globalComponents?: IComponents; +// globalUtils?: IUtils; +// containerId?: string; +// } -function transformConfig(config: IAppConfig | (() => IAppConfig)): IRecoreAppConfig { - if (!config) { - return {}; - } - if (typeof config === 'function') { - config = config(); - } - return { - history: config.history, - globalComponents: config.components, - globalUtils: config.utils, - containerId: config.containerId, - }; -} +// function transformConfig(config: IAppConfig | (() => IAppConfig)): IRecoreAppConfig { +// if (!config) { +// return {}; +// } +// if (typeof config === 'function') { +// config = config(); +// } +// return { +// history: config.history, +// globalComponents: config.components, +// globalUtils: config.utils, +// containerId: config.containerId, +// }; +// } -export default function run() { +export default function runApp() { const provider = app.getProvider(); if (!provider) { - throw new Error(''); + throw new Error('Please register class Provider'); } provider.onReady(() => { const promise = provider.async(); @@ -58,8 +57,7 @@ export default function run() { return; } const App = provider.createApp(); - config = transformConfig(config); - runApp(App, config); + provider.runApp(App, config); }); }); } diff --git a/packages/runtime/src/index.ts b/packages/runtime/src/index.ts index bb8e079a1..7a0999d95 100644 --- a/packages/runtime/src/index.ts +++ b/packages/runtime/src/index.ts @@ -1,7 +1,5 @@ -import { navigator, Router } from '@ali/recore'; import Provider from './core/provider'; -import ReactProvider from './core/provider/react'; import app from './core'; import * as Utils from './utils'; -export { app, Router, Provider, ReactProvider, navigator, Utils }; +export { app, Provider, Utils }; From 93cd908adb92946802607ac32d2f47acb5872df3 Mon Sep 17 00:00:00 2001 From: "wuyue.xht" Date: Tue, 19 May 2020 21:03:13 +0800 Subject: [PATCH 4/5] chore(runtime): index.d.ts --- packages/runtime/index.d.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/runtime/index.d.ts b/packages/runtime/index.d.ts index 6e3013290..76ca8e969 100644 --- a/packages/runtime/index.d.ts +++ b/packages/runtime/index.d.ts @@ -1,8 +1,7 @@ -import { ReactType } from 'react'; type HistoryMode = 'browser' | 'hash'; interface ComponentsMap { - [key: string]: ReactType; + [key: string]: any; } interface UtilsMap { From cb0f3827c775be8ef58fb64367c45fb5a707c6a1 Mon Sep 17 00:00:00 2001 From: "wuyue.xht" Date: Tue, 19 May 2020 21:03:34 +0800 Subject: [PATCH 5/5] feat(rax-provider): init --- packages/rax-provider/CHANGELOG.md | 4 + packages/rax-provider/README.md | 1 + packages/rax-provider/build.json | 11 +++ packages/rax-provider/package.json | 30 ++++++ packages/rax-provider/src/index.js | 5 + packages/rax-provider/src/lazy-component.js | 28 ++++++ packages/rax-provider/src/provider.js | 103 ++++++++++++++++++++ packages/rax-provider/src/router.js | 26 +++++ 8 files changed, 208 insertions(+) create mode 100644 packages/rax-provider/CHANGELOG.md create mode 100644 packages/rax-provider/README.md create mode 100644 packages/rax-provider/build.json create mode 100644 packages/rax-provider/package.json create mode 100644 packages/rax-provider/src/index.js create mode 100644 packages/rax-provider/src/lazy-component.js create mode 100644 packages/rax-provider/src/provider.js create mode 100644 packages/rax-provider/src/router.js diff --git a/packages/rax-provider/CHANGELOG.md b/packages/rax-provider/CHANGELOG.md new file mode 100644 index 000000000..e9fb6ecf5 --- /dev/null +++ b/packages/rax-provider/CHANGELOG.md @@ -0,0 +1,4 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. \ No newline at end of file diff --git a/packages/rax-provider/README.md b/packages/rax-provider/README.md new file mode 100644 index 000000000..f37b9b0f6 --- /dev/null +++ b/packages/rax-provider/README.md @@ -0,0 +1 @@ +# 低代码引擎运行时框架 diff --git a/packages/rax-provider/build.json b/packages/rax-provider/build.json new file mode 100644 index 000000000..3edf14380 --- /dev/null +++ b/packages/rax-provider/build.json @@ -0,0 +1,11 @@ +{ + "plugins": [ + [ + "build-plugin-rax-component", + { + "type": "rax", + "targets": ["web"] + } + ] + ] +} diff --git a/packages/rax-provider/package.json b/packages/rax-provider/package.json new file mode 100644 index 000000000..62712f312 --- /dev/null +++ b/packages/rax-provider/package.json @@ -0,0 +1,30 @@ +{ + "name": "@ali/lowcode-rax-provider", + "version": "0.8.14-beta.0", + "description": "Rax Provider for Runtime", + "files": [ + "es", + "lib" + ], + "main": "lib/index.js", + "module": "es/index.js", + "scripts": { + "start": "build-scripts start", + "build": "build-scripts build" + }, + "license": "MIT", + "dependencies": { + "@ali/lowcode-runtime": "^0.8.14-beta.0", + "rax": "1.1.2", + "driver-universal": "^3.1.3", + "rax-use-router": "^3.0.0", + "history": "^4.10.1" + }, + "devDependencies": { + "@alib/build-scripts": "^0.1.18", + "build-plugin-rax-component": "^0.2.0" + }, + "publishConfig": { + "registry": "https://registry.npm.alibaba-inc.com" + } +} diff --git a/packages/rax-provider/src/index.js b/packages/rax-provider/src/index.js new file mode 100644 index 000000000..e51321b80 --- /dev/null +++ b/packages/rax-provider/src/index.js @@ -0,0 +1,5 @@ +import RaxProvider from './provider'; +import getRouter from './router'; + +export { getRouter }; +export default RaxProvider; diff --git a/packages/rax-provider/src/lazy-component.js b/packages/rax-provider/src/lazy-component.js new file mode 100644 index 000000000..5430c75b9 --- /dev/null +++ b/packages/rax-provider/src/lazy-component.js @@ -0,0 +1,28 @@ +import { createElement, useState, useEffect } from 'rax'; +import { app } from '@ali/lowcode-runtime'; + +export default function LazyComponent(props) { + const [schema, setSchema] = useState(null); + + useEffect(() => { + (async () => { + const { getPageData } = props || {}; + if (getPageData && !schema) { + const data = await getPageData(); + setSchema(data); + } + })(); + }); + + const { getPageData, ...restProps } = props || {}; + const Renderer = app.getRenderer(); + const Loading = app.getLoading(); + if (!Renderer || !schema) { + if (!Loading) { + return null; + } + // loading扩展点 + return createElement(Loading); + } + return createElement(Renderer, { schema, loading: Loading ? createElement(Loading) : null, ...restProps }); +} diff --git a/packages/rax-provider/src/provider.js b/packages/rax-provider/src/provider.js new file mode 100644 index 000000000..b64b2316e --- /dev/null +++ b/packages/rax-provider/src/provider.js @@ -0,0 +1,103 @@ +import { createElement, render } from 'rax'; +import UniversalDriver from 'driver-universal'; +import { app, Provider } from '@ali/lowcode-runtime'; +import LazyComponent from './lazy-component'; +import getRouter from './router'; + +export default class RaxProvider extends Provider { + // 定制构造根组件的逻辑,如切换路由机制 + createApp() { + const RouterView = this.getRouterView(); + let App; + const layoutConfig = this.getLayoutConfig(); + if (!layoutConfig || !layoutConfig.componentName) { + App = (props) => (RouterView ? createElement(RouterView, { ...props }) : null); + return App; + } + const { componentName: layoutName, props: layoutProps } = layoutConfig; + const { content: Layout, props: extraLayoutProps } = app.getLayout(layoutName) || {}; + const sectionalRender = this.isSectionalRender(); + if (!sectionalRender && Layout) { + App = (props) => + createElement( + Layout, + { ...layoutProps, ...extraLayoutProps }, + RouterView ? createElement(RouterView, props) : null, + ); + } else { + App = (props) => (RouterView ? createElement(RouterView, props) : null); + } + return App; + } + + runApp(App, config) { + render(createElement(App), document.getElementById(config?.containerId || 'App'), { driver: UniversalDriver }); + } + + // 内置实现 for 动态化渲染 + getRouterView() { + const routerConfig = this.getRouterConfig(); + if (!routerConfig) { + return null; + } + const routes = []; + let homePageId = this.getHomePage(); + Object.keys(routerConfig).forEach((pageId, idx) => { + if (!pageId) { + return; + } + const path = routerConfig[pageId]; + routes.push({ + path, + component: (props: any) => + this.getLazyComponent(pageId, { + components: this.getComponents(), + utils: this.getUtils(), + componentsMap: this.getComponentsMapObj(), + ...props, + }), + }); + if (homePageId) { + return; + } + if (idx === 0 || path === '/') { + homePageId = pageId; + } + }); + if (homePageId) { + routes.push({ + path: '**', + component: (props) => + this.getLazyComponent(homePageId, { + components: this.getComponents(), + utils: this.getUtils(), + componentsMap: this.getComponentsMapObj(), + ...props, + }), + }); + } + const Router = getRouter({ + history: this.getHistory(), + routes, + }); + const RouterView = (props) => createElement(Router, props); + return RouterView; + } + + getLazyComponent(pageId, props) { + if (!pageId) { + return null; + } + if (this.getlazyElement(pageId)) { + return this.getlazyElement(pageId); + } + const lazyElement = createElement(LazyComponent, { + // eslint-disable-next-line no-return-await + getPageData: async () => await this.getPageData(pageId), + key: pageId, + ...props, + }); + this.setlazyElement(pageId, lazyElement); + return lazyElement; + } +} diff --git a/packages/rax-provider/src/router.js b/packages/rax-provider/src/router.js new file mode 100644 index 000000000..5f2ffaba4 --- /dev/null +++ b/packages/rax-provider/src/router.js @@ -0,0 +1,26 @@ +import { useRouter } from 'rax-use-router'; +import { createHashHistory, createBrowserHistory } from 'history'; + +const getConfig = (config) => { + let { history } = config; + const { routes } = config; + if (typeof history === 'string') { + if (history === 'hash') { + history = createHashHistory(); + } else if (history === 'browser') { + history = createBrowserHistory(); + } + } + return () => ({ + history, + routes, + }); +}; + +export default function getRouter(config) { + return function Router() { + const configWrapper = getConfig(config); + const { component } = useRouter(configWrapper); + return component; + }; +}